Merge mozilla-inbound to mozilla-central based on a green PGO changeset with some green mobile specific patches pushed on top of it; a=merge
Merge mozilla-inbound to mozilla-central based on a green PGO changeset with some green mobile specific patches pushed on top of it; a=merge
--- a/dom/base/StructuredCloneTags.h
+++ b/dom/base/StructuredCloneTags.h
@@ -39,18 +39,24 @@
#include "jsapi.h"
namespace mozilla {
namespace dom {
enum StructuredCloneTags {
SCTAG_BASE = JS_SCTAG_USER_MIN,
+
+ // These tags are used only for main thread structured clone.
SCTAG_DOM_BLOB,
SCTAG_DOM_FILE,
SCTAG_DOM_FILELIST,
+
+ // These tags are used for both main thread and workers.
+ SCTAG_DOM_IMAGEDATA,
+
SCTAG_DOM_MAX
};
} // namespace dom
} // namespace mozilla
#endif // StructuredCloneTags_h__
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -81,16 +81,18 @@
#include "nsIArray.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
#include "nsDOMScriptObjectHolder.h"
#include "prmem.h"
#include "WrapperFactory.h"
#include "nsGlobalWindow.h"
#include "nsScriptNameSpaceManager.h"
+#include "StructuredCloneTags.h"
+#include "mozilla/dom/ImageData.h"
#include "nsJSPrincipals.h"
#ifdef XP_MACOSX
// AssertMacros.h defines 'check' and conflicts with AccessCheck.h
#undef check
#endif
#include "AccessCheck.h"
@@ -108,16 +110,17 @@
#include "mozilla/FunctionTimer.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/bindings/Utils.h"
#include "sampler.h"
using namespace mozilla;
+using namespace mozilla::dom;
const size_t gStackSize = 8192;
#ifdef PR_LOGGING
static PRLogModuleInfo* gJSDiagnostics;
#endif
// Thank you Microsoft!
@@ -3605,28 +3608,80 @@ SetMemoryGCSliceTimePrefChangedCallback(
JSObject*
NS_DOMReadStructuredClone(JSContext* cx,
JSStructuredCloneReader* reader,
uint32_t tag,
uint32_t data,
void* closure)
{
- // We don't currently support any extensions to structured cloning.
+ if (tag == SCTAG_DOM_IMAGEDATA) {
+ // Read the information out of the stream.
+ uint32_t width, height;
+ JS::Value dataArray;
+ if (!JS_ReadUint32Pair(reader, &width, &height) ||
+ !JS_ReadTypedArray(reader, &dataArray)) {
+ return nsnull;
+ }
+ MOZ_ASSERT(dataArray.isObject());
+
+ // Construct the ImageData.
+ nsCOMPtr<nsIDOMImageData> imageData = new ImageData(width, height,
+ dataArray.toObject());
+ // Wrap it in a jsval.
+ JSObject* global = JS_GetGlobalForScopeChain(cx);
+ if (!global) {
+ return nsnull;
+ }
+ nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
+ JS::Value val;
+ nsresult rv =
+ nsContentUtils::WrapNative(cx, global, imageData, &val,
+ getter_AddRefs(wrapper));
+ if (NS_FAILED(rv)) {
+ return nsnull;
+ }
+ return val.toObjectOrNull();
+ }
+
+ // Don't know what this is. Bail.
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return nsnull;
}
JSBool
NS_DOMWriteStructuredClone(JSContext* cx,
JSStructuredCloneWriter* writer,
JSObject* obj,
void *closure)
{
- // We don't currently support any extensions to structured cloning.
+ nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
+ nsContentUtils::XPConnect()->
+ GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
+ nsISupports *native = wrappedNative ? wrappedNative->Native() : nsnull;
+
+ nsCOMPtr<nsIDOMImageData> imageData = do_QueryInterface(native);
+ if (imageData) {
+ // Prepare the ImageData internals.
+ PRUint32 width, height;
+ JS::Value dataArray;
+ if (NS_FAILED(imageData->GetWidth(&width)) ||
+ NS_FAILED(imageData->GetHeight(&height)) ||
+ NS_FAILED(imageData->GetData(cx, &dataArray)))
+ {
+ return false;
+ }
+
+ // Write the internals to the stream.
+ return JS_WriteUint32Pair(writer, SCTAG_DOM_IMAGEDATA, 0) &&
+ JS_WriteUint32Pair(writer, width, height) &&
+ JS_WriteTypedArray(writer, dataArray);
+ }
+
+ // Don't know what this is. Bail.
nsDOMClassInfo::ThrowJSException(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
return JS_FALSE;
}
void
NS_DOMStructuredCloneError(JSContext* cx,
uint32_t errorid)
{
--- a/dom/tests/mochitest/bugs/Makefile.in
+++ b/dom/tests/mochitest/bugs/Makefile.in
@@ -152,12 +152,15 @@ include $(topsrcdir)/config/rules.mk
iframe_bug304459-2.html \
test_bug38959.html \
iframe_bug38959-1.html \
iframe_bug38959-2.html \
test_onerror_message.html \
test_bug735237.html \
test_bug739038.html \
test_bug740811.html \
+ test_bug743615.html \
+ utils_bug743615.js \
+ worker_bug743615.js \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/test_bug743615.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=743615
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 743615</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="utils_bug743615.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=743615">Mozilla Bug 743615</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<canvas id="c" width="200" height="200"><canvas>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for structured cloning ImageData. **/
+
+SimpleTest.waitForExplicitFinish();
+window.addEventListener('message', windowMessage);
+startTest();
+
+function startTest() {
+ // Make an ImageData.
+ var ctx = document.getElementById('c').getContext('2d');
+ ctx.fillStyle = 'rgb(';
+ ctx.fillRect(30, 30, 50, 50);
+
+ // Make a blank ImageData.
+ var imageData = ctx.createImageData(200, 200);
+ is(imageData.data.length, imageData.width * imageData.height * 4,
+ 'right size for data');
+
+ // Write some things into it.
+ var pattern = makePattern(imageData.data.length, 42, 7);
+ setPattern(imageData, pattern);
+ ok(checkPattern(imageData, pattern), 'Can read it back before sending');
+
+ // PostMessage it to ourselves.
+ window.postMessage({ imageData: imageData,
+ pattern: pattern,
+ dataRef: imageData.data }, '*');
+}
+
+function windowMessage(evt) {
+ // Check the pattern we received.
+ var imageData = evt.data.imageData;
+ var pattern = evt.data.pattern;
+ ok(checkPattern(imageData, pattern),
+ 'postMessage from self worked correctly');
+
+ // We're not spec compliant on this yet.
+ todo_is(imageData.data, evt.data.dataRef,
+ 'Should have backrefs for imagedata buffer');
+
+ // Make a new pattern, and send it to a worker.
+ pattern = makePattern(imageData.data.length, 4, 3);
+ setPattern(imageData, pattern);
+ var worker = new Worker('worker_bug743615.js');
+ worker.onmessage = workerMessage;
+ worker.postMessage( {imageData: imageData, pattern: pattern });
+}
+
+function workerMessage(evt) {
+ // Relay the results of the worker-side tests.
+ is(evt.data.statusMessage, 'PASS', evt.data.statusMessage);
+
+ // Test what the worker sent us.
+ ok(checkPattern(evt.data.imageData, evt.data.pattern),
+ 'postMessage from worker worked correctly');
+
+ // All done.
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/utils_bug743615.js
@@ -0,0 +1,25 @@
+function makePattern(len, start, inc) {
+ var pattern = [];
+ while(len) {
+ pattern.push(start);
+ start = (start + inc) % 256;
+ --len;
+ }
+ return pattern;
+}
+
+function setPattern(imageData, pattern) {
+ if (pattern.length != imageData.data.length)
+ throw Error('Invalid pattern');
+ for (var i = 0; i < pattern.length; ++i)
+ imageData.data[i] = pattern[i];
+}
+
+function checkPattern(imageData, pattern) {
+ if (pattern.length != imageData.data.length)
+ throw Error('Invalid pattern');
+ for (var i = 0; i < pattern.length; ++i)
+ if (imageData.data[i] != pattern[i])
+ return false;
+ return true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/bugs/worker_bug743615.js
@@ -0,0 +1,38 @@
+importScripts('utils_bug743615.js');
+
+self.onmessage = function onMessage(evt) {
+ // Check the pattern that was sent.
+ var imageData = evt.data.imageData;
+ var pattern = evt.data.pattern;
+ var statusMessage = checkPattern(imageData, pattern)
+ ? 'PASS' : 'Got corrupt typed array in worker';
+
+ // Check against the interface object.
+ if (!(imageData instanceof ImageData))
+ statusMessage += ", Bad interface object in worker";
+
+ // Check the getters.
+ if (imageData.width * imageData.height != imageData.data.length / 4) {
+ statusMessage += ", Bad ImageData getters in worker: "
+ statusMessage += [imageData.width, imageData.height].join(', ');
+ }
+
+ // Make sure that writing to .data is a no-op when not in strict mode.
+ var origData = imageData.data;
+ var threw = false;
+ try {
+ imageData.data = [];
+ imageData.width = 2;
+ imageData.height = 2;
+ } catch(e) { threw = true; }
+ if (threw || imageData.data !== origData)
+ statusMessage = statusMessage + ", Should silently ignore sets";
+
+
+
+ // Send back a new pattern.
+ pattern = makePattern(imageData.data.length, 99, 2);
+ setPattern(imageData, pattern);
+ self.postMessage({ statusMessage: statusMessage, imageData: imageData,
+ pattern: pattern });
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/ImageData.cpp
@@ -0,0 +1,202 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ImageData.h"
+
+#include "jsfriendapi.h"
+
+#include "nsTraceRefcnt.h"
+
+#define PROPERTY_FLAGS \
+ (JSPROP_ENUMERATE | JSPROP_SHARED)
+
+USING_WORKERS_NAMESPACE
+
+namespace {
+
+class ImageData
+{
+ static JSClass sClass;
+ static JSPropertySpec sProperties[];
+
+ enum SLOT {
+ SLOT_width = 0,
+ SLOT_height,
+ SLOT_data,
+
+ SLOT_COUNT
+ };
+
+public:
+ static JSObject*
+ InitClass(JSContext* aCx, JSObject* aObj)
+ {
+ return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
+ NULL, NULL, NULL);
+ }
+
+ static JSObject*
+ Create(JSContext* aCx, uint32_t aWidth, uint32_t aHeight, JSObject *aData)
+ {
+ MOZ_ASSERT(aData);
+ MOZ_ASSERT(JS_IsTypedArrayObject(aData, aCx));
+ MOZ_ASSERT(JS_IsUint8ClampedArray(aData, aCx));
+
+ JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
+ if (!obj) {
+ return NULL;
+ }
+
+ JS_SetReservedSlot(obj, SLOT_width, UINT_TO_JSVAL(aWidth));
+ JS_SetReservedSlot(obj, SLOT_height, UINT_TO_JSVAL(aHeight));
+ JS_SetReservedSlot(obj, SLOT_data, OBJECT_TO_JSVAL(aData));
+
+ // This is an empty object. The point is just to differentiate instances
+ // from the interface object.
+ ImageData* priv = new ImageData();
+ JS_SetPrivate(obj, priv);
+
+ return obj;
+ }
+
+ static bool
+ IsInstance(JSObject* aObj)
+ {
+ return JS_GetClass(aObj) == &sClass;
+ }
+
+ static uint32_t
+ GetWidth(JSObject* aObj)
+ {
+ MOZ_ASSERT(IsInstance(aObj));
+ return JS_DoubleToUint32(JS_GetReservedSlot(aObj, SLOT_width).toNumber());
+ }
+
+ static uint32_t
+ GetHeight(JSObject* aObj)
+ {
+ MOZ_ASSERT(IsInstance(aObj));
+ return JS_DoubleToUint32(JS_GetReservedSlot(aObj, SLOT_height).toNumber());
+ }
+
+ static
+ JSObject* GetData(JSObject* aObj)
+ {
+ MOZ_ASSERT(IsInstance(aObj));
+ return &JS_GetReservedSlot(aObj, SLOT_data).toObject();
+ }
+
+private:
+ ImageData()
+ {
+ MOZ_COUNT_CTOR(mozilla::dom::workers::ImageData);
+ }
+
+ ~ImageData()
+ {
+ MOZ_COUNT_DTOR(mozilla::dom::workers::ImageData);
+ }
+
+ static JSBool
+ Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
+ {
+ JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
+ sClass.name);
+ return false;
+ }
+
+ static void
+ Finalize(JSFreeOp* aFop, JSObject* aObj)
+ {
+ MOZ_ASSERT(JS_GetClass(aObj) == &sClass);
+ delete static_cast<ImageData*>(JS_GetPrivate(aObj));
+ }
+
+ static JSBool
+ GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
+ {
+ JSClass* classPtr = JS_GetClass(aObj);
+ if (classPtr != &sClass) {
+ JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
+ JSMSG_INCOMPATIBLE_PROTO, sClass.name, "GetProperty",
+ classPtr->name);
+ return false;
+ }
+
+ MOZ_ASSERT(JSID_IS_INT(aIdval));
+ MOZ_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < SLOT_COUNT);
+
+ *aVp = JS_GetReservedSlot(aObj, JSID_TO_INT(aIdval));
+ return true;
+ }
+};
+
+JSClass ImageData::sClass = {
+ "ImageData",
+ JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
+ JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
+ JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
+};
+
+JSPropertySpec ImageData::sProperties[] = {
+ // These properties are read-only per spec, which means that sets must throw
+ // in strict mode and silently fail otherwise. This is a problem for workers
+ // in general (because js_GetterOnlyPropertyStub throws unconditionally). The
+ // general plan for fixing this involves the new DOM bindings. But Peace
+ // Keeper breaks if we throw when setting these properties, so we need to do
+ // something about it in the mean time. So we use NULL, which defaults to the
+ // class setter (JS_StrictPropertyStub), which is always a silent no-op,
+ // regardless of strict mode. Not ideal, but good enough for now.
+ { "width", SLOT_width, PROPERTY_FLAGS, GetProperty, NULL },
+ { "height", SLOT_height, PROPERTY_FLAGS, GetProperty, NULL },
+ { "data", SLOT_data, PROPERTY_FLAGS, GetProperty, NULL },
+ { 0, 0, 0, NULL, NULL }
+};
+
+} // anonymous namespace
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace imagedata {
+
+bool
+InitClass(JSContext* aCx, JSObject* aGlobal)
+{
+ return !!ImageData::InitClass(aCx, aGlobal);
+}
+
+JSObject*
+Create(JSContext* aCx, uint32_t aWidth, uint32_t aHeight, JSObject* aData)
+{
+ return ImageData::Create(aCx, aWidth, aHeight, aData);
+}
+
+bool
+IsImageData(JSObject* aObj)
+{
+ return ImageData::IsInstance(aObj);
+}
+
+uint32_t
+GetWidth(JSObject* aObj)
+{
+ return ImageData::GetWidth(aObj);
+}
+
+uint32_t
+GetHeight(JSObject* aObj)
+{
+ return ImageData::GetHeight(aObj);
+}
+
+JSObject*
+GetData(JSObject* aObj)
+{
+ return ImageData::GetData(aObj);
+}
+
+
+} // namespace imagedata
+
+END_WORKERS_NAMESPACE
new file mode 100644
--- /dev/null
+++ b/dom/workers/ImageData.h
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_workers_imagedata_h__
+#define mozilla_dom_workers_imagedata_h__
+
+#include "Workers.h"
+
+BEGIN_WORKERS_NAMESPACE
+
+namespace imagedata {
+
+bool
+InitClass(JSContext* aCx, JSObject* aGlobal);
+
+JSObject*
+Create(JSContext* aCx, uint32_t aWidth, uint32_t aHeight, JSObject* aData);
+
+/*
+ * All data members live in private slots on the JS Object. Callers must
+ * first check IsImageData, after which they may call the data accessors.
+ */
+
+bool
+IsImageData(JSObject* aObj);
+
+uint32_t
+GetWidth(JSObject* aObj);
+
+uint32_t
+GetHeight(JSObject* aObj);
+
+JSObject*
+GetData(JSObject* aObj);
+
+} // namespace imagedata
+
+END_WORKERS_NAMESPACE
+
+#endif // mozilla_dom_workers_imagedata_h__
--- a/dom/workers/Makefile.in
+++ b/dom/workers/Makefile.in
@@ -19,16 +19,17 @@ CPPSRCS = \
DOMBindingBase.cpp \
Events.cpp \
EventListenerManager.cpp \
EventTarget.cpp \
Exceptions.cpp \
File.cpp \
FileReaderSync.cpp \
FileReaderSyncPrivate.cpp \
+ ImageData.cpp \
Location.cpp \
Navigator.cpp \
Principal.cpp \
RuntimeService.cpp \
ScriptLoader.cpp \
Worker.cpp \
WorkerPrivate.cpp \
WorkerScope.cpp \
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -74,16 +74,17 @@
#ifdef ANDROID
#include <android/log.h>
#endif
#include "Events.h"
#include "Exceptions.h"
#include "File.h"
+#include "ImageData.h"
#include "Principal.h"
#include "RuntimeService.h"
#include "ScriptLoader.h"
#include "Worker.h"
#include "WorkerFeature.h"
#include "WorkerScope.h"
#if 0 // Define to run GC more often.
@@ -98,16 +99,17 @@
using mozilla::MutexAutoLock;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
using mozilla::dom::workers::exceptions::ThrowDOMExceptionForCode;
USING_WORKERS_NAMESPACE
using namespace mozilla::dom::workers::events;
+using namespace mozilla::dom;
namespace {
const char gErrorChars[] = "error";
const char gMessageChars[] = "message";
template <class T>
class AutoPtrComparator
@@ -328,16 +330,35 @@ struct WorkerStructuredCloneCallbacks
#endif
// nsIDOMBlob should be threadsafe, thus we will use the same instance
// in the worker.
JSObject* jsBlob = file::CreateBlob(aCx, blob);
return jsBlob;
}
}
+ // See if the object is an ImageData.
+ else if (aTag == SCTAG_DOM_IMAGEDATA) {
+ JS_ASSERT(!aData);
+
+ // Read the information out of the stream.
+ uint32_t width, height;
+ jsval dataArray;
+ if (!JS_ReadUint32Pair(aReader, &width, &height) ||
+ !JS_ReadTypedArray(aReader, &dataArray))
+ {
+ return nsnull;
+ }
+ MOZ_ASSERT(dataArray.isObject());
+
+ // Construct the ImageData.
+ JSObject* obj = imagedata::Create(aCx, width, height,
+ JSVAL_TO_OBJECT(dataArray));
+ return obj;
+ }
Error(aCx, 0);
return nsnull;
}
static JSBool
Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
void* aClosure)
@@ -369,16 +390,29 @@ struct WorkerStructuredCloneCallbacks
JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) &&
JS_WriteBytes(aWriter, &blob, sizeof(blob))) {
clonedObjects->AppendElement(blob);
return true;
}
}
}
+ // See if this is an ImageData object.
+ if (imagedata::IsImageData(aObj)) {
+ // Pull the properties off the object.
+ uint32_t width = imagedata::GetWidth(aObj);
+ uint32_t height = imagedata::GetHeight(aObj);
+ JSObject* data = imagedata::GetData(aObj);
+
+ // Write the structured clone.
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_IMAGEDATA, 0) &&
+ JS_WriteUint32Pair(aWriter, width, height) &&
+ JS_WriteTypedArray(aWriter, OBJECT_TO_JSVAL(data));
+ }
+
Error(aCx, 0);
return false;
}
static void
Error(JSContext* aCx, uint32_t /* aErrorId */)
{
ThrowDOMExceptionForCode(aCx, DATA_CLONE_ERR);
@@ -461,22 +495,16 @@ struct MainThreadWorkerStructuredCloneCa
Error(aCx, DATA_CLONE_ERR);
return nsnull;
}
return JSVAL_TO_OBJECT(wrappedBlob);
}
}
- JSObject* clone =
- WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, aClosure);
- if (clone) {
- return clone;
- }
-
JS_ClearPendingException(aCx);
return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull);
}
static JSBool
Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
void* aClosure)
{
@@ -522,22 +550,16 @@ struct MainThreadWorkerStructuredCloneCa
JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) {
clonedObjects->AppendElement(blob);
return true;
}
}
}
}
- JSBool ok =
- WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
- if (ok) {
- return ok;
- }
-
JS_ClearPendingException(aCx);
return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull);
}
static void
Error(JSContext* aCx, uint32_t aErrorId)
{
AssertIsOnMainThread();
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -50,16 +50,17 @@
#include "mozilla/Mutex.h"
#include "mozilla/TimeStamp.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsEventQueue.h"
#include "nsStringGlue.h"
#include "nsTArray.h"
#include "nsTPriorityQueue.h"
+#include "StructuredCloneTags.h"
#include "EventTarget.h"
#include "Queue.h"
#include "WorkerFeature.h"
class JSAutoStructuredCloneBuffer;
class nsIDocument;
class nsIPrincipal;
@@ -807,17 +808,17 @@ private:
ProcessAllControlRunnables();
};
WorkerPrivate*
GetWorkerPrivateFromContext(JSContext* aCx);
enum WorkerStructuredDataType
{
- DOMWORKER_SCTAG_FILE = JS_SCTAG_USER_MIN + 0x1000,
+ DOMWORKER_SCTAG_FILE = SCTAG_DOM_MAX,
DOMWORKER_SCTAG_BLOB,
DOMWORKER_SCTAG_END
};
JSStructuredCloneCallbacks*
WorkerStructuredCloneCallbacks(bool aMainRuntime);
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -57,16 +57,17 @@
#include "ChromeWorkerScope.h"
#include "Events.h"
#include "EventListenerManager.h"
#include "EventTarget.h"
#include "Exceptions.h"
#include "File.h"
#include "FileReaderSync.h"
#include "Location.h"
+#include "ImageData.h"
#include "Navigator.h"
#include "Principal.h"
#include "ScriptLoader.h"
#include "Worker.h"
#include "WorkerPrivate.h"
#include "XMLHttpRequest.h"
#include "WorkerInlines.h"
@@ -991,16 +992,17 @@ CreateDedicatedWorkerGlobalScope(JSConte
}
// Init other classes we care about.
if (!events::InitClasses(aCx, global, false) ||
!file::InitClasses(aCx, global) ||
!filereadersync::InitClass(aCx, global) ||
!exceptions::InitClasses(aCx, global) ||
!location::InitClass(aCx, global) ||
+ !imagedata::InitClass(aCx, global) ||
!navigator::InitClass(aCx, global)) {
return NULL;
}
// Init other paris-bindings.
if (!XMLHttpRequest_workers::CreateInterfaceObjects(aCx, global) ||
!XMLHttpRequestUpload_workers::CreateInterfaceObjects(aCx, global)) {
return NULL;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5179,21 +5179,27 @@ JS_SetStructuredCloneCallbacks(JSRuntime
JS_PUBLIC_API(JSBool)
JS_ReadUint32Pair(JSStructuredCloneReader *r, uint32_t *p1, uint32_t *p2);
JS_PUBLIC_API(JSBool)
JS_ReadBytes(JSStructuredCloneReader *r, void *p, size_t len);
JS_PUBLIC_API(JSBool)
+JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp);
+
+JS_PUBLIC_API(JSBool)
JS_WriteUint32Pair(JSStructuredCloneWriter *w, uint32_t tag, uint32_t data);
JS_PUBLIC_API(JSBool)
JS_WriteBytes(JSStructuredCloneWriter *w, const void *p, size_t len);
+JS_PUBLIC_API(JSBool)
+JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v);
+
/************************************************************************/
/*
* Locale specific string conversion and error message callbacks.
*/
struct JSLocaleCallbacks {
JSLocaleToUpperCase localeToUpperCase;
JSLocaleToLowerCase localeToLowerCase;
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -419,16 +419,23 @@ JSStructuredCloneWriter::checkStack()
JS_ASSERT(total <= ids.length());
size_t j = objs.length();
for (size_t i = 0; i < limit; i++)
JS_ASSERT(memory.has(&objs[--j].toObject()));
#endif
}
+JS_PUBLIC_API(JSBool)
+JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v)
+{
+ JS_ASSERT(v.isObject());
+ return w->writeTypedArray(&v.toObject());
+}
+
bool
JSStructuredCloneWriter::writeTypedArray(JSObject *obj)
{
JSObject *arr = TypedArray::getTypedArray(obj);
if (!out.writePair(ArrayTypeToTag(TypedArray::getType(arr)), TypedArray::getLength(arr)))
return false;
switch (TypedArray::getType(arr)) {
@@ -684,16 +691,26 @@ JSStructuredCloneReader::readString(uint
if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars))
return NULL;
JSString *str = js_NewString(context(), chars.get(), nchars);
if (str)
chars.forget();
return str;
}
+JS_PUBLIC_API(JSBool)
+JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp)
+{
+ uint32_t tag, nelems;
+ if (!r->input().readPair(&tag, &nelems))
+ return false;
+ JS_ASSERT(tag >= SCTAG_TYPED_ARRAY_MIN && tag <= SCTAG_TYPED_ARRAY_MAX);
+ return r->readTypedArray(tag, nelems, vp);
+}
+
bool
JSStructuredCloneReader::readTypedArray(uint32_t tag, uint32_t nelems, Value *vp)
{
JSObject *obj = NULL;
switch (tag) {
case SCTAG_TYPED_ARRAY_INT8:
obj = JS_NewInt8Array(context(), nelems);
--- a/js/src/jsclone.h
+++ b/js/src/jsclone.h
@@ -138,16 +138,18 @@ struct JSStructuredCloneReader {
// Stack of all objects read during this deserialization
js::AutoValueVector allObjs;
// The user defined callbacks that will be used for cloning.
const JSStructuredCloneCallbacks *callbacks;
// Any value passed to JS_ReadStructuredClone.
void *closure;
+
+ friend JSBool JS_ReadTypedArray(JSStructuredCloneReader *r, jsval *vp);
};
struct JSStructuredCloneWriter {
public:
explicit JSStructuredCloneWriter(js::SCOutput &out, const JSStructuredCloneCallbacks *cb,
void *cbClosure)
: out(out), objs(out.context()), counts(out.context()), ids(out.context()),
memory(out.context()), callbacks(cb), closure(cbClosure) { }
@@ -191,11 +193,13 @@ struct JSStructuredCloneWriter {
typedef js::HashMap<JSObject *, uint32_t> CloneMemory;
CloneMemory memory;
// The user defined callbacks that will be used for cloning.
const JSStructuredCloneCallbacks *callbacks;
// Any value passed to JS_WriteStructuredClone.
void *closure;
+
+ friend JSBool JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v);
};
#endif /* jsclone_h___ */
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -163,16 +163,30 @@ nsLayoutUtils::Are3DTransformsEnabled()
s3DTransformPrefCached = true;
mozilla::Preferences::AddBoolVarCache(&s3DTransformsEnabled,
"layout.3d-transforms.enabled");
}
return s3DTransformsEnabled;
}
+bool
+nsLayoutUtils::UseBackgroundNearestFiltering()
+{
+ static bool sUseBackgroundNearestFilteringEnabled;
+ static bool sUseBackgroundNearestFilteringPrefInitialised = false;
+
+ if (!sUseBackgroundNearestFilteringPrefInitialised) {
+ sUseBackgroundNearestFilteringPrefInitialised = true;
+ sUseBackgroundNearestFilteringEnabled = mozilla::Preferences::GetBool("gfx.filter.nearest.force-enabled", false);
+ }
+
+ return sUseBackgroundNearestFilteringEnabled;
+}
+
void
nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
nsOverflowAreas& aOverflowAreas)
{
// Iterate over all children except pop-ups.
const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
nsIFrame::kSelectPopupList);
for (nsIFrame::ChildListIterator childLists(aFrame);
@@ -3729,16 +3743,21 @@ nsLayoutUtils::DrawBackgroundImage(nsRen
GraphicsFilter aGraphicsFilter,
const nsRect& aDest,
const nsRect& aFill,
const nsPoint& aAnchor,
const nsRect& aDirty,
PRUint32 aImageFlags)
{
SAMPLE_LABEL("layout", "nsLayoutUtils::DrawBackgroundImage");
+
+ if (UseBackgroundNearestFiltering()) {
+ aGraphicsFilter = gfxPattern::FILTER_NEAREST;
+ }
+
return DrawImageInternal(aRenderingContext, aImage, aGraphicsFilter,
aDest, aFill, aAnchor, aDirty,
aImageSize, aImageFlags);
}
/* static */ nsresult
nsLayoutUtils::DrawImage(nsRenderingContext* aRenderingContext,
imgIContainer* aImage,
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1492,16 +1492,22 @@ public:
bool clear);
/**
* Checks if CSS 3D transforms are currently enabled.
*/
static bool Are3DTransformsEnabled();
/**
+ * Checks if we should forcibly use nearest pixel filtering for the
+ * background.
+ */
+ static bool UseBackgroundNearestFiltering();
+
+ /**
* Unions the overflow areas of all non-popup children of aFrame with
* aOverflowAreas.
*/
static void UnionChildOverflow(nsIFrame* aFrame,
nsOverflowAreas& aOverflowAreas);
/**
* Return whether this is a frame whose width is used when computing
--- a/layout/reftests/backgrounds/reftest.list
+++ b/layout/reftests/backgrounds/reftest.list
@@ -112,17 +112,18 @@ fails-if(Android) == viewport-translucen
== background-size-monster-px.html background-size-monster-ref.html
== background-size-monster-rem.html background-size-monster-ref.html
# There seems to be a pixel-snapping problem here, where the repeated background
# image isn't being snapped at its boundaries. Note that the boundaries within
# the image aren't the issue, because they're being obscured to avoid sampling
# algorithm dependencies (at least assuming the sampling algorithm in use
# doesn't sample too far astray from the boundaries).
-fails == background-size-zoom-repeat.html background-size-zoom-repeat-ref.html
+# Android uses FILTER_NEAREST which doesn't suffer from this issue.
+fails-if(!Android) == background-size-zoom-repeat.html background-size-zoom-repeat-ref.html
# -moz-default-background-color and -moz-default-color (bug 591341)
== background-moz-default-background-color.html background-moz-default-background-color-ref.html
== fixed-bg-with-transform-outside-viewport-1.html fixed-bg-with-transform-outside-viewport-ref.html
HTTP == root-background-1.html root-background-ref.html
HTTP != root-background-1.html about:blank
--- a/layout/reftests/backgrounds/vector/reftest.list
+++ b/layout/reftests/backgrounds/vector/reftest.list
@@ -81,18 +81,19 @@ include empty/reftest.list
== tall--contain--percent-width-nonpercent-height.html ref-tall-lime256x384-aqua256x384.html
== tall--contain--percent-width-nonpercent-height-viewbox.html ref-tall-lime48x384-aqua48x384.html
== tall--contain--percent-width-omitted-height.html ref-tall-lime256x384-aqua256x384.html
== tall--contain--percent-width-omitted-height-viewbox.html ref-tall-lime48x384-aqua48x384.html
== tall--contain--percent-width-percent-height.html ref-tall-lime256x384-aqua256x384.html
== tall--contain--percent-width-percent-height-viewbox.html ref-tall-lime48x384-aqua48x384.html
# We smear the background image when scaling it in these two tests...
-fails == tall--cover--nonpercent-width-nonpercent-height.html ref-tall-lime256x512-aqua256x256.html
-fails == tall--cover--nonpercent-width-nonpercent-height-viewbox.html ref-tall-lime256x512-aqua256x256.html
+# Android uses FILTER_NEAREST for background images so the smear doesn't occur
+fails-if(!Android) == tall--cover--nonpercent-width-nonpercent-height.html ref-tall-lime256x512-aqua256x256.html
+fails-if(!Android) == tall--cover--nonpercent-width-nonpercent-height-viewbox.html ref-tall-lime256x512-aqua256x256.html
# ...but we don't in identical tests with image-rendering: -moz-crisp-edges.
== tall--cover--nonpercent-width-nonpercent-height--crisp.html ref-tall-lime256x512-aqua256x256.html
== tall--cover--nonpercent-width-nonpercent-height-viewbox--crisp.html ref-tall-lime256x512-aqua256x256.html
== tall--cover--nonpercent-width-omitted-height.html ref-tall-lime256x384-aqua256x384.html
== tall--cover--nonpercent-width-omitted-height-viewbox.html ref-tall-lime256x768.html
== tall--cover--nonpercent-width-percent-height.html ref-tall-lime256x384-aqua256x384.html
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -207,16 +207,22 @@ pref("gfx.color_management.mode", 2);
pref("gfx.color_management.display_profile", "");
pref("gfx.color_management.rendering_intent", 0);
pref("gfx.color_management.enablev4", false);
pref("gfx.downloadable_fonts.enabled", true);
pref("gfx.downloadable_fonts.fallback_delay", 3000);
pref("gfx.downloadable_fonts.sanitize", true);
+#ifdef ANDROID
+pref("gfx.filter.nearest.force-enabled", true);
+#else
+pref("gfx.filter.nearest.force-enabled", false);
+#endif
+
// whether to always search all font cmaps during system font fallback
pref("gfx.font_rendering.fallback.always_use_cmaps", false);
#ifdef MOZ_GRAPHITE
pref("gfx.font_rendering.graphite.enabled", false);
#endif
// see gfx/thebes/gfxUnicodeProperties.h for definitions of script bits