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
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 24 Apr 2012 10:39:25 -0400
changeset 92207 f410f3436cdbf5e7c6f62209d41beb61bb319232
parent 92206 82cb595c2a03e80fe17aa0f65905c25c4086a253 (current diff)
parent 92200 b88fba85586ba81d8709fbcdcc15a0b7e36a025a (diff)
child 92208 d7f017e8e572d8ecff1cfcfee37bd621293654ba
child 92282 5c68b6be6b8ae6d9f2645d22e4091e5f39fc5094
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersmerge
milestone14.0a1
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