Merge mozilla-central to mozilla-inbound
authorEd Morley <emorley@mozilla.com>
Fri, 13 Jul 2012 15:30:35 +0100
changeset 101911 7a5b34645a73fc70b0f5feea88cb67320766bfb9
parent 101878 a2d40b91eea9262dd138daace1569c28fdd6a846 (current diff)
parent 101910 e7033562615233d5466b88aad91cdec11873b017 (diff)
child 101912 2a31f6253167ce50001530b0241c87bdfcf8223c
push idunknown
push userunknown
push dateunknown
milestone16.0a1
Merge mozilla-central to mozilla-inbound
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -404,16 +404,26 @@ pref("b2g.remote-js.port", 9999);
 // Handle hardware buttons in the b2g chrome package
 pref("b2g.keys.menu.enabled", true);
 
 // Screen timeout in seconds
 pref("power.screen.timeout", 60);
 
 pref("full-screen-api.enabled", true);
 
+#ifndef MOZ_WIDGET_GONK
+// If we're not actually on physical hardware, don't make the top level widget
+// fullscreen when transitioning to fullscreen. This means in emulated
+// environments (like the b2g desktop client) we won't make the client window
+// fill the whole screen, we'll just make the content fill the client window,
+// i.e. it won't give the impression to content that the number of device
+// screen pixels changes!
+pref("full-screen-api.ignore-widgets", true);
+#endif
+
 pref("media.volume.steps", 10);
 
 //Enable/disable marionette server, set listening port
 pref("marionette.defaultPrefs.enabled", true);
 pref("marionette.defaultPrefs.port", 2828);
 
 #ifdef MOZ_UPDATER
 pref("app.update.enabled", true);
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -42,16 +42,25 @@ var SettingsListener = {
     }));
 
     this._callbacks[name] = callback;
   }
 };
 
 SettingsListener.init();
 
+// =================== Audio ====================
+SettingsListener.observe('audio.volume.master', 0.5, function(value) {
+  let audioManager = Services.audioManager;
+  if (!audioManager)
+    return;
+
+  audioManager.masterVolume = Math.min(0, Math.max(value, 1));
+});
+
 
 // =================== Languages ====================
 SettingsListener.observe('language.current', 'en-US', function(value) {
   Services.prefs.setCharPref('intl.accept_languages', value);
 });
 
 
 // =================== RIL ====================
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -171,69 +171,32 @@ var shell = {
     window.removeEventListener('sizemodechange', this);
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
 
 #ifndef MOZ_WIDGET_GONK
     delete Services.audioManager;
 #endif
   },
 
-  changeVolume: function shell_changeVolume(delta) {
-    let steps = 10;
-    try {
-      steps = Services.prefs.getIntPref("media.volume.steps");
-      if (steps <= 0)
-        steps = 1;
-    } catch(e) {}
-
-    let audioManager = Services.audioManager;
-    if (!audioManager)
-      return;
-
-    let currentVolume = audioManager.masterVolume;
-    let newStep = Math.round(steps * Math.sqrt(currentVolume)) + delta;
-    let volume = (newStep / steps) * (newStep / steps);
-
-    if (volume > 1)
-      volume = 1;
-    if (volume < 0)
-      volume = 0;
-    audioManager.masterVolume = volume;
-  },
-
   forwardKeyToContent: function shell_forwardKeyToContent(evt) {
     let content = shell.contentBrowser.contentWindow;
     let generatedEvent = content.document.createEvent('KeyboardEvent');
     generatedEvent.initKeyEvent(evt.type, true, true, evt.view, evt.ctrlKey,
                                 evt.altKey, evt.shiftKey, evt.metaKey,
                                 evt.keyCode, evt.charCode);
 
     content.document.documentElement.dispatchEvent(generatedEvent);
   },
 
   handleEvent: function shell_handleEvent(evt) {
     let content = this.contentBrowser.contentWindow;
     switch (evt.type) {
       case 'keydown':
       case 'keyup':
       case 'keypress':
-        // For debug purposes and because some of the APIs are not yet exposed
-        // to the content, let's react on some of the keyup events.
-        if (evt.type == 'keyup' && evt.eventPhase == evt.BUBBLING_PHASE) {
-          switch (evt.keyCode) {
-            case evt.DOM_VK_PAGE_DOWN:
-              this.changeVolume(-1);
-              break;
-
-            case evt.DOM_VK_PAGE_UP:
-              this.changeVolume(1);
-              break;
-          }
-        }
-
         // Redirect the HOME key to System app and stop the applications from
         // handling it.
         let rootContentEvt = (evt.target.ownerDocument.defaultView == content);
         if (!rootContentEvt && evt.eventPhase == evt.CAPTURING_PHASE &&
             evt.keyCode == evt.DOM_VK_HOME) {
           this.forwardKeyToContent(evt);
           evt.preventDefault();
           evt.stopImmediatePropagation();
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -92,17 +92,16 @@ var tabPreviews = {
 
 var tabPreviewPanelHelper = {
   opening: function (host) {
     host.panel.hidden = false;
 
     var handler = this._generateHandler(host);
     host.panel.addEventListener("popupshown", handler, false);
     host.panel.addEventListener("popuphiding", handler, false);
-    host.panel.addEventListener("popuphidden", handler, false);
 
     host._prevFocus = document.commandDispatcher.focusedElement;
   },
   _generateHandler: function (host) {
     var self = this;
     return function (event) {
       if (event.target == host.panel) {
         host.panel.removeEventListener(event.type, arguments.callee, false);
@@ -125,21 +124,16 @@ var tabPreviewPanelHelper = {
       host._prevFocus = null;
     } else
       gBrowser.selectedBrowser.focus();
 
     if (host.tabToSelect) {
       gBrowser.selectedTab = host.tabToSelect;
       host.tabToSelect = null;
     }
-  },
-  _popuphidden: function (host) {
-    // Destroy the widget in order to prevent outdated content
-    // when re-opening the panel.
-    host.panel.hidden = true;
   }
 };
 
 /**
  * Ctrl-Tab panel
  */
 var ctrlTab = {
   get panel () {
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -604,17 +604,19 @@ static const char kDOMStringBundleURL[] 
 #define EVENTTARGET_SCRIPTABLE_FLAGS                                          \
   (DOM_DEFAULT_SCRIPTABLE_FLAGS       |                                       \
    nsIXPCScriptable::WANT_ADDPROPERTY)
 
 #define IDBEVENTTARGET_SCRIPTABLE_FLAGS                                       \
   (EVENTTARGET_SCRIPTABLE_FLAGS)
 
 #define DOMCLASSINFO_STANDARD_FLAGS                                           \
-  (nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::DOM_OBJECT)
+  (nsIClassInfo::MAIN_THREAD_ONLY |                                           \
+   nsIClassInfo::DOM_OBJECT       |                                           \
+   nsIClassInfo::SINGLETON_CLASSINFO)
 
 
 #ifdef DEBUG
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
     eDOMClassInfo_##_class##_id,
 #else
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
   // nothing
@@ -4780,16 +4782,24 @@ nsDOMClassInfo::PostCreate(nsIXPConnectW
                            JSContext *cx, JSObject *obj)
 {
   NS_WARNING("nsDOMClassInfo::PostCreate Don't call me!");
 
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
+nsDOMClassInfo::PostTransplant(nsIXPConnectWrappedNative *wrapper,
+                               JSContext *cx, JSObject *obj)
+{
+  MOZ_NOT_REACHED("nsDOMClassInfo::PostTransplant Don't call me!");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMClassInfo::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                             JSObject *obj, jsid id, jsval *vp,
                             bool *_retval)
 {
   NS_WARNING("nsDOMClassInfo::AddProperty Don't call me!");
 
   return NS_ERROR_UNEXPECTED;
 }
@@ -8190,16 +8200,25 @@ nsElementSH::PostCreate(nsIXPConnectWrap
       nsContentUtils::AddScriptRunner(
         NS_NewRunnableMethod(binding, &nsXBLBinding::ExecuteAttachedHandler));
     }
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsElementSH::PostTransplant(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                            JSObject *obj)
+{
+  // XBL bindings are reapplied asynchronously when the node is inserted into a
+  // new document and frame construction occurs.
+  return NS_OK;
+}
+
 
 // Generic array scriptable helper.
 
 NS_IMETHODIMP
 nsGenericArraySH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                              JSObject *obj, jsid id, PRUint32 flags,
                              JSObject **objp, bool *_retval)
 {
@@ -8733,16 +8752,24 @@ nsDocumentSH::PostCreate(nsIXPConnectWra
                                JS_PropertyStub, JS_StrictPropertyStub,
                                JSPROP_READONLY | JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocumentSH::PostTransplant(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                             JSObject *obj)
+{
+  // Nothing to do here.
+  return NS_OK;
+}
+
 // HTMLDocument helper
 
 static nsresult
 ResolveImpl(JSContext *cx, nsIXPConnectWrappedNative *wrapper, jsid id,
             nsISupports **result, nsWrapperCache **aCache)
 {
   nsHTMLDocument *doc =
     static_cast<nsHTMLDocument*>(static_cast<nsINode*>(wrapper->Native()));
@@ -9857,16 +9884,30 @@ nsHTMLPluginObjElementSH::PostCreate(nsI
   nsRefPtr<nsPluginProtoChainInstallRunner> runner =
     new nsPluginProtoChainInstallRunner(wrapper, scriptContext);
   nsContentUtils::AddScriptRunner(runner);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsHTMLPluginObjElementSH::PostTransplant(nsIXPConnectWrappedNative *wrapper,
+                                         JSContext *cx, JSObject *obj)
+{
+  // Call through to PostCreate to do the prototype setup all over again. We
+  // may reuse the same prototype, in which case our prototype will be a wrapped
+  // version of the original.
+  nsresult rv = PostCreate(wrapper, cx, obj);
+  if (NS_FAILED(rv)) {
+      NS_WARNING("Calling PostCreate during PostTransplant for plugin element failed.");
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsHTMLPluginObjElementSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
                                       JSContext *cx, JSObject *obj, jsid id,
                                       jsval *vp, bool *_retval)
 {
   JSAutoRequest ar(cx);
 
   JSObject *pi_obj = ::JS_GetPrototype(obj);
   if (NS_UNLIKELY(!pi_obj)) {
@@ -10668,17 +10709,17 @@ nsDOMConstructorSH::HasInstance(nsIXPCon
 }
 
 NS_IMETHODIMP
 nsNonDOMObjectSH::GetFlags(PRUint32 *aFlags)
 {
   // This is NOT a DOM Object.  Use this helper class for cases when you need
   // to do something like implement nsISecurityCheckedComponent in a meaningful
   // way.
-  *aFlags = nsIClassInfo::MAIN_THREAD_ONLY;
+  *aFlags = nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::SINGLETON_CLASSINFO;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAttributeSH::GetFlags(PRUint32 *aFlags)
 {
   // Just like nsNodeSH, but without CONTENT_NODE
   *aFlags = DOMCLASSINFO_STANDARD_FLAGS;
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -557,16 +557,18 @@ protected:
   {
   }
 
 public:
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj);
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj);
+  NS_IMETHOD PostTransplant(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                            JSObject *obj);
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsElementSH(aData);
   }
 };
 
 
@@ -744,16 +746,18 @@ public:
 public:
   NS_IMETHOD PostCreatePrototype(JSContext * cx, JSObject * proto);
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, PRUint32 flags,
                         JSObject **objp, bool *_retval);
   NS_IMETHOD GetFlags(PRUint32* aFlags);
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj);
+  NS_IMETHOD  PostTransplant(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                             JSObject *obj);
 
   static nsIClassInfo *doCreate(nsDOMClassInfoData* aData)
   {
     return new nsDocumentSH(aData);
   }
 };
 
 
@@ -898,16 +902,18 @@ protected:
 public:
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, PRUint32 flags,
                         JSObject **objp, bool *_retval);
   NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx,
                        JSObject *globalObj, JSObject **parentObj);
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj);
+  NS_IMETHOD PostTransplant(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+                            JSObject *obj);
   NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, jsval *vp, bool *_retval);
   NS_IMETHOD SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, jsval *vp, bool *_retval);
   NS_IMETHOD Call(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                   JSObject *obj, PRUint32 argc, jsval *argv, jsval *vp,
                   bool *_retval);
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4481,19 +4481,25 @@ nsGlobalWindow::SetFullScreenInternal(bo
   if (aFullScreen && xulWin) {
     xulWin->SetIntrinsicallySized(false);
   }
 
   // Set this before so if widget sends an event indicating its
   // gone full screen, the state trap above works.
   mFullScreen = aFullScreen;
 
-  nsCOMPtr<nsIWidget> widget = GetMainWidget();
-  if (widget)
-    widget->MakeFullScreen(aFullScreen);
+  // Sometimes we don't want the top-level widget to actually go fullscreen,
+  // for example in the B2G desktop client, we don't want the emulated screen
+  // dimensions to appear to increase when entering fullscreen mode; we just
+  // want the content to fill the entire client area of the emulator window.
+  if (!Preferences::GetBool("full-screen-api.ignore-widgets", false)) {
+    nsCOMPtr<nsIWidget> widget = GetMainWidget();
+    if (widget)
+      widget->MakeFullScreen(aFullScreen);
+  }
 
   if (!mFullScreen) {
     // Force exit from DOM full-screen mode. This is so that if we're in
     // DOM full-screen mode and the user exits full-screen mode with
     // the browser full-screen mode toggle keyboard-shortcut, we'll detect
     // that and leave DOM API full-screen mode too.
     nsIDocument::ExitFullScreen(false);
   }
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -2,16 +2,17 @@
 /* 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 "base/basictypes.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "jswrapper.h"
 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsJSNPRuntime.h"
 #include "nsNPAPIPlugin.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsDOMJSUtils.h"
@@ -460,18 +461,38 @@ JSValToNPVariant(NPP npp, JSContext *cx,
       NS_ERROR("Unknown primitive type!");
 
       return false;
     }
 
     return true;
   }
 
-  NPObject *npobj =
-    nsJSObjWrapper::GetNewOrUsed(npp, cx, JSVAL_TO_OBJECT(val));
+  // The reflected plugin object may be in another compartment if the plugin
+  // element has since been adopted into a new document. We don't bother
+  // transplanting the plugin objects, and just do a unwrap with security
+  // checks if we encounter one of them as an argument. If the unwrap fails,
+  // we clear the pending exception and just run with the original wrapped object,
+  // since sometimes there are legitimate cases where a security wrapper ends
+  // up here (for example, Location objects, which are _always_ behind security
+  // wrappers).
+  //
+  // NB: In addition to clearing the pending exception, we also have to temporarily
+  // disable the error reporter, because SpiderMonkey calls it directly if there's
+  // no JS code on the stack, which might be the case here.
+  JSObject *obj = JSVAL_TO_OBJECT(val);
+  JSErrorReporter reporter = JS_SetErrorReporter(cx, NULL);
+  obj = js::UnwrapObjectChecked(cx, obj);
+  JS_SetErrorReporter(cx, reporter);
+  if (!obj) {
+    JS_ClearPendingException(cx);
+    obj = JSVAL_TO_OBJECT(val);
+  }
+
+  NPObject *npobj = nsJSObjWrapper::GetNewOrUsed(npp, cx, obj);
   if (!npobj) {
     return false;
   }
 
   // Pass over ownership of npobj to *variant
   OBJECT_TO_NPVARIANT(npobj, *variant);
 
   return true;
@@ -1121,37 +1142,50 @@ nsJSObjWrapper::GetNewOrUsed(NPP npp, JS
     PL_DHashTableRawRemove(&sJSObjWrappers, entry);
 
     return nsnull;
   }
 
   return wrapper;
 }
 
-static NPObject *
-GetNPObject(JSObject *obj)
+// Climb the prototype chain, unwrapping as necessary until we find an NP object
+// wrapper.
+//
+// Note that the returned value is not necessarily in the same compartment as cx.
+// Callers should use it in very limited ways (checking the private is fine).
+static JSObject *
+GetNPObjectWrapper(JSContext *cx, JSObject *obj)
 {
-  while (obj && JS_GetClass(obj) != &sNPObjectJSWrapperClass) {
+  while (obj && (obj = js::UnwrapObjectChecked(cx, obj))) {
+    if (JS_GetClass(obj) == &sNPObjectJSWrapperClass)
+      return obj;
     obj = ::JS_GetPrototype(obj);
   }
-
+  return NULL;
+}
+
+static NPObject *
+GetNPObject(JSContext *cx, JSObject *obj)
+{
+  obj = GetNPObjectWrapper(cx, obj);
   if (!obj) {
     return nsnull;
   }
 
   return (NPObject *)::JS_GetPrivate(obj);
 }
 
 
 // Does not actually add a property because this is always followed by a
 // SetProperty call.
 static JSBool
 NPObjWrapper_AddProperty(JSContext *cx, JSHandleObject obj, JSHandleId id, jsval *vp)
 {
-  NPObject *npobj = GetNPObject(obj);
+  NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->hasMethod) {
     ThrowJSException(cx, "Bad NPObject as private data!");
 
     return JS_FALSE;
   }
 
@@ -1182,17 +1216,17 @@ NPObjWrapper_AddProperty(JSContext *cx, 
   }
 
   return JS_TRUE;
 }
 
 static JSBool
 NPObjWrapper_DelProperty(JSContext *cx, JSHandleObject obj, JSHandleId id, jsval *vp)
 {
-  NPObject *npobj = GetNPObject(obj);
+  NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->removeProperty) {
     ThrowJSException(cx, "Bad NPObject as private data!");
 
     return JS_FALSE;
   }
 
@@ -1213,17 +1247,17 @@ NPObjWrapper_DelProperty(JSContext *cx, 
     *vp = JSVAL_FALSE;
 
   return ReportExceptionIfPending(cx);
 }
 
 static JSBool
 NPObjWrapper_SetProperty(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict, jsval *vp)
 {
-  NPObject *npobj = GetNPObject(obj);
+  NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->setProperty) {
     ThrowJSException(cx, "Bad NPObject as private data!");
 
     return JS_FALSE;
   }
 
@@ -1272,17 +1306,17 @@ NPObjWrapper_SetProperty(JSContext *cx, 
   }
 
   return JS_TRUE;
 }
 
 static JSBool
 NPObjWrapper_GetProperty(JSContext *cx, JSHandleObject obj, JSHandleId id, jsval *vp)
 {
-  NPObject *npobj = GetNPObject(obj);
+  NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->hasMethod || !npobj->_class->getProperty) {
     ThrowJSException(cx, "Bad NPObject as private data!");
 
     return JS_FALSE;
   }
 
@@ -1360,27 +1394,17 @@ NPObjWrapper_GetProperty(JSContext *cx, 
 
   return JS_TRUE;
 }
 
 static JSBool
 CallNPMethodInternal(JSContext *cx, JSObject *obj, unsigned argc, jsval *argv,
                      jsval *rval, bool ctorCall)
 {
-  while (obj && JS_GetClass(obj) != &sNPObjectJSWrapperClass) {
-    obj = ::JS_GetPrototype(obj);
-  }
-
-  if (!obj) {
-    ThrowJSException(cx, "NPMethod called on non-NPObject wrapped JSObject!");
-
-    return JS_FALSE;
-  }
-
-  NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
+  NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class) {
     ThrowJSException(cx, "Bad NPObject as private data!");
 
     return JS_FALSE;
   }
 
   // Find out what plugin (NPP) is the owner of the object we're
@@ -1513,17 +1537,17 @@ struct NPObjectEnumerateState {
   PRUint32     length;
   NPIdentifier *value;
 };
 
 static JSBool
 NPObjWrapper_newEnumerate(JSContext *cx, JSHandleObject obj, JSIterateOp enum_op,
                           jsval *statep, jsid *idp)
 {
-  NPObject *npobj = GetNPObject(obj);
+  NPObject *npobj = GetNPObject(cx, obj);
   NPIdentifier *enum_value;
   uint32_t length;
   NPObjectEnumerateState *state;
 
   if (!npobj || !npobj->_class) {
     ThrowJSException(cx, "Bad NPObject as private data!");
     return JS_FALSE;
   }
@@ -1593,17 +1617,17 @@ NPObjWrapper_newEnumerate(JSContext *cx,
 
   return JS_TRUE;
 }
 
 static JSBool
 NPObjWrapper_NewResolve(JSContext *cx, JSHandleObject obj, JSHandleId id, unsigned flags,
                         JSMutableHandleObject objp)
 {
-  NPObject *npobj = GetNPObject(obj);
+  NPObject *npobj = GetNPObject(cx, obj);
 
   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
       !npobj->_class->hasMethod) {
     ThrowJSException(cx, "Bad NPObject as private data!");
 
     return JS_FALSE;
   }
 
@@ -1753,17 +1777,17 @@ nsNPObjWrapper::OnDestroy(NPObject *npob
     // Found a live NPObject wrapper, null out its JSObjects' private
     // data.
 
     ::JS_SetPrivate(entry->mJSObj, nsnull);
 
     // Remove the npobj from the hash now that it went away.
     PL_DHashTableRawRemove(&sNPObjWrappers, entry);
 
-    OnWrapperDestroyed();
+    // The finalize hook will call OnWrapperDestroyed().
   }
 }
 
 // Look up or create a JSObject that wraps the NPObject npobj.
 
 // static
 JSObject *
 nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
@@ -1803,18 +1827,23 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JS
   if (!entry) {
     // Out of memory
     JS_ReportOutOfMemory(cx);
 
     return nsnull;
   }
 
   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
-    // Found a live NPObject wrapper, return it.
-    return entry->mJSObj;
+    // Found a live NPObject wrapper. It may not be in the same compartment
+    // as cx, so we need to wrap it before returning it.
+    JSObject *obj = entry->mJSObj;
+    if (!JS_WrapObject(cx, &obj)) {
+      return NULL;
+    }
+    return obj;
   }
 
   entry->mNPObj = npobj;
   entry->mNpp = npp;
 
   JSAutoRequest ar(cx);
 
   PRUint32 generation = sNPObjWrappers.generation;
@@ -2034,17 +2063,19 @@ nsJSNPRuntime::OnPluginDestroy(NPP npp)
   }
 
   // Loop over the DOM element's JS object prototype chain and remove
   // all JS objects of the class sNPObjectJSWrapperClass (there should
   // be only one, but remove all instances found in case the page put
   // more than one of the plugin's scriptable objects on the prototype
   // chain).
   while (obj && (proto = ::JS_GetPrototype(obj))) {
-    if (JS_GetClass(proto) == &sNPObjectJSWrapperClass) {
+    // Unwrap while checking the jsclass - if the prototype is a wrapper for
+    // an NP object, that counts too.
+    if (JS_GetClass(js::UnwrapObject(proto)) == &sNPObjectJSWrapperClass) {
       // We found an NPObject on the proto chain, get its prototype...
       proto = ::JS_GetPrototype(proto);
 
       // ... and pull it out of the chain.
       ::JS_SetPrototype(cx, obj, proto);
     }
 
     obj = proto;
@@ -2131,19 +2162,17 @@ CreateNPObjectMember(NPP npp, JSContext 
     }
   }
 
   fieldValue = NPVariantToJSVal(npp, cx, &npv);
 
   // npobjWrapper is the JSObject through which we make sure we don't
   // outlive the underlying NPObject, so make sure it points to the
   // real JSObject wrapper for the NPObject.
-  while (JS_GetClass(obj) != &sNPObjectJSWrapperClass) {
-    obj = ::JS_GetPrototype(obj);
-  }
+  obj = GetNPObjectWrapper(cx, obj);
 
   memberPrivate->npobjWrapper = obj;
 
   memberPrivate->fieldValue = fieldValue;
   memberPrivate->methodName = identifier;
   memberPrivate->npp = npp;
 
   ::JS_RemoveValueRoot(cx, vp);
@@ -2205,17 +2234,17 @@ NPObjectMember_Call(JSContext *cx, unsig
 
   NPObjectMemberPrivate *memberPrivate =
     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
                                                      &sNPObjectMemberClass,
                                                      JS_ARGV(cx, vp));
   if (!memberPrivate || !memberPrivate->npobjWrapper)
     return JS_FALSE;
 
-  NPObject *npobj = GetNPObject(memberPrivate->npobjWrapper);
+  NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper);
   if (!npobj) {
     ThrowJSException(cx, "Call on invalid member object");
 
     return JS_FALSE;
   }
 
   NPVariant npargs_buf[8];
   NPVariant *npargs = npargs_buf;
--- a/dom/plugins/test/mochitest/Makefile.in
+++ b/dom/plugins/test/mochitest/Makefile.in
@@ -59,16 +59,18 @@ MOCHITEST_FILES = \
   test_crashing.html \
   $(warning test_crashing2.html disabled due to random orange; see bug 566049) \
   test_hanging.html \
   crashing_subpage.html \
   test_GCrace.html \
   test_propertyAndMethod.html \
   test_bug539565-1.html \
   test_bug539565-2.html \
+  test_bug771202.html \
+  file_bug771202.html \
   test_enumerate.html \
   test_npruntime_construct.html \
   307-xo-redirect.sjs \
   test_redirect_handling.html \
   test_zero_opacity.html \
   test_NPPVpluginWantsAllNetworkStreams.html \
   test_npruntime_npnsetexception.html \
   test_NPNVdocumentOrigin.html \
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/file_bug771202.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+  <embed id="pluginElement" type="application/x-test" width="200" height="200"></embed>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_bug771202.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=771202
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 771202</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.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=771202">Mozilla Bug 771202</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for recreating spliced plugin prototype chains after tranplant. **/
+SimpleTest.waitForExplicitFinish();
+
+function go() {
+  // Set things up.
+  var win = document.getElementById('ifr').contentWindow;
+  var plugin = win.document.getElementById('pluginElement');
+  var testValue = plugin.getObjectValue();
+
+  function checkPlugin() {
+      dump("About to call checkObjectValue\n");
+      ok(plugin.checkObjectValue(testValue), 'Plugin proto should work correctly');
+  }
+  // First, check things before transplanting.
+  checkPlugin();
+
+  // Adopt the plugin and retest.
+  document.body.appendChild(plugin);
+  checkPlugin();
+
+  // All done.
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+
+<iframe id="ifr" onload="go();" src="file_bug771202.html">
+</body>
+</html>
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -69,16 +69,18 @@ using mozilla::MutexAutoLock;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
 
 USING_WORKERS_NAMESPACE
 using namespace mozilla::dom::workers::events;
 using namespace mozilla::dom;
 
+NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
+
 namespace {
 
 const char gErrorChars[] = "error";
 const char gMessageChars[] = "message";
 
 template <class T>
 class AutoPtrComparator
 {
@@ -118,162 +120,16 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
   T* raw = nsnull;
   aSrc.swap(raw);
 
   nsISupports* rawSupports =
     static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
   dest->swap(rawSupports);
 }
 
-NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
-
-struct WorkerJSRuntimeStats : public JS::RuntimeStats
-{
-  WorkerJSRuntimeStats(nsACString &aRtPath)
-   : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) { }
-
-  ~WorkerJSRuntimeStats() {
-    for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
-      free(compartmentStatsVector[i].extra1);
-      // no need to free |extra2|, because it's a static string
-    }
-  }
-
-  virtual void initExtraCompartmentStats(JSCompartment *c,
-                                         JS::CompartmentStats *cstats) MOZ_OVERRIDE
-  {
-    MOZ_ASSERT(!cstats->extra1);
-    MOZ_ASSERT(!cstats->extra2);
-    
-    // ReportJSRuntimeExplicitTreeStats expects that cstats->{extra1,extra2}
-    // are char pointers.
-
-    // This is the |cJSPathPrefix|.  Each worker has exactly two compartments:
-    // one for atoms, and one for everything else.
-    nsCString cJSPathPrefix(mRtPath);
-    cJSPathPrefix += js::IsAtomsCompartment(c)
-                   ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
-                   : NS_LITERAL_CSTRING("compartment(web-worker)/");
-    cstats->extra1 = strdup(cJSPathPrefix.get());
-
-    // This is the |cDOMPathPrefix|, which should never be used when reporting
-    // with workers (hence the "?!").
-    cstats->extra2 = (void *)"explicit/workers/?!/";
-  }
-
-private:
-  nsCString mRtPath;
-};
-  
-class WorkerMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
-{
-  WorkerPrivate* mWorkerPrivate;
-  nsCString mAddressString;
-  nsCString mRtPath;
-
-public:
-  NS_DECL_ISUPPORTS
-
-  WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate)
-  : mWorkerPrivate(aWorkerPrivate)
-  {
-    aWorkerPrivate->AssertIsOnWorkerThread();
-
-    nsCString escapedDomain(aWorkerPrivate->Domain());
-    escapedDomain.ReplaceChar('/', '\\');
-
-    NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
-    escapedURL.ReplaceChar('/', '\\');
-
-    {
-      // 64bit address plus '0x' plus null terminator.
-      char address[21];
-      uint32_t addressSize =
-        JS_snprintf(address, sizeof(address), "%p", aWorkerPrivate);
-      if (addressSize != uint32_t(-1)) {
-        mAddressString.Assign(address, addressSize);
-      }
-      else {
-        NS_WARNING("JS_snprintf failed!");
-        mAddressString.AssignLiteral("<unknown address>");
-      }
-    }
-
-    mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") +
-              escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
-              escapedURL + NS_LITERAL_CSTRING(", ") + mAddressString +
-              NS_LITERAL_CSTRING(")/");
-  }
-
-  nsresult
-  CollectForRuntime(bool aIsQuick, void* aData)
-  {
-    AssertIsOnMainThread();
-
-    if (mWorkerPrivate) {
-      bool disabled;
-      if (!mWorkerPrivate->BlockAndCollectRuntimeStats(aIsQuick, aData, &disabled)) {
-        return NS_ERROR_FAILURE;
-      }
-
-      // Don't ever try to talk to the worker again.
-      if (disabled) {
-#ifdef DEBUG
-        {
-          nsCAutoString message("Unable to report memory for ");
-          if (mWorkerPrivate->IsChromeWorker()) {
-            message.AppendLiteral("Chrome");
-          }
-          message += NS_LITERAL_CSTRING("Worker (") + mAddressString +
-                     NS_LITERAL_CSTRING(")! It is either using ctypes or is in "
-                                        "the process of being destroyed");
-          NS_WARNING(message.get());
-        }
-#endif
-        mWorkerPrivate = nsnull;
-      }
-    }
-    return NS_OK;
-  }
-
-  NS_IMETHOD GetName(nsACString &aName)
-  {
-      aName.AssignLiteral("workers");
-      return NS_OK;
-  }
-
-  NS_IMETHOD
-  CollectReports(nsIMemoryMultiReporterCallback* aCallback,
-                 nsISupports* aClosure)
-  {
-    AssertIsOnMainThread();
-
-    WorkerJSRuntimeStats rtStats(mRtPath);
-    nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    // Always report, even if we're disabled, so that we at least get an entry
-    // in about::memory.
-    return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath,
-                                                 aCallback, aClosure);
-  }
-
-  NS_IMETHOD
-  GetExplicitNonHeap(PRInt64 *aAmount)
-  {
-    AssertIsOnMainThread();
-
-    return CollectForRuntime(/* isQuick = */true, aAmount);
-  }
-};
-
-NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter)
-
 struct WorkerStructuredCloneCallbacks
 {
   static JSObject*
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
     // See if object is a nsIDOMFile pointer.
     if (aTag == DOMWORKER_SCTAG_FILE) {
@@ -1605,16 +1461,175 @@ public:
     }
 
     return true;
   }
 };
 
 } /* anonymous namespace */
 
+struct WorkerJSRuntimeStats : public JS::RuntimeStats
+{
+  WorkerJSRuntimeStats(nsACString &aRtPath)
+   : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) { }
+
+  ~WorkerJSRuntimeStats() {
+    for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
+      free(compartmentStatsVector[i].extra1);
+      // no need to free |extra2|, because it's a static string
+    }
+  }
+
+  virtual void initExtraCompartmentStats(JSCompartment *c,
+                                         JS::CompartmentStats *cstats) MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(!cstats->extra1);
+    MOZ_ASSERT(!cstats->extra2);
+    
+    // ReportJSRuntimeExplicitTreeStats expects that cstats->{extra1,extra2}
+    // are char pointers.
+
+    // This is the |cJSPathPrefix|.  Each worker has exactly two compartments:
+    // one for atoms, and one for everything else.
+    nsCString cJSPathPrefix(mRtPath);
+    cJSPathPrefix += js::IsAtomsCompartment(c)
+                   ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
+                   : NS_LITERAL_CSTRING("compartment(web-worker)/");
+    cstats->extra1 = strdup(cJSPathPrefix.get());
+
+    // This is the |cDOMPathPrefix|, which should never be used when reporting
+    // with workers (hence the "?!").
+    cstats->extra2 = (void *)"explicit/workers/?!/";
+  }
+
+private:
+  nsCString mRtPath;
+};
+  
+BEGIN_WORKERS_NAMESPACE
+
+class WorkerMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
+{
+  WorkerPrivate* mWorkerPrivate;
+  nsCString mAddressString;
+  nsCString mRtPath;
+
+public:
+  NS_DECL_ISUPPORTS
+
+  WorkerMemoryReporter(WorkerPrivate* aWorkerPrivate)
+  : mWorkerPrivate(aWorkerPrivate)
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    nsCString escapedDomain(aWorkerPrivate->Domain());
+    escapedDomain.ReplaceChar('/', '\\');
+
+    NS_ConvertUTF16toUTF8 escapedURL(aWorkerPrivate->ScriptURL());
+    escapedURL.ReplaceChar('/', '\\');
+
+    {
+      // 64bit address plus '0x' plus null terminator.
+      char address[21];
+      uint32_t addressSize =
+        JS_snprintf(address, sizeof(address), "%p", aWorkerPrivate);
+      if (addressSize != uint32_t(-1)) {
+        mAddressString.Assign(address, addressSize);
+      }
+      else {
+        NS_WARNING("JS_snprintf failed!");
+        mAddressString.AssignLiteral("<unknown address>");
+      }
+    }
+
+    mRtPath = NS_LITERAL_CSTRING("explicit/workers/workers(") +
+              escapedDomain + NS_LITERAL_CSTRING(")/worker(") +
+              escapedURL + NS_LITERAL_CSTRING(", ") + mAddressString +
+              NS_LITERAL_CSTRING(")/");
+  }
+
+  nsresult
+  CollectForRuntime(bool aIsQuick, void* aData)
+  {
+    AssertIsOnMainThread();
+
+    if (!mWorkerPrivate) {
+#ifdef DEBUG
+      nsCAutoString message("Unable to report memory for ");
+      if (mWorkerPrivate->IsChromeWorker()) {
+        message.AppendLiteral("Chrome");
+      }
+      message += NS_LITERAL_CSTRING("Worker (") + mAddressString +
+                 NS_LITERAL_CSTRING(")! It is either using ctypes or is in "
+                                    "the process of being destroyed");
+      NS_WARNING(message.get());
+#endif
+      return NS_OK;
+    }
+    
+    if (!mWorkerPrivate->BlockAndCollectRuntimeStats(aIsQuick, aData)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+  }
+
+  NS_IMETHOD GetName(nsACString &aName)
+  {
+    aName.AssignLiteral("workers");
+    return NS_OK;
+  }
+
+  NS_IMETHOD
+  CollectReports(nsIMemoryMultiReporterCallback* aCallback,
+                 nsISupports* aClosure)
+  {
+    AssertIsOnMainThread();
+
+    WorkerJSRuntimeStats rtStats(mRtPath);
+    nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    // Always report, even if we're disabled, so that we at least get an entry
+    // in about::memory.
+    return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath,
+                                                 aCallback, aClosure);
+  }
+
+  NS_IMETHOD
+  GetExplicitNonHeap(PRInt64 *aAmount)
+  {
+    AssertIsOnMainThread();
+
+    return CollectForRuntime(/* isQuick = */true, aAmount);
+  }
+
+  void Disable()
+  {
+#ifdef DEBUG
+    // Setting mWorkerPrivate to nsnull is safe only because we've locked the
+    // worker's mutex on the worker's thread, in the caller.  So we check that.
+    //
+    // Also, we may have already disabled the reporter (and thus set
+    // mWorkerPrivate to nsnull) due to the use of CTypes (see
+    // ChromeWorkerScope.cpp).  That's why the NULL check is necessary.
+    if (mWorkerPrivate) {
+        mWorkerPrivate->mMutex.AssertCurrentThreadOwns();
+    }
+#endif
+    mWorkerPrivate = nsnull;
+  }
+};
+
+END_WORKERS_NAMESPACE
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter)
+
 #ifdef DEBUG
 void
 mozilla::dom::workers::AssertIsOnMainThread()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget,
@@ -2446,18 +2461,17 @@ WorkerPrivate::WorkerPrivate(JSContext* 
                              nsCOMPtr<nsIDocument>& aDocument)
 : WorkerPrivateParent<WorkerPrivate>(aCx, aObject, aParent, aParentJSContext,
                                      aScriptURL, aIsChromeWorker, aDomain,
                                      aWindow, aParentScriptContext, aBaseURI,
                                      aPrincipal, aDocument),
   mJSContext(nsnull), mErrorHandlerRecursionCount(0), mNextTimeoutId(1),
   mStatus(Pending), mSuspended(false), mTimerRunning(false),
   mRunningExpiredTimeouts(false), mCloseHandlerStarted(false),
-  mCloseHandlerFinished(false), mMemoryReporterRunning(false),
-  mMemoryReporterDisabled(false)
+  mCloseHandlerFinished(false), mMemoryReporterRunning(false)
 {
   MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivate);
 }
 
 WorkerPrivate::~WorkerPrivate()
 {
   MOZ_COUNT_DTOR(mozilla::dom::workers::WorkerPrivate);
 }
@@ -2894,30 +2908,23 @@ WorkerPrivate::ScheduleDeletion(bool aWa
       new TopLevelWorkerFinishedRunnable(this, currentThread);
     if (NS_FAILED(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL))) {
       NS_WARNING("Failed to dispatch runnable!");
     }
   }
 }
 
 bool
-WorkerPrivate::BlockAndCollectRuntimeStats(bool aIsQuick, void* aData, bool* aDisabled)
+WorkerPrivate::BlockAndCollectRuntimeStats(bool aIsQuick, void* aData)
 {
   AssertIsOnMainThread();
   NS_ASSERTION(aData, "Null data!");
 
   {
     MutexAutoLock lock(mMutex);
-
-    if (mMemoryReporterDisabled) {
-      *aDisabled = true;
-      return true;
-    }
-
-    *aDisabled = false;
     mMemoryReporterRunning = true;
   }
 
   bool succeeded;
 
   nsRefPtr<CollectRuntimeStatsRunnable> runnable =
     new CollectRuntimeStatsRunnable(this, aIsQuick, aData, &succeeded);
   if (!runnable->Dispatch(nsnull)) {
@@ -2938,22 +2945,24 @@ WorkerPrivate::DisableMemoryReporter()
 {
   AssertIsOnWorkerThread();
 
   bool result = true;
 
   {
     MutexAutoLock lock(mMutex);
 
-    mMemoryReporterDisabled = true;
-
     while (mMemoryReporterRunning) {
       MutexAutoUnlock unlock(mMutex);
       result = ProcessAllControlRunnables() && result;
     }
+
+    if (mMemoryReporter) {
+      mMemoryReporter->Disable();
+    }
   }
 
   return result;
 }
 
 bool
 WorkerPrivate::ProcessAllControlRunnables()
 {
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -26,25 +26,25 @@
 
 #include "EventTarget.h"
 #include "Queue.h"
 #include "WorkerFeature.h"
 
 class JSAutoStructuredCloneBuffer;
 class nsIDocument;
 class nsIPrincipal;
-class nsIMemoryMultiReporter;
 class nsIScriptContext;
 class nsIURI;
 class nsPIDOMWindow;
 class nsITimer;
 class nsIXPCScriptNotify;
 
 BEGIN_WORKERS_NAMESPACE
 
+class WorkerMemoryReporter;
 class WorkerPrivate;
 
 class WorkerRunnable : public nsIRunnable
 {
 public:
   enum Target { ParentThread, WorkerThread };
   enum BusyBehavior { ModifyBusyCount, UnchangedBusyCount };
   enum ClearingBehavior { SkipWhenClearing, RunWhenClearing };
@@ -490,16 +490,17 @@ public:
   AssertInnerWindowIsCorrect() const
   { }
 #endif
 };
 
 class WorkerPrivate : public WorkerPrivateParent<WorkerPrivate>
 {
   friend class WorkerPrivateParent<WorkerPrivate>;
+  friend class WorkerMemoryReporter;
   typedef WorkerPrivateParent<WorkerPrivate> ParentType;
 
   struct TimeoutInfo;
 
   typedef Queue<WorkerRunnable*, 50> EventQueue;
   EventQueue mQueue;
   EventQueue mControlQueue;
 
@@ -529,29 +530,28 @@ class WorkerPrivate : public WorkerPriva
   nsRefPtr<WorkerCrossThreadDispatcher> mCrossThreadDispatcher;
 
   // Things touched on worker thread only.
   nsTArray<ParentType*> mChildWorkers;
   nsTArray<WorkerFeature*> mFeatures;
   nsTArray<nsAutoPtr<TimeoutInfo> > mTimeouts;
 
   nsCOMPtr<nsITimer> mTimer;
-  nsCOMPtr<nsIMemoryMultiReporter> mMemoryReporter;
+  nsRefPtr<WorkerMemoryReporter> mMemoryReporter;
 
   mozilla::TimeStamp mKillTime;
   PRUint32 mErrorHandlerRecursionCount;
   PRUint32 mNextTimeoutId;
   Status mStatus;
   bool mSuspended;
   bool mTimerRunning;
   bool mRunningExpiredTimeouts;
   bool mCloseHandlerStarted;
   bool mCloseHandlerFinished;
   bool mMemoryReporterRunning;
-  bool mMemoryReporterDisabled;
 
 #ifdef DEBUG
   nsCOMPtr<nsIThread> mThread;
 #endif
 
 public:
   ~WorkerPrivate();
 
@@ -677,17 +677,17 @@ public:
 
   void
   UpdateJSRuntimeHeapSizeInternal(JSContext* aCx, PRUint32 aJSRuntimeHeapSize);
 
   void
   ScheduleDeletion(bool aWasPending);
 
   bool
-  BlockAndCollectRuntimeStats(bool isQuick, void* aData, bool* aDisabled);
+  BlockAndCollectRuntimeStats(bool isQuick, void* aData);
 
   bool
   DisableMemoryReporter();
 
 #ifdef JS_GC_ZEAL
   void
   UpdateGCZealInternal(JSContext* aCx, PRUint8 aGCZeal);
 #endif
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -193,11 +193,15 @@ NOTE: we previously supported ARM assemb
 pixman-export.patch: use cairo_public for PIXMAN_EXPORT to make sure pixman symbols are not exported in libxul
 
 pixman-limits.patch: include limits.h for SIZE_MAX
 
 pixman-lowres-interp.patch: Use lower quality interpolation for more speed.
 
 pixman-bilinear-fastpath.patch: Bilinear fast paths for non-neon
 
+pixman-16-bit-pipeline.patch: 16 bit pipeline for dithering
+
+pixman-dither.patch: Add dithering of 16 bit gradients
+
 ==== disable printing patch ====
 
 disable-printing.patch:  allows us to use NS_PRINTING to disable printing.
--- a/gfx/cairo/libpixman/src/Makefile.in
+++ b/gfx/cairo/libpixman/src/Makefile.in
@@ -76,16 +76,17 @@ endif
 endif
 
 
 CSRCS	= \
 	pixman-access.c \
 	pixman-access-accessors.c \
 	pixman-bits-image.c \
 	pixman.c \
+	pixman-combine16.c \
 	pixman-combine32.c \
 	pixman-combine64.c \
 	pixman-conical-gradient.c \
 	pixman-cpu.c \
 	pixman-edge.c \
 	pixman-edge-accessors.c \
 	pixman-fast-path.c \
 	pixman-general.c \
--- a/gfx/cairo/libpixman/src/pixman-access.c
+++ b/gfx/cairo/libpixman/src/pixman-access.c
@@ -933,16 +933,54 @@ store_scanline_x2b10g10r10 (bits_image_t
     {
 	WRITE (image, pixel++,
 	       ((values[i] >> 38) & 0x3ff) |
 	       ((values[i] >> 12) & 0xffc00) |
 	       ((values[i] << 14) & 0x3ff00000));
     }
 }
 
+static void
+store_scanline_16 (bits_image_t *  image,
+		   int             x,
+		   int             y,
+		   int             width,
+		   const uint32_t *v)
+{
+    uint16_t *bits = (uint16_t*)(image->bits + image->rowstride * y);
+    uint16_t *values = (uint16_t *)v;
+    uint16_t *pixel = bits + x;
+    int i;
+
+    for (i = 0; i < width; ++i)
+    {
+	WRITE (image, pixel++, values[i]);
+    }
+}
+
+static void
+fetch_scanline_16 (pixman_image_t *image,
+                            int             x,
+                            int             y,
+                            int             width,
+                            uint32_t *      b,
+                            const uint32_t *mask)
+{
+    const uint16_t *bits = (uint16_t*)(image->bits.bits + y * image->bits.rowstride);
+    const uint16_t *pixel = bits + x;
+    int i;
+    uint16_t *buffer = (uint16_t *)b;
+
+    for (i = 0; i < width; ++i)
+    {
+	*buffer++ = READ (image, pixel++);
+    }
+}
+
+
 /*
  * Contracts a 64bpp image to 32bpp and then stores it using a regular 32-bit
  * store proc. Despite the type, this function expects a uint64_t buffer.
  */
 static void
 store_scanline_generic_64 (bits_image_t *  image,
                            int             x,
                            int             y,
@@ -1044,32 +1082,47 @@ fetch_pixel_generic_lossy_32 (bits_image
     pixman_contract (&result, &pixel64, 1);
 
     return result;
 }
 
 typedef struct
 {
     pixman_format_code_t	format;
+    fetch_scanline_t		fetch_scanline_16;
     fetch_scanline_t		fetch_scanline_32;
     fetch_scanline_t		fetch_scanline_64;
     fetch_pixel_32_t		fetch_pixel_32;
     fetch_pixel_64_t		fetch_pixel_64;
+    store_scanline_t		store_scanline_16;
     store_scanline_t		store_scanline_32;
     store_scanline_t		store_scanline_64;
 } format_info_t;
 
 #define FORMAT_INFO(format) 						\
     {									\
 	PIXMAN_ ## format,						\
+	    NULL,							\
 	    fetch_scanline_ ## format,					\
 	    fetch_scanline_generic_64,					\
 	    fetch_pixel_ ## format, fetch_pixel_generic_64,		\
+	    NULL,							\
 	    store_scanline_ ## format, store_scanline_generic_64	\
     }
+#define FORMAT_INFO16(format) 						\
+    {									\
+	PIXMAN_ ## format,						\
+	    fetch_scanline_16,						\
+	    fetch_scanline_ ## format,					\
+	    fetch_scanline_generic_64,					\
+	    fetch_pixel_ ## format, fetch_pixel_generic_64,		\
+	    store_scanline_16,						\
+	    store_scanline_ ## format, store_scanline_generic_64	\
+    }
+
 
 static const format_info_t accessors[] =
 {
 /* 32 bpp formats */
     FORMAT_INFO (a8r8g8b8),
     FORMAT_INFO (x8r8g8b8),
     FORMAT_INFO (a8b8g8r8),
     FORMAT_INFO (x8b8g8r8),
@@ -1079,18 +1132,18 @@ static const format_info_t accessors[] =
     FORMAT_INFO (r8g8b8x8),
     FORMAT_INFO (x14r6g6b6),
 
 /* 24bpp formats */
     FORMAT_INFO (r8g8b8),
     FORMAT_INFO (b8g8r8),
     
 /* 16bpp formats */
-    FORMAT_INFO (r5g6b5),
-    FORMAT_INFO (b5g6r5),
+    FORMAT_INFO16 (r5g6b5),
+    FORMAT_INFO16 (b5g6r5),
     
     FORMAT_INFO (a1r5g5b5),
     FORMAT_INFO (x1r5g5b5),
     FORMAT_INFO (a1b5g5r5),
     FORMAT_INFO (x1b5g5r5),
     FORMAT_INFO (a4r4g4b4),
     FORMAT_INFO (x4r4g4b4),
     FORMAT_INFO (a4b4g4r4),
@@ -1132,62 +1185,64 @@ static const format_info_t accessors[] =
     
 /* 1bpp formats */
     FORMAT_INFO (a1),
     FORMAT_INFO (g1),
     
 /* Wide formats */
     
     { PIXMAN_a2r10g10b10,
-      NULL, fetch_scanline_a2r10g10b10,
+      NULL, NULL, fetch_scanline_a2r10g10b10,
       fetch_pixel_generic_lossy_32, fetch_pixel_a2r10g10b10,
       NULL, store_scanline_a2r10g10b10 },
     
     { PIXMAN_x2r10g10b10,
-      NULL, fetch_scanline_x2r10g10b10,
+      NULL, NULL, fetch_scanline_x2r10g10b10,
       fetch_pixel_generic_lossy_32, fetch_pixel_x2r10g10b10,
       NULL, store_scanline_x2r10g10b10 },
     
     { PIXMAN_a2b10g10r10,
-      NULL, fetch_scanline_a2b10g10r10,
+      NULL, NULL, fetch_scanline_a2b10g10r10,
       fetch_pixel_generic_lossy_32, fetch_pixel_a2b10g10r10,
       NULL, store_scanline_a2b10g10r10 },
     
     { PIXMAN_x2b10g10r10,
-      NULL, fetch_scanline_x2b10g10r10,
+      NULL, NULL, fetch_scanline_x2b10g10r10,
       fetch_pixel_generic_lossy_32, fetch_pixel_x2b10g10r10,
       NULL, store_scanline_x2b10g10r10 },
     
 /* YUV formats */
     { PIXMAN_yuy2,
-      fetch_scanline_yuy2, fetch_scanline_generic_64,
+      NULL, fetch_scanline_yuy2, fetch_scanline_generic_64,
       fetch_pixel_yuy2, fetch_pixel_generic_64,
       NULL, NULL },
     
     { PIXMAN_yv12,
-      fetch_scanline_yv12, fetch_scanline_generic_64,
+      NULL, fetch_scanline_yv12, fetch_scanline_generic_64,
       fetch_pixel_yv12, fetch_pixel_generic_64,
       NULL, NULL },
     
     { PIXMAN_null },
 };
 
 static void
 setup_accessors (bits_image_t *image)
 {
     const format_info_t *info = accessors;
     
     while (info->format != PIXMAN_null)
     {
 	if (info->format == image->format)
 	{
+	    image->fetch_scanline_16 = info->fetch_scanline_16;
 	    image->fetch_scanline_32 = info->fetch_scanline_32;
 	    image->fetch_scanline_64 = info->fetch_scanline_64;
 	    image->fetch_pixel_32 = info->fetch_pixel_32;
 	    image->fetch_pixel_64 = info->fetch_pixel_64;
+	    image->store_scanline_16 = info->store_scanline_16;
 	    image->store_scanline_32 = info->store_scanline_32;
 	    image->store_scanline_64 = info->store_scanline_64;
 	    
 	    return;
 	}
 	
 	info++;
     }
--- a/gfx/cairo/libpixman/src/pixman-bits-image.c
+++ b/gfx/cairo/libpixman/src/pixman-bits-image.c
@@ -1247,16 +1247,31 @@ src_get_scanline_wide (pixman_iter_t *it
 
 void
 _pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter)
 {
     if (iter->flags & ITER_NARROW)
 	iter->get_scanline = src_get_scanline_narrow;
     else
 	iter->get_scanline = src_get_scanline_wide;
+
+}
+
+static uint32_t *
+dest_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
+{
+    pixman_image_t *image  = iter->image;
+    int             x      = iter->x;
+    int             y      = iter->y;
+    int             width  = iter->width;
+    uint32_t *	    buffer = iter->buffer;
+
+    image->bits.fetch_scanline_16 (image, x, y, width, buffer, mask);
+
+    return iter->buffer;
 }
 
 static uint32_t *
 dest_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
 {
     pixman_image_t *image  = iter->image;
     int             x      = iter->x;
     int             y      = iter->y;
@@ -1327,16 +1342,30 @@ dest_get_scanline_wide (pixman_iter_t *i
 	    free (alpha);
 	}
     }
 
     return iter->buffer;
 }
 
 static void
+dest_write_back_16 (pixman_iter_t *iter)
+{
+    bits_image_t *  image  = &iter->image->bits;
+    int             x      = iter->x;
+    int             y      = iter->y;
+    int             width  = iter->width;
+    const uint32_t *buffer = iter->buffer;
+
+    image->store_scanline_16 (image, x, y, width, buffer);
+
+    iter->y++;
+}
+
+static void
 dest_write_back_narrow (pixman_iter_t *iter)
 {
     bits_image_t *  image  = &iter->image->bits;
     int             x      = iter->x;
     int             y      = iter->y;
     int             width  = iter->width;
     const uint32_t *buffer = iter->buffer;
 
@@ -1375,28 +1404,41 @@ dest_write_back_wide (pixman_iter_t *ite
     }
 
     iter->y++;
 }
 
 void
 _pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter)
 {
-    if (iter->flags & ITER_NARROW)
+    if (iter->flags & ITER_16)
+    {
+        if ((iter->flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) ==
+	    (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA))
+	{
+            iter->get_scanline = _pixman_iter_get_scanline_noop;
+        }
+        else
+        {
+	    iter->get_scanline = dest_get_scanline_16;
+        }
+	iter->write_back = dest_write_back_16;
+    }
+    else if (iter->flags & ITER_NARROW)
     {
 	if ((iter->flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) ==
 	    (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA))
 	{
 	    iter->get_scanline = _pixman_iter_get_scanline_noop;
 	}
 	else
 	{
 	    iter->get_scanline = dest_get_scanline_narrow;
 	}
-	
+
 	iter->write_back = dest_write_back_narrow;
     }
     else
     {
 	iter->get_scanline = dest_get_scanline_wide;
 	iter->write_back = dest_write_back_wide;
     }
 }
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-combine16.c
@@ -0,0 +1,124 @@
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+
+#include "pixman-private.h"
+
+#include "pixman-combine32.h"
+
+static force_inline uint32_t
+combine_mask (const uint32_t src, const uint32_t mask)
+{
+    uint32_t s, m;
+
+    m = mask >> A_SHIFT;
+
+    if (!m)
+	return 0;
+    s = src;
+
+    UN8x4_MUL_UN8 (s, m);
+
+    return s;
+}
+
+static inline uint32_t convert_0565_to_8888(uint16_t color)
+{
+    return CONVERT_0565_TO_8888(color);
+}
+
+static inline uint16_t convert_8888_to_0565(uint32_t color)
+{
+    return CONVERT_8888_TO_0565(color);
+}
+
+static void
+combine_src_u (pixman_implementation_t *imp,
+               pixman_op_t              op,
+               uint32_t *               dest,
+               const uint32_t *         src,
+               const uint32_t *         mask,
+               int                      width)
+{
+    int i;
+
+    if (!mask)
+	memcpy (dest, src, width * sizeof (uint16_t));
+    else
+    {
+	uint16_t *d = (uint16_t*)dest;
+	uint16_t *src16 = (uint16_t*)src;
+	for (i = 0; i < width; ++i)
+	{
+	    if ((*mask & 0xff000000) == 0xff000000) {
+		// it's likely worth special casing
+		// fully opaque because it avoids
+		// the cost of conversion as well the multiplication
+		*(d + i) = *src16;
+	    } else {
+		// the mask is still 32bits
+		uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
+		*(d + i) = convert_8888_to_0565(s);
+	    }
+	    mask++;
+	    src16++;
+	}
+    }
+
+}
+
+static void
+combine_over_u (pixman_implementation_t *imp,
+               pixman_op_t              op,
+               uint32_t *                dest,
+               const uint32_t *          src,
+               const uint32_t *          mask,
+               int                      width)
+{
+    int i;
+
+    if (!mask)
+	memcpy (dest, src, width * sizeof (uint16_t));
+    else
+    {
+	uint16_t *d = (uint16_t*)dest;
+	uint16_t *src16 = (uint16_t*)src;
+	for (i = 0; i < width; ++i)
+	{
+	    if ((*mask & 0xff000000) == 0xff000000) {
+		// it's likely worth special casing
+		// fully opaque because it avoids
+		// the cost of conversion as well the multiplication
+		*(d + i) = *src16;
+	    } else if ((*mask & 0xff000000) == 0x00000000) {
+		// keep the dest the same
+	    } else {
+		// the mask is still 32bits
+		uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
+		uint32_t ia = ALPHA_8 (~s);
+		uint32_t d32 = convert_0565_to_8888(*(d + i));
+		UN8x4_MUL_UN8_ADD_UN8x4 (d32, ia, s);
+		*(d + i) = convert_8888_to_0565(d32);
+	    }
+	    mask++;
+	    src16++;
+	}
+    }
+
+}
+
+
+void
+_pixman_setup_combiner_functions_16 (pixman_implementation_t *imp)
+{
+    int i;
+    for (i = 0; i < PIXMAN_N_OPERATORS; i++) {
+	imp->combine_16[i] = NULL;
+    }
+    imp->combine_16[PIXMAN_OP_SRC] = combine_src_u;
+    imp->combine_16[PIXMAN_OP_OVER] = combine_over_u;
+}
+
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/libpixman/src/pixman-dither.h
@@ -0,0 +1,51 @@
+#define R16_BITS     5
+#define G16_BITS     6
+#define B16_BITS     5
+
+#define R16_SHIFT    (B16_BITS + G16_BITS)
+#define G16_SHIFT    (B16_BITS)
+#define B16_SHIFT    0
+
+#define MASK 0xff
+#define ONE_HALF 0x80
+
+#define A_SHIFT 8 * 3
+#define R_SHIFT 8 * 2
+#define G_SHIFT 8
+#define A_MASK 0xff000000
+#define R_MASK 0xff0000
+#define G_MASK 0xff00
+
+#define RB_MASK 0xff00ff
+#define AG_MASK 0xff00ff00
+#define RB_ONE_HALF 0x800080
+#define RB_MASK_PLUS_ONE 0x10000100
+
+#define ALPHA_8(x) ((x) >> A_SHIFT)
+#define RED_8(x) (((x) >> R_SHIFT) & MASK)
+#define GREEN_8(x) (((x) >> G_SHIFT) & MASK)
+#define BLUE_8(x) ((x) & MASK)
+
+// This uses the same dithering technique that Skia does.
+// It is essentially preturbing the lower bit based on the
+// high bit
+static inline uint16_t dither_32_to_16(uint32_t c)
+{
+    uint8_t b = BLUE_8(c);
+    uint8_t g = GREEN_8(c);
+    uint8_t r = RED_8(c);
+    r = ((r << 1) - ((r >> (8 - R16_BITS) << (8 - R16_BITS)) | (r >> R16_BITS))) >> (8 - R16_BITS);
+    g = ((g << 1) - ((g >> (8 - G16_BITS) << (8 - G16_BITS)) | (g >> G16_BITS))) >> (8 - G16_BITS);
+    b = ((b << 1) - ((b >> (8 - B16_BITS) << (8 - B16_BITS)) | (b >> B16_BITS))) >> (8 - B16_BITS);
+    return ((r << R16_SHIFT) | (g << G16_SHIFT) | (b << B16_SHIFT));
+}
+
+static inline uint16_t dither_8888_to_0565(uint32_t color, pixman_bool_t toggle)
+{
+    // alternate between a preturbed truncation and a regular truncation
+    if (toggle) {
+	return dither_32_to_16(color);
+    } else {
+	return CONVERT_8888_TO_0565(color);
+    }
+}
--- a/gfx/cairo/libpixman/src/pixman-general.c
+++ b/gfx/cairo/libpixman/src/pixman-general.c
@@ -106,46 +106,61 @@ general_composite_rect  (pixman_implemen
     PIXMAN_COMPOSITE_ARGS (info);
     uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 3 + 7) / 8];
     uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer;
     uint8_t *src_buffer, *mask_buffer, *dest_buffer;
     pixman_iter_t src_iter, mask_iter, dest_iter;
     pixman_combine_32_func_t compose;
     pixman_bool_t component_alpha;
     iter_flags_t narrow, src_flags;
+    iter_flags_t rgb16;
     int Bpp;
     int i;
 
     if ((src_image->common.flags & FAST_PATH_NARROW_FORMAT)		    &&
 	(!mask_image || mask_image->common.flags & FAST_PATH_NARROW_FORMAT) &&
 	(dest_image->common.flags & FAST_PATH_NARROW_FORMAT))
     {
 	narrow = ITER_NARROW;
 	Bpp = 4;
     }
     else
     {
 	narrow = 0;
 	Bpp = 8;
     }
 
+    // XXX: This special casing is bad. Ideally, we'd keep the general code general perhaps
+    // by having it deal more specifically with different intermediate formats
+    if (
+	(dest_image->common.flags & FAST_PATH_16_FORMAT && (src_image->type == LINEAR || src_image->type == RADIAL)) &&
+	( op == PIXMAN_OP_SRC ||
+         (op == PIXMAN_OP_OVER && (src_image->common.flags & FAST_PATH_IS_OPAQUE))
+	)
+	) {
+	rgb16 = ITER_16;
+    } else {
+	rgb16 = 0;
+    }
+
+
     if (width * Bpp > SCANLINE_BUFFER_LENGTH)
     {
 	scanline_buffer = pixman_malloc_abc (width, 3, Bpp);
 
 	if (!scanline_buffer)
 	    return;
     }
 
     src_buffer = scanline_buffer;
     mask_buffer = src_buffer + width * Bpp;
     dest_buffer = mask_buffer + width * Bpp;
 
     /* src iter */
-    src_flags = narrow | op_flags[op].src;
+    src_flags = narrow | op_flags[op].src | rgb16;
 
     _pixman_implementation_src_iter_init (imp->toplevel, &src_iter, src_image,
 					  src_x, src_y, width, height,
 					  src_buffer, src_flags);
 
     /* mask iter */
     if ((src_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) ==
 	(ITER_IGNORE_ALPHA | ITER_IGNORE_RGB))
@@ -164,20 +179,20 @@ general_composite_rect  (pixman_implemen
 
     _pixman_implementation_src_iter_init (
 	imp->toplevel, &mask_iter, mask_image, mask_x, mask_y, width, height,
 	mask_buffer, narrow | (component_alpha? 0 : ITER_IGNORE_RGB));
 
     /* dest iter */
     _pixman_implementation_dest_iter_init (
 	imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height,
-	dest_buffer, narrow | op_flags[op].dst);
+	dest_buffer, narrow | op_flags[op].dst | rgb16);
 
     compose = _pixman_implementation_lookup_combiner (
-	imp->toplevel, op, component_alpha, narrow);
+	imp->toplevel, op, component_alpha, narrow, !!rgb16);
 
     if (!compose)
 	return;
 
     for (i = 0; i < height; ++i)
     {
 	uint32_t *s, *m, *d;
 
@@ -234,16 +249,17 @@ general_fill (pixman_implementation_t *i
     return FALSE;
 }
 
 pixman_implementation_t *
 _pixman_implementation_create_general (void)
 {
     pixman_implementation_t *imp = _pixman_implementation_create (NULL, general_fast_path);
 
+    _pixman_setup_combiner_functions_16 (imp);
     _pixman_setup_combiner_functions_32 (imp);
     _pixman_setup_combiner_functions_64 (imp);
 
     imp->blt = general_blt;
     imp->fill = general_fill;
     imp->src_iter_init = general_src_iter_init;
     imp->dest_iter_init = general_dest_iter_init;
 
--- a/gfx/cairo/libpixman/src/pixman-image.c
+++ b/gfx/cairo/libpixman/src/pixman-image.c
@@ -451,16 +451,20 @@ compute_image_info (pixman_image_t *imag
 		flags |= FAST_PATH_IS_OPAQUE;
 	}
 
 	if (image->bits.read_func || image->bits.write_func)
 	    flags &= ~FAST_PATH_NO_ACCESSORS;
 
 	if (PIXMAN_FORMAT_IS_WIDE (image->bits.format))
 	    flags &= ~FAST_PATH_NARROW_FORMAT;
+
+	if (image->bits.format == PIXMAN_r5g6b5)
+	    flags |= FAST_PATH_16_FORMAT;
+
 	break;
 
     case RADIAL:
 	code = PIXMAN_unknown;
 
 	/*
 	 * As explained in pixman-radial-gradient.c, every point of
 	 * the plane has a valid associated radius (and thus will be
--- a/gfx/cairo/libpixman/src/pixman-implementation.c
+++ b/gfx/cairo/libpixman/src/pixman-implementation.c
@@ -101,45 +101,51 @@ pixman_implementation_t *
     imp->fill = delegate_fill;
     imp->src_iter_init = delegate_src_iter_init;
     imp->dest_iter_init = delegate_dest_iter_init;
 
     imp->fast_paths = fast_paths;
 
     for (i = 0; i < PIXMAN_N_OPERATORS; ++i)
     {
+	imp->combine_16[i] = NULL;
 	imp->combine_32[i] = NULL;
 	imp->combine_64[i] = NULL;
 	imp->combine_32_ca[i] = NULL;
 	imp->combine_64_ca[i] = NULL;
     }
 
     return imp;
 }
 
 pixman_combine_32_func_t
 _pixman_implementation_lookup_combiner (pixman_implementation_t *imp,
 					pixman_op_t		 op,
 					pixman_bool_t		 component_alpha,
-					pixman_bool_t		 narrow)
+					pixman_bool_t		 narrow,
+					pixman_bool_t		 rgb16)
 {
     pixman_combine_32_func_t f;
 
     do
     {
 	pixman_combine_32_func_t (*combiners[]) =
 	{
 	    (pixman_combine_32_func_t *)imp->combine_64,
 	    (pixman_combine_32_func_t *)imp->combine_64_ca,
 	    imp->combine_32,
 	    imp->combine_32_ca,
+	    (pixman_combine_32_func_t *)imp->combine_16,
+	    NULL,
 	};
-
-	f = combiners[component_alpha | (narrow << 1)][op];
-
+        if (rgb16) {
+            f = combiners[4][op];
+        } else {
+            f = combiners[component_alpha + (narrow << 1)][op];
+        }
 	imp = imp->delegate;
     }
     while (!f);
 
     return f;
 }
 
 pixman_bool_t
--- a/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
@@ -26,16 +26,18 @@
  */
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 #include <stdlib.h>
 #include "pixman-private.h"
 
+#include "pixman-dither.h"
+
 static pixman_bool_t
 linear_gradient_is_horizontal (pixman_image_t *image,
 			       int             x,
 			       int             y,
 			       int             width,
 			       int             height)
 {
     linear_gradient_t *linear = (linear_gradient_t *)image;
@@ -217,42 +219,204 @@ linear_get_scanline_narrow (pixman_iter_
 	}
     }
 
     iter->y++;
 
     return iter->buffer;
 }
 
+static uint16_t convert_8888_to_0565(uint32_t color)
+{
+    return CONVERT_8888_TO_0565(color);
+}
+
+
+
+static uint32_t *
+linear_get_scanline_16 (pixman_iter_t  *iter,
+			const uint32_t *mask)
+{
+    pixman_image_t *image  = iter->image;
+    int             x      = iter->x;
+    int             y      = iter->y;
+    int             width  = iter->width;
+    uint16_t *      buffer = (uint16_t*)iter->buffer;
+    pixman_bool_t   toggle = ((x ^ y) & 1);
+
+    pixman_vector_t v, unit;
+    pixman_fixed_32_32_t l;
+    pixman_fixed_48_16_t dx, dy;
+    gradient_t *gradient = (gradient_t *)image;
+    linear_gradient_t *linear = (linear_gradient_t *)image;
+    uint16_t *end = buffer + width;
+    pixman_gradient_walker_t walker;
+
+    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
+
+    /* reference point is the center of the pixel */
+    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
+    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
+    v.vector[2] = pixman_fixed_1;
+
+    if (image->common.transform)
+    {
+	if (!pixman_transform_point_3d (image->common.transform, &v))
+	    return iter->buffer;
+
+	unit.vector[0] = image->common.transform->matrix[0][0];
+	unit.vector[1] = image->common.transform->matrix[1][0];
+	unit.vector[2] = image->common.transform->matrix[2][0];
+    }
+    else
+    {
+	unit.vector[0] = pixman_fixed_1;
+	unit.vector[1] = 0;
+	unit.vector[2] = 0;
+    }
+
+    dx = linear->p2.x - linear->p1.x;
+    dy = linear->p2.y - linear->p1.y;
+
+    l = dx * dx + dy * dy;
+
+    if (l == 0 || unit.vector[2] == 0)
+    {
+	/* affine transformation only */
+        pixman_fixed_32_32_t t, next_inc;
+	double inc;
+
+	if (l == 0 || v.vector[2] == 0)
+	{
+	    t = 0;
+	    inc = 0;
+	}
+	else
+	{
+	    double invden, v2;
+
+	    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
+		(l * (double) v.vector[2]);
+	    v2 = v.vector[2] * (1. / pixman_fixed_1);
+	    t = ((dx * v.vector[0] + dy * v.vector[1]) - 
+		 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+	    inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
+	}
+	next_inc = 0;
+
+	if (((pixman_fixed_32_32_t )(inc * width)) == 0)
+	{
+	    register uint32_t color;
+	    uint16_t dither_diff;
+	    uint16_t color16;
+	    uint16_t color16b;
+
+	    color = _pixman_gradient_walker_pixel (&walker, t);
+	    color16 = dither_8888_to_0565(color, toggle);
+	    color16b = dither_8888_to_0565(color, toggle^1);
+	    // compute the difference
+	    dither_diff =  color16 ^ color16b;
+	    while (buffer < end) {
+		*buffer++ = color16;
+		// use dither_diff to toggle between color16 and color16b
+		color16 ^= dither_diff;
+		toggle ^= 1;
+	    }
+	}
+	else
+	{
+	    int i;
+
+	    i = 0;
+	    while (buffer < end)
+	    {
+		if (!mask || *mask++)
+		{
+		    *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
+										 t + next_inc),
+						  toggle);
+		}
+		toggle ^= 1;
+		i++;
+		next_inc = inc * i;
+		buffer++;
+	    }
+	}
+    }
+    else
+    {
+	/* projective transformation */
+        double t;
+
+	t = 0;
+
+	while (buffer < end)
+	{
+	    if (!mask || *mask++)
+	    {
+	        if (v.vector[2] != 0)
+		{
+		    double invden, v2;
+
+		    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
+			(l * (double) v.vector[2]);
+		    v2 = v.vector[2] * (1. / pixman_fixed_1);
+		    t = ((dx * v.vector[0] + dy * v.vector[1]) - 
+			 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+		}
+
+		*buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t),
+					      toggle);
+	    }
+	    toggle ^= 1;
+
+	    ++buffer;
+
+	    v.vector[0] += unit.vector[0];
+	    v.vector[1] += unit.vector[1];
+	    v.vector[2] += unit.vector[2];
+	}
+    }
+
+    iter->y++;
+
+    return iter->buffer;
+}
+
 static uint32_t *
 linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
 {
     uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
 
     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
 
     return buffer;
 }
 
 void
 _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
 {
-    if (linear_gradient_is_horizontal (
+    // XXX: we can't use this optimization when dithering
+    if (0 && linear_gradient_is_horizontal (
 	    iter->image, iter->x, iter->y, iter->width, iter->height))
     {
-	if (iter->flags & ITER_NARROW)
+	if (iter->flags & ITER_16)
+	    linear_get_scanline_16 (iter, NULL);
+	else if (iter->flags & ITER_NARROW)
 	    linear_get_scanline_narrow (iter, NULL);
 	else
 	    linear_get_scanline_wide (iter, NULL);
 
 	iter->get_scanline = _pixman_iter_get_scanline_noop;
     }
     else
     {
-	if (iter->flags & ITER_NARROW)
+	if (iter->flags & ITER_16)
+	    iter->get_scanline = linear_get_scanline_16;
+	else if (iter->flags & ITER_NARROW)
 	    iter->get_scanline = linear_get_scanline_narrow;
 	else
 	    iter->get_scanline = linear_get_scanline_wide;
     }
 }
 
 PIXMAN_EXPORT pixman_image_t *
 pixman_image_create_linear_gradient (pixman_point_fixed_t *        p1,
--- a/gfx/cairo/libpixman/src/pixman-private.h
+++ b/gfx/cairo/libpixman/src/pixman-private.h
@@ -152,24 +152,28 @@ struct bits_image
     int                        height;
     uint32_t *                 bits;
     uint32_t *                 free_me;
     int                        rowstride;  /* in number of uint32_t's */
 
     fetch_scanline_t           get_scanline_32;
     fetch_scanline_t           get_scanline_64;
 
+    fetch_scanline_t           fetch_scanline_16;
+
     fetch_scanline_t           fetch_scanline_32;
     fetch_pixel_32_t	       fetch_pixel_32;
     store_scanline_t           store_scanline_32;
 
     fetch_scanline_t           fetch_scanline_64;
     fetch_pixel_64_t	       fetch_pixel_64;
     store_scanline_t           store_scanline_64;
 
+    store_scanline_t           store_scanline_16;
+
     /* Used for indirect access to the bits */
     pixman_read_memory_func_t  read_func;
     pixman_write_memory_func_t write_func;
 };
 
 union pixman_image
 {
     image_type_t       type;
@@ -202,17 +206,24 @@ typedef enum
      * destination.
      *
      * When he destination is xRGB, this is useful knowledge, because then
      * we can treat it as if it were ARGB, which means in some cases we can
      * avoid copying it to a temporary buffer.
      */
     ITER_LOCALIZED_ALPHA =	(1 << 1),
     ITER_IGNORE_ALPHA =		(1 << 2),
-    ITER_IGNORE_RGB =		(1 << 3)
+    ITER_IGNORE_RGB =		(1 << 3),
+
+    /* With the addition of ITER_16 we now have two flags that to represent
+     * 3 pipelines. This means that there can be an invalid state when
+     * both ITER_NARROW and ITER_16 are set. In this case
+     * ITER_16 overrides NARROW and we should use the 16 bit pipeline.
+     * Note: ITER_16 still has a 32 bit mask, which is a bit weird. */
+    ITER_16 =			(1 << 4)
 } iter_flags_t;
 
 struct pixman_iter_t
 {
     /* These are initialized by _pixman_implementation_{src,dest}_init */
     pixman_image_t *		image;
     uint32_t *			buffer;
     int				x, y;
@@ -429,16 +440,17 @@ typedef pixman_bool_t (*pixman_fill_func
 					     int                      x,
 					     int                      y,
 					     int                      width,
 					     int                      height,
 					     uint32_t                 xor);
 typedef void (*pixman_iter_init_func_t) (pixman_implementation_t *imp,
                                          pixman_iter_t           *iter);
 
+void _pixman_setup_combiner_functions_16 (pixman_implementation_t *imp);
 void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp);
 void _pixman_setup_combiner_functions_64 (pixman_implementation_t *imp);
 
 typedef struct
 {
     pixman_op_t             op;
     pixman_format_code_t    src_format;
     uint32_t		    src_flags;
@@ -459,32 +471,34 @@ struct pixman_implementation_t
     pixman_fill_func_t		fill;
     pixman_iter_init_func_t     src_iter_init;
     pixman_iter_init_func_t     dest_iter_init;
 
     pixman_combine_32_func_t	combine_32[PIXMAN_N_OPERATORS];
     pixman_combine_32_func_t	combine_32_ca[PIXMAN_N_OPERATORS];
     pixman_combine_64_func_t	combine_64[PIXMAN_N_OPERATORS];
     pixman_combine_64_func_t	combine_64_ca[PIXMAN_N_OPERATORS];
+    pixman_combine_64_func_t	combine_16[PIXMAN_N_OPERATORS];
 };
 
 uint32_t
 _pixman_image_get_solid (pixman_implementation_t *imp,
 			 pixman_image_t *         image,
                          pixman_format_code_t     format);
 
 pixman_implementation_t *
 _pixman_implementation_create (pixman_implementation_t *delegate,
 			       const pixman_fast_path_t *fast_paths);
 
 pixman_combine_32_func_t
 _pixman_implementation_lookup_combiner (pixman_implementation_t *imp,
 					pixman_op_t		 op,
 					pixman_bool_t		 component_alpha,
-					pixman_bool_t		 wide);
+					pixman_bool_t		 wide,
+					pixman_bool_t		 rgb16);
 
 pixman_bool_t
 _pixman_implementation_blt (pixman_implementation_t *imp,
                             uint32_t *               src_bits,
                             uint32_t *               dst_bits,
                             int                      src_stride,
                             int                      dst_stride,
                             int                      src_bpp,
@@ -613,16 +627,17 @@ uint32_t *
 #define FAST_PATH_Y_UNIT_ZERO			(1 << 18)
 #define FAST_PATH_BILINEAR_FILTER		(1 << 19)
 #define FAST_PATH_ROTATE_90_TRANSFORM		(1 << 20)
 #define FAST_PATH_ROTATE_180_TRANSFORM		(1 << 21)
 #define FAST_PATH_ROTATE_270_TRANSFORM		(1 << 22)
 #define FAST_PATH_SAMPLES_COVER_CLIP_NEAREST	(1 << 23)
 #define FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR	(1 << 24)
 #define FAST_PATH_BITS_IMAGE			(1 << 25)
+#define FAST_PATH_16_FORMAT			(1 << 26)
 
 #define FAST_PATH_PAD_REPEAT						\
     (FAST_PATH_NO_NONE_REPEAT		|				\
      FAST_PATH_NO_NORMAL_REPEAT		|				\
      FAST_PATH_NO_REFLECT_REPEAT)
 
 #define FAST_PATH_NORMAL_REPEAT						\
     (FAST_PATH_NO_NONE_REPEAT		|				\
--- a/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
@@ -29,16 +29,18 @@
 
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
 #include <stdlib.h>
 #include <math.h>
 #include "pixman-private.h"
 
+#include "pixman-dither.h"
+
 static inline pixman_fixed_32_32_t
 dot (pixman_fixed_48_16_t x1,
      pixman_fixed_48_16_t y1,
      pixman_fixed_48_16_t z1,
      pixman_fixed_48_16_t x2,
      pixman_fixed_48_16_t y2,
      pixman_fixed_48_16_t z2)
 {
@@ -395,35 +397,294 @@ radial_get_scanline_narrow (pixman_iter_
 	    v.vector[2] += unit.vector[2];
 	}
     }
 
     iter->y++;
     return iter->buffer;
 }
 
+static uint16_t convert_8888_to_0565(uint32_t color)
+{
+    return CONVERT_8888_TO_0565(color);
+}
+
+static uint32_t *
+radial_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
+{
+    /*
+     * Implementation of radial gradients following the PDF specification.
+     * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
+     * Manual (PDF 32000-1:2008 at the time of this writing).
+     *
+     * In the radial gradient problem we are given two circles (c₁,r₁) and
+     * (c₂,r₂) that define the gradient itself.
+     *
+     * Mathematically the gradient can be defined as the family of circles
+     *
+     *     ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
+     *
+     * excluding those circles whose radius would be < 0. When a point
+     * belongs to more than one circle, the one with a bigger t is the only
+     * one that contributes to its color. When a point does not belong
+     * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
+     * Further limitations on the range of values for t are imposed when
+     * the gradient is not repeated, namely t must belong to [0,1].
+     *
+     * The graphical result is the same as drawing the valid (radius > 0)
+     * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient
+     * is not repeated) using SOURCE operator composition.
+     *
+     * It looks like a cone pointing towards the viewer if the ending circle
+     * is smaller than the starting one, a cone pointing inside the page if
+     * the starting circle is the smaller one and like a cylinder if they
+     * have the same radius.
+     *
+     * What we actually do is, given the point whose color we are interested
+     * in, compute the t values for that point, solving for t in:
+     *
+     *     length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
+     *
+     * Let's rewrite it in a simpler way, by defining some auxiliary
+     * variables:
+     *
+     *     cd = c₂ - c₁
+     *     pd = p - c₁
+     *     dr = r₂ - r₁
+     *     length(t·cd - pd) = r₁ + t·dr
+     *
+     * which actually means
+     *
+     *     hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
+     *
+     * or
+     *
+     *     ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
+     *
+     * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes:
+     *
+     *     (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
+     *
+     * where we can actually expand the squares and solve for t:
+     *
+     *     t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
+     *       = r₁² + 2·r₁·t·dr + t²·dr²
+     *
+     *     (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
+     *         (pdx² + pdy² - r₁²) = 0
+     *
+     *     A = cdx² + cdy² - dr²
+     *     B = pdx·cdx + pdy·cdy + r₁·dr
+     *     C = pdx² + pdy² - r₁²
+     *     At² - 2Bt + C = 0
+     *
+     * The solutions (unless the equation degenerates because of A = 0) are:
+     *
+     *     t = (B ± ⎷(B² - A·C)) / A
+     *
+     * The solution we are going to prefer is the bigger one, unless the
+     * radius associated to it is negative (or it falls outside the valid t
+     * range).
+     *
+     * Additional observations (useful for optimizations):
+     * A does not depend on p
+     *
+     * A < 0 <=> one of the two circles completely contains the other one
+     *   <=> for every p, the radiuses associated with the two t solutions
+     *       have opposite sign
+     */
+    pixman_image_t *image = iter->image;
+    int x = iter->x;
+    int y = iter->y;
+    int width = iter->width;
+    uint16_t *buffer = iter->buffer;
+    pixman_bool_t toggle = ((x ^ y) & 1);
+
+    gradient_t *gradient = (gradient_t *)image;
+    radial_gradient_t *radial = (radial_gradient_t *)image;
+    uint16_t *end = buffer + width;
+    pixman_gradient_walker_t walker;
+    pixman_vector_t v, unit;
+
+    /* reference point is the center of the pixel */
+    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
+    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
+    v.vector[2] = pixman_fixed_1;
+
+    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
+
+    if (image->common.transform)
+    {
+	if (!pixman_transform_point_3d (image->common.transform, &v))
+	    return iter->buffer;
+
+	unit.vector[0] = image->common.transform->matrix[0][0];
+	unit.vector[1] = image->common.transform->matrix[1][0];
+	unit.vector[2] = image->common.transform->matrix[2][0];
+    }
+    else
+    {
+	unit.vector[0] = pixman_fixed_1;
+	unit.vector[1] = 0;
+	unit.vector[2] = 0;
+    }
+
+    if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1)
+    {
+	/*
+	 * Given:
+	 *
+	 * t = (B ± ⎷(B² - A·C)) / A
+	 *
+	 * where
+	 *
+	 * A = cdx² + cdy² - dr²
+	 * B = pdx·cdx + pdy·cdy + r₁·dr
+	 * C = pdx² + pdy² - r₁²
+	 * det = B² - A·C
+	 *
+	 * Since we have an affine transformation, we know that (pdx, pdy)
+	 * increase linearly with each pixel,
+	 *
+	 * pdx = pdx₀ + n·ux,
+	 * pdy = pdy₀ + n·uy,
+	 *
+	 * we can then express B, C and det through multiple differentiation.
+	 */
+	pixman_fixed_32_32_t b, db, c, dc, ddc;
+
+	/* warning: this computation may overflow */
+	v.vector[0] -= radial->c1.x;
+	v.vector[1] -= radial->c1.y;
+
+	/*
+	 * B and C are computed and updated exactly.
+	 * If fdot was used instead of dot, in the worst case it would
+	 * lose 11 bits of precision in each of the multiplication and
+	 * summing up would zero out all the bit that were preserved,
+	 * thus making the result 0 instead of the correct one.
+	 * This would mean a worst case of unbound relative error or
+	 * about 2^10 absolute error
+	 */
+	b = dot (v.vector[0], v.vector[1], radial->c1.radius,
+		 radial->delta.x, radial->delta.y, radial->delta.radius);
+	db = dot (unit.vector[0], unit.vector[1], 0,
+		  radial->delta.x, radial->delta.y, 0);
+
+	c = dot (v.vector[0], v.vector[1],
+		 -((pixman_fixed_48_16_t) radial->c1.radius),
+		 v.vector[0], v.vector[1], radial->c1.radius);
+	dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0],
+		  2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1],
+		  0,
+		  unit.vector[0], unit.vector[1], 0);
+	ddc = 2 * dot (unit.vector[0], unit.vector[1], 0,
+		       unit.vector[0], unit.vector[1], 0);
+
+	while (buffer < end)
+	{
+	    if (!mask || *mask++)
+	    {
+		*buffer = dither_8888_to_0565(
+			  radial_compute_color (radial->a, b, c,
+						radial->inva,
+						radial->delta.radius,
+						radial->mindr,
+						&walker,
+						image->common.repeat),
+			  toggle);
+	    }
+
+	    toggle ^= 1;
+	    b += db;
+	    c += dc;
+	    dc += ddc;
+	    ++buffer;
+	}
+    }
+    else
+    {
+	/* projective */
+	/* Warning:
+	 * error propagation guarantees are much looser than in the affine case
+	 */
+	while (buffer < end)
+	{
+	    if (!mask || *mask++)
+	    {
+		if (v.vector[2] != 0)
+		{
+		    double pdx, pdy, invv2, b, c;
+
+		    invv2 = 1. * pixman_fixed_1 / v.vector[2];
+
+		    pdx = v.vector[0] * invv2 - radial->c1.x;
+		    /*    / pixman_fixed_1 */
+
+		    pdy = v.vector[1] * invv2 - radial->c1.y;
+		    /*    / pixman_fixed_1 */
+
+		    b = fdot (pdx, pdy, radial->c1.radius,
+			      radial->delta.x, radial->delta.y,
+			      radial->delta.radius);
+		    /*  / pixman_fixed_1 / pixman_fixed_1 */
+
+		    c = fdot (pdx, pdy, -radial->c1.radius,
+			      pdx, pdy, radial->c1.radius);
+		    /*  / pixman_fixed_1 / pixman_fixed_1 */
+
+		    *buffer = dither_8888_to_0565 (
+			      radial_compute_color (radial->a, b, c,
+						    radial->inva,
+						    radial->delta.radius,
+						    radial->mindr,
+						    &walker,
+						    image->common.repeat),
+			      toggle);
+		}
+		else
+		{
+		    *buffer = 0;
+		}
+	    }
+
+	    ++buffer;
+	    toggle ^= 1;
+
+	    v.vector[0] += unit.vector[0];
+	    v.vector[1] += unit.vector[1];
+	    v.vector[2] += unit.vector[2];
+	}
+    }
+
+    iter->y++;
+    return iter->buffer;
+}
 static uint32_t *
 radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
 {
     uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
 
     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
 
     return buffer;
 }
 
 void
 _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
 {
-    if (iter->flags & ITER_NARROW)
+    if (iter->flags & ITER_16)
+	iter->get_scanline = radial_get_scanline_16;
+    else if (iter->flags & ITER_NARROW)
 	iter->get_scanline = radial_get_scanline_narrow;
     else
 	iter->get_scanline = radial_get_scanline_wide;
 }
 
+
 PIXMAN_EXPORT pixman_image_t *
 pixman_image_create_radial_gradient (pixman_point_fixed_t *        inner,
                                      pixman_point_fixed_t *        outer,
                                      pixman_fixed_t                inner_radius,
                                      pixman_fixed_t                outer_radius,
                                      const pixman_gradient_stop_t *stops,
                                      int                           n_stops)
 {
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/pixman-16-bit-pipeline.patch
@@ -0,0 +1,1242 @@
+diff --git a/gfx/cairo/libpixman/src/pixman-access.c b/gfx/cairo/libpixman/src/pixman-access.c
+--- a/gfx/cairo/libpixman/src/pixman-access.c
++++ b/gfx/cairo/libpixman/src/pixman-access.c
+@@ -933,16 +933,54 @@ store_scanline_x2b10g10r10 (bits_image_t
+     {
+ 	WRITE (image, pixel++,
+ 	       ((values[i] >> 38) & 0x3ff) |
+ 	       ((values[i] >> 12) & 0xffc00) |
+ 	       ((values[i] << 14) & 0x3ff00000));
+     }
+ }
+ 
++static void
++store_scanline_16 (bits_image_t *  image,
++		   int             x,
++		   int             y,
++		   int             width,
++		   const uint32_t *v)
++{
++    uint16_t *bits = (uint16_t*)(image->bits + image->rowstride * y);
++    uint16_t *values = (uint16_t *)v;
++    uint16_t *pixel = bits + x;
++    int i;
++
++    for (i = 0; i < width; ++i)
++    {
++	WRITE (image, pixel++, values[i]);
++    }
++}
++
++static void
++fetch_scanline_16 (pixman_image_t *image,
++                            int             x,
++                            int             y,
++                            int             width,
++                            uint32_t *      b,
++                            const uint32_t *mask)
++{
++    const uint16_t *bits = (uint16_t*)(image->bits.bits + y * image->bits.rowstride);
++    const uint16_t *pixel = bits + x;
++    int i;
++    uint16_t *buffer = (uint16_t *)b;
++
++    for (i = 0; i < width; ++i)
++    {
++	*buffer++ = READ (image, pixel++);
++    }
++}
++
++
+ /*
+  * Contracts a 64bpp image to 32bpp and then stores it using a regular 32-bit
+  * store proc. Despite the type, this function expects a uint64_t buffer.
+  */
+ static void
+ store_scanline_generic_64 (bits_image_t *  image,
+                            int             x,
+                            int             y,
+@@ -1044,32 +1082,47 @@ fetch_pixel_generic_lossy_32 (bits_image
+     pixman_contract (&result, &pixel64, 1);
+ 
+     return result;
+ }
+ 
+ typedef struct
+ {
+     pixman_format_code_t	format;
++    fetch_scanline_t		fetch_scanline_16;
+     fetch_scanline_t		fetch_scanline_32;
+     fetch_scanline_t		fetch_scanline_64;
+     fetch_pixel_32_t		fetch_pixel_32;
+     fetch_pixel_64_t		fetch_pixel_64;
++    store_scanline_t		store_scanline_16;
+     store_scanline_t		store_scanline_32;
+     store_scanline_t		store_scanline_64;
+ } format_info_t;
+ 
+ #define FORMAT_INFO(format) 						\
+     {									\
+ 	PIXMAN_ ## format,						\
++	    NULL,							\
+ 	    fetch_scanline_ ## format,					\
+ 	    fetch_scanline_generic_64,					\
+ 	    fetch_pixel_ ## format, fetch_pixel_generic_64,		\
++	    NULL,							\
+ 	    store_scanline_ ## format, store_scanline_generic_64	\
+     }
++#define FORMAT_INFO16(format) 						\
++    {									\
++	PIXMAN_ ## format,						\
++	    fetch_scanline_16,						\
++	    fetch_scanline_ ## format,					\
++	    fetch_scanline_generic_64,					\
++	    fetch_pixel_ ## format, fetch_pixel_generic_64,		\
++	    store_scanline_16,						\
++	    store_scanline_ ## format, store_scanline_generic_64	\
++    }
++
+ 
+ static const format_info_t accessors[] =
+ {
+ /* 32 bpp formats */
+     FORMAT_INFO (a8r8g8b8),
+     FORMAT_INFO (x8r8g8b8),
+     FORMAT_INFO (a8b8g8r8),
+     FORMAT_INFO (x8b8g8r8),
+@@ -1079,18 +1132,18 @@ static const format_info_t accessors[] =
+     FORMAT_INFO (r8g8b8x8),
+     FORMAT_INFO (x14r6g6b6),
+ 
+ /* 24bpp formats */
+     FORMAT_INFO (r8g8b8),
+     FORMAT_INFO (b8g8r8),
+     
+ /* 16bpp formats */
+-    FORMAT_INFO (r5g6b5),
+-    FORMAT_INFO (b5g6r5),
++    FORMAT_INFO16 (r5g6b5),
++    FORMAT_INFO16 (b5g6r5),
+     
+     FORMAT_INFO (a1r5g5b5),
+     FORMAT_INFO (x1r5g5b5),
+     FORMAT_INFO (a1b5g5r5),
+     FORMAT_INFO (x1b5g5r5),
+     FORMAT_INFO (a4r4g4b4),
+     FORMAT_INFO (x4r4g4b4),
+     FORMAT_INFO (a4b4g4r4),
+@@ -1132,62 +1185,64 @@ static const format_info_t accessors[] =
+     
+ /* 1bpp formats */
+     FORMAT_INFO (a1),
+     FORMAT_INFO (g1),
+     
+ /* Wide formats */
+     
+     { PIXMAN_a2r10g10b10,
+-      NULL, fetch_scanline_a2r10g10b10,
++      NULL, NULL, fetch_scanline_a2r10g10b10,
+       fetch_pixel_generic_lossy_32, fetch_pixel_a2r10g10b10,
+       NULL, store_scanline_a2r10g10b10 },
+     
+     { PIXMAN_x2r10g10b10,
+-      NULL, fetch_scanline_x2r10g10b10,
++      NULL, NULL, fetch_scanline_x2r10g10b10,
+       fetch_pixel_generic_lossy_32, fetch_pixel_x2r10g10b10,
+       NULL, store_scanline_x2r10g10b10 },
+     
+     { PIXMAN_a2b10g10r10,
+-      NULL, fetch_scanline_a2b10g10r10,
++      NULL, NULL, fetch_scanline_a2b10g10r10,
+       fetch_pixel_generic_lossy_32, fetch_pixel_a2b10g10r10,
+       NULL, store_scanline_a2b10g10r10 },
+     
+     { PIXMAN_x2b10g10r10,
+-      NULL, fetch_scanline_x2b10g10r10,
++      NULL, NULL, fetch_scanline_x2b10g10r10,
+       fetch_pixel_generic_lossy_32, fetch_pixel_x2b10g10r10,
+       NULL, store_scanline_x2b10g10r10 },
+     
+ /* YUV formats */
+     { PIXMAN_yuy2,
+-      fetch_scanline_yuy2, fetch_scanline_generic_64,
++      NULL, fetch_scanline_yuy2, fetch_scanline_generic_64,
+       fetch_pixel_yuy2, fetch_pixel_generic_64,
+       NULL, NULL },
+     
+     { PIXMAN_yv12,
+-      fetch_scanline_yv12, fetch_scanline_generic_64,
++      NULL, fetch_scanline_yv12, fetch_scanline_generic_64,
+       fetch_pixel_yv12, fetch_pixel_generic_64,
+       NULL, NULL },
+     
+     { PIXMAN_null },
+ };
+ 
+ static void
+ setup_accessors (bits_image_t *image)
+ {
+     const format_info_t *info = accessors;
+     
+     while (info->format != PIXMAN_null)
+     {
+ 	if (info->format == image->format)
+ 	{
++	    image->fetch_scanline_16 = info->fetch_scanline_16;
+ 	    image->fetch_scanline_32 = info->fetch_scanline_32;
+ 	    image->fetch_scanline_64 = info->fetch_scanline_64;
+ 	    image->fetch_pixel_32 = info->fetch_pixel_32;
+ 	    image->fetch_pixel_64 = info->fetch_pixel_64;
++	    image->store_scanline_16 = info->store_scanline_16;
+ 	    image->store_scanline_32 = info->store_scanline_32;
+ 	    image->store_scanline_64 = info->store_scanline_64;
+ 	    
+ 	    return;
+ 	}
+ 	
+ 	info++;
+     }
+diff --git a/gfx/cairo/libpixman/src/pixman-bits-image.c b/gfx/cairo/libpixman/src/pixman-bits-image.c
+--- a/gfx/cairo/libpixman/src/pixman-bits-image.c
++++ b/gfx/cairo/libpixman/src/pixman-bits-image.c
+@@ -1247,16 +1247,31 @@ src_get_scanline_wide (pixman_iter_t *it
+ 
+ void
+ _pixman_bits_image_src_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+ {
+     if (iter->flags & ITER_NARROW)
+ 	iter->get_scanline = src_get_scanline_narrow;
+     else
+ 	iter->get_scanline = src_get_scanline_wide;
++
++}
++
++static uint32_t *
++dest_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
++{
++    pixman_image_t *image  = iter->image;
++    int             x      = iter->x;
++    int             y      = iter->y;
++    int             width  = iter->width;
++    uint32_t *	    buffer = iter->buffer;
++
++    image->bits.fetch_scanline_16 (image, x, y, width, buffer, mask);
++
++    return iter->buffer;
+ }
+ 
+ static uint32_t *
+ dest_get_scanline_narrow (pixman_iter_t *iter, const uint32_t *mask)
+ {
+     pixman_image_t *image  = iter->image;
+     int             x      = iter->x;
+     int             y      = iter->y;
+@@ -1327,16 +1342,30 @@ dest_get_scanline_wide (pixman_iter_t *i
+ 	    free (alpha);
+ 	}
+     }
+ 
+     return iter->buffer;
+ }
+ 
+ static void
++dest_write_back_16 (pixman_iter_t *iter)
++{
++    bits_image_t *  image  = &iter->image->bits;
++    int             x      = iter->x;
++    int             y      = iter->y;
++    int             width  = iter->width;
++    const uint32_t *buffer = iter->buffer;
++
++    image->store_scanline_16 (image, x, y, width, buffer);
++
++    iter->y++;
++}
++
++static void
+ dest_write_back_narrow (pixman_iter_t *iter)
+ {
+     bits_image_t *  image  = &iter->image->bits;
+     int             x      = iter->x;
+     int             y      = iter->y;
+     int             width  = iter->width;
+     const uint32_t *buffer = iter->buffer;
+ 
+@@ -1375,28 +1404,41 @@ dest_write_back_wide (pixman_iter_t *ite
+     }
+ 
+     iter->y++;
+ }
+ 
+ void
+ _pixman_bits_image_dest_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+ {
+-    if (iter->flags & ITER_NARROW)
++    if (iter->flags & ITER_16)
++    {
++        if ((iter->flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) ==
++	    (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA))
++	{
++            iter->get_scanline = _pixman_iter_get_scanline_noop;
++        }
++        else
++        {
++	    iter->get_scanline = dest_get_scanline_16;
++        }
++	iter->write_back = dest_write_back_16;
++    }
++    else if (iter->flags & ITER_NARROW)
+     {
+ 	if ((iter->flags & (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA)) ==
+ 	    (ITER_IGNORE_RGB | ITER_IGNORE_ALPHA))
+ 	{
+ 	    iter->get_scanline = _pixman_iter_get_scanline_noop;
+ 	}
+ 	else
+ 	{
+ 	    iter->get_scanline = dest_get_scanline_narrow;
+ 	}
+-	
++
+ 	iter->write_back = dest_write_back_narrow;
+     }
+     else
+     {
+ 	iter->get_scanline = dest_get_scanline_wide;
+ 	iter->write_back = dest_write_back_wide;
+     }
+ }
+diff --git a/gfx/cairo/libpixman/src/pixman-combine16.c b/gfx/cairo/libpixman/src/pixman-combine16.c
+new file mode 100644
+--- /dev/null
++++ b/gfx/cairo/libpixman/src/pixman-combine16.c
+@@ -0,0 +1,124 @@
++#ifdef HAVE_CONFIG_H
++#include <config.h>
++#endif
++
++#include <math.h>
++#include <string.h>
++
++#include "pixman-private.h"
++
++#include "pixman-combine32.h"
++
++static force_inline uint32_t
++combine_mask (const uint32_t src, const uint32_t mask)
++{
++    uint32_t s, m;
++
++    m = mask >> A_SHIFT;
++
++    if (!m)
++	return 0;
++    s = src;
++
++    UN8x4_MUL_UN8 (s, m);
++
++    return s;
++}
++
++static inline uint32_t convert_0565_to_8888(uint16_t color)
++{
++    return CONVERT_0565_TO_8888(color);
++}
++
++static inline uint16_t convert_8888_to_0565(uint32_t color)
++{
++    return CONVERT_8888_TO_0565(color);
++}
++
++static void
++combine_src_u (pixman_implementation_t *imp,
++               pixman_op_t              op,
++               uint32_t *               dest,
++               const uint32_t *         src,
++               const uint32_t *         mask,
++               int                      width)
++{
++    int i;
++
++    if (!mask)
++	memcpy (dest, src, width * sizeof (uint16_t));
++    else
++    {
++	uint16_t *d = (uint16_t*)dest;
++	uint16_t *src16 = (uint16_t*)src;
++	for (i = 0; i < width; ++i)
++	{
++	    if ((*mask & 0xff000000) == 0xff000000) {
++		// it's likely worth special casing
++		// fully opaque because it avoids
++		// the cost of conversion as well the multiplication
++		*(d + i) = *src16;
++	    } else {
++		// the mask is still 32bits
++		uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
++		*(d + i) = convert_8888_to_0565(s);
++	    }
++	    mask++;
++	    src16++;
++	}
++    }
++
++}
++
++static void
++combine_over_u (pixman_implementation_t *imp,
++               pixman_op_t              op,
++               uint32_t *                dest,
++               const uint32_t *          src,
++               const uint32_t *          mask,
++               int                      width)
++{
++    int i;
++
++    if (!mask)
++	memcpy (dest, src, width * sizeof (uint16_t));
++    else
++    {
++	uint16_t *d = (uint16_t*)dest;
++	uint16_t *src16 = (uint16_t*)src;
++	for (i = 0; i < width; ++i)
++	{
++	    if ((*mask & 0xff000000) == 0xff000000) {
++		// it's likely worth special casing
++		// fully opaque because it avoids
++		// the cost of conversion as well the multiplication
++		*(d + i) = *src16;
++	    } else if ((*mask & 0xff000000) == 0x00000000) {
++		// keep the dest the same
++	    } else {
++		// the mask is still 32bits
++		uint32_t s = combine_mask (convert_0565_to_8888(*src16), *mask);
++		uint32_t ia = ALPHA_8 (~s);
++		uint32_t d32 = convert_0565_to_8888(*(d + i));
++		UN8x4_MUL_UN8_ADD_UN8x4 (d32, ia, s);
++		*(d + i) = convert_8888_to_0565(d32);
++	    }
++	    mask++;
++	    src16++;
++	}
++    }
++
++}
++
++
++void
++_pixman_setup_combiner_functions_16 (pixman_implementation_t *imp)
++{
++    int i;
++    for (i = 0; i < PIXMAN_N_OPERATORS; i++) {
++	imp->combine_16[i] = NULL;
++    }
++    imp->combine_16[PIXMAN_OP_SRC] = combine_src_u;
++    imp->combine_16[PIXMAN_OP_OVER] = combine_over_u;
++}
++
+diff --git a/gfx/cairo/libpixman/src/pixman-general.c b/gfx/cairo/libpixman/src/pixman-general.c
+--- a/gfx/cairo/libpixman/src/pixman-general.c
++++ b/gfx/cairo/libpixman/src/pixman-general.c
+@@ -106,46 +106,61 @@ general_composite_rect  (pixman_implemen
+     PIXMAN_COMPOSITE_ARGS (info);
+     uint64_t stack_scanline_buffer[(SCANLINE_BUFFER_LENGTH * 3 + 7) / 8];
+     uint8_t *scanline_buffer = (uint8_t *) stack_scanline_buffer;
+     uint8_t *src_buffer, *mask_buffer, *dest_buffer;
+     pixman_iter_t src_iter, mask_iter, dest_iter;
+     pixman_combine_32_func_t compose;
+     pixman_bool_t component_alpha;
+     iter_flags_t narrow, src_flags;
++    iter_flags_t rgb16;
+     int Bpp;
+     int i;
+ 
+     if ((src_image->common.flags & FAST_PATH_NARROW_FORMAT)		    &&
+ 	(!mask_image || mask_image->common.flags & FAST_PATH_NARROW_FORMAT) &&
+ 	(dest_image->common.flags & FAST_PATH_NARROW_FORMAT))
+     {
+ 	narrow = ITER_NARROW;
+ 	Bpp = 4;
+     }
+     else
+     {
+ 	narrow = 0;
+ 	Bpp = 8;
+     }
+ 
++    // XXX: This special casing is bad. Ideally, we'd keep the general code general perhaps
++    // by having it deal more specifically with different intermediate formats
++    if (
++	(dest_image->common.flags & FAST_PATH_16_FORMAT && (src_image->type == LINEAR || src_image->type == RADIAL)) &&
++	( op == PIXMAN_OP_SRC ||
++         (op == PIXMAN_OP_OVER && (src_image->common.flags & FAST_PATH_IS_OPAQUE))
++	)
++	) {
++	rgb16 = ITER_16;
++    } else {
++	rgb16 = 0;
++    }
++
++
+     if (width * Bpp > SCANLINE_BUFFER_LENGTH)
+     {
+ 	scanline_buffer = pixman_malloc_abc (width, 3, Bpp);
+ 
+ 	if (!scanline_buffer)
+ 	    return;
+     }
+ 
+     src_buffer = scanline_buffer;
+     mask_buffer = src_buffer + width * Bpp;
+     dest_buffer = mask_buffer + width * Bpp;
+ 
+     /* src iter */
+-    src_flags = narrow | op_flags[op].src;
++    src_flags = narrow | op_flags[op].src | rgb16;
+ 
+     _pixman_implementation_src_iter_init (imp->toplevel, &src_iter, src_image,
+ 					  src_x, src_y, width, height,
+ 					  src_buffer, src_flags);
+ 
+     /* mask iter */
+     if ((src_flags & (ITER_IGNORE_ALPHA | ITER_IGNORE_RGB)) ==
+ 	(ITER_IGNORE_ALPHA | ITER_IGNORE_RGB))
+@@ -164,20 +179,20 @@ general_composite_rect  (pixman_implemen
+ 
+     _pixman_implementation_src_iter_init (
+ 	imp->toplevel, &mask_iter, mask_image, mask_x, mask_y, width, height,
+ 	mask_buffer, narrow | (component_alpha? 0 : ITER_IGNORE_RGB));
+ 
+     /* dest iter */
+     _pixman_implementation_dest_iter_init (
+ 	imp->toplevel, &dest_iter, dest_image, dest_x, dest_y, width, height,
+-	dest_buffer, narrow | op_flags[op].dst);
++	dest_buffer, narrow | op_flags[op].dst | rgb16);
+ 
+     compose = _pixman_implementation_lookup_combiner (
+-	imp->toplevel, op, component_alpha, narrow);
++	imp->toplevel, op, component_alpha, narrow, !!rgb16);
+ 
+     if (!compose)
+ 	return;
+ 
+     for (i = 0; i < height; ++i)
+     {
+ 	uint32_t *s, *m, *d;
+ 
+@@ -234,16 +249,17 @@ general_fill (pixman_implementation_t *i
+     return FALSE;
+ }
+ 
+ pixman_implementation_t *
+ _pixman_implementation_create_general (void)
+ {
+     pixman_implementation_t *imp = _pixman_implementation_create (NULL, general_fast_path);
+ 
++    _pixman_setup_combiner_functions_16 (imp);
+     _pixman_setup_combiner_functions_32 (imp);
+     _pixman_setup_combiner_functions_64 (imp);
+ 
+     imp->blt = general_blt;
+     imp->fill = general_fill;
+     imp->src_iter_init = general_src_iter_init;
+     imp->dest_iter_init = general_dest_iter_init;
+ 
+diff --git a/gfx/cairo/libpixman/src/pixman-image.c b/gfx/cairo/libpixman/src/pixman-image.c
+--- a/gfx/cairo/libpixman/src/pixman-image.c
++++ b/gfx/cairo/libpixman/src/pixman-image.c
+@@ -451,16 +451,20 @@ compute_image_info (pixman_image_t *imag
+ 		flags |= FAST_PATH_IS_OPAQUE;
+ 	}
+ 
+ 	if (image->bits.read_func || image->bits.write_func)
+ 	    flags &= ~FAST_PATH_NO_ACCESSORS;
+ 
+ 	if (PIXMAN_FORMAT_IS_WIDE (image->bits.format))
+ 	    flags &= ~FAST_PATH_NARROW_FORMAT;
++
++	if (image->bits.format == PIXMAN_r5g6b5)
++	    flags |= FAST_PATH_16_FORMAT;
++
+ 	break;
+ 
+     case RADIAL:
+ 	code = PIXMAN_unknown;
+ 
+ 	/*
+ 	 * As explained in pixman-radial-gradient.c, every point of
+ 	 * the plane has a valid associated radius (and thus will be
+diff --git a/gfx/cairo/libpixman/src/pixman-implementation.c b/gfx/cairo/libpixman/src/pixman-implementation.c
+--- a/gfx/cairo/libpixman/src/pixman-implementation.c
++++ b/gfx/cairo/libpixman/src/pixman-implementation.c
+@@ -101,45 +101,51 @@ pixman_implementation_t *
+     imp->fill = delegate_fill;
+     imp->src_iter_init = delegate_src_iter_init;
+     imp->dest_iter_init = delegate_dest_iter_init;
+ 
+     imp->fast_paths = fast_paths;
+ 
+     for (i = 0; i < PIXMAN_N_OPERATORS; ++i)
+     {
++	imp->combine_16[i] = NULL;
+ 	imp->combine_32[i] = NULL;
+ 	imp->combine_64[i] = NULL;
+ 	imp->combine_32_ca[i] = NULL;
+ 	imp->combine_64_ca[i] = NULL;
+     }
+ 
+     return imp;
+ }
+ 
+ pixman_combine_32_func_t
+ _pixman_implementation_lookup_combiner (pixman_implementation_t *imp,
+ 					pixman_op_t		 op,
+ 					pixman_bool_t		 component_alpha,
+-					pixman_bool_t		 narrow)
++					pixman_bool_t		 narrow,
++					pixman_bool_t		 rgb16)
+ {
+     pixman_combine_32_func_t f;
+ 
+     do
+     {
+ 	pixman_combine_32_func_t (*combiners[]) =
+ 	{
+ 	    (pixman_combine_32_func_t *)imp->combine_64,
+ 	    (pixman_combine_32_func_t *)imp->combine_64_ca,
+ 	    imp->combine_32,
+ 	    imp->combine_32_ca,
++	    (pixman_combine_32_func_t *)imp->combine_16,
++	    NULL,
+ 	};
+-
+-	f = combiners[component_alpha | (narrow << 1)][op];
+-
++        if (rgb16) {
++            f = combiners[4][op];
++        } else {
++            f = combiners[component_alpha + (narrow << 1)][op];
++        }
+ 	imp = imp->delegate;
+     }
+     while (!f);
+ 
+     return f;
+ }
+ 
+ pixman_bool_t
+diff --git a/gfx/cairo/libpixman/src/pixman-linear-gradient.c b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-linear-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+@@ -217,42 +217,185 @@ linear_get_scanline_narrow (pixman_iter_
+ 	}
+     }
+ 
+     iter->y++;
+ 
+     return iter->buffer;
+ }
+ 
++static uint16_t convert_8888_to_0565(uint32_t color)
++{
++    return CONVERT_8888_TO_0565(color);
++}
++
++static uint32_t *
++linear_get_scanline_16 (pixman_iter_t  *iter,
++			const uint32_t *mask)
++{
++    pixman_image_t *image  = iter->image;
++    int             x      = iter->x;
++    int             y      = iter->y;
++    int             width  = iter->width;
++    uint16_t *      buffer = (uint16_t*)iter->buffer;
++
++    pixman_vector_t v, unit;
++    pixman_fixed_32_32_t l;
++    pixman_fixed_48_16_t dx, dy;
++    gradient_t *gradient = (gradient_t *)image;
++    linear_gradient_t *linear = (linear_gradient_t *)image;
++    uint16_t *end = buffer + width;
++    pixman_gradient_walker_t walker;
++
++    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
++
++    /* reference point is the center of the pixel */
++    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
++    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
++    v.vector[2] = pixman_fixed_1;
++
++    if (image->common.transform)
++    {
++	if (!pixman_transform_point_3d (image->common.transform, &v))
++	    return iter->buffer;
++
++	unit.vector[0] = image->common.transform->matrix[0][0];
++	unit.vector[1] = image->common.transform->matrix[1][0];
++	unit.vector[2] = image->common.transform->matrix[2][0];
++    }
++    else
++    {
++	unit.vector[0] = pixman_fixed_1;
++	unit.vector[1] = 0;
++	unit.vector[2] = 0;
++    }
++
++    dx = linear->p2.x - linear->p1.x;
++    dy = linear->p2.y - linear->p1.y;
++
++    l = dx * dx + dy * dy;
++
++    if (l == 0 || unit.vector[2] == 0)
++    {
++	/* affine transformation only */
++        pixman_fixed_32_32_t t, next_inc;
++	double inc;
++
++	if (l == 0 || v.vector[2] == 0)
++	{
++	    t = 0;
++	    inc = 0;
++	}
++	else
++	{
++	    double invden, v2;
++
++	    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
++		(l * (double) v.vector[2]);
++	    v2 = v.vector[2] * (1. / pixman_fixed_1);
++	    t = ((dx * v.vector[0] + dy * v.vector[1]) - 
++		 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
++	    inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
++	}
++	next_inc = 0;
++
++	if (((pixman_fixed_32_32_t )(inc * width)) == 0)
++	{
++	    register uint16_t color;
++
++	    color = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
++	    while (buffer < end)
++		*buffer++ = color;
++	}
++	else
++	{
++	    int i;
++
++	    i = 0;
++	    while (buffer < end)
++	    {
++		if (!mask || *mask++)
++		{
++		    *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
++										  t + next_inc));
++		}
++		i++;
++		next_inc = inc * i;
++		buffer++;
++	    }
++	}
++    }
++    else
++    {
++	/* projective transformation */
++        double t;
++
++	t = 0;
++
++	while (buffer < end)
++	{
++	    if (!mask || *mask++)
++	    {
++	        if (v.vector[2] != 0)
++		{
++		    double invden, v2;
++
++		    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
++			(l * (double) v.vector[2]);
++		    v2 = v.vector[2] * (1. / pixman_fixed_1);
++		    t = ((dx * v.vector[0] + dy * v.vector[1]) - 
++			 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
++		}
++
++		*buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
++	    }
++
++	    ++buffer;
++
++	    v.vector[0] += unit.vector[0];
++	    v.vector[1] += unit.vector[1];
++	    v.vector[2] += unit.vector[2];
++	}
++    }
++
++    iter->y++;
++
++    return iter->buffer;
++}
++
+ static uint32_t *
+ linear_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+ {
+     uint32_t *buffer = linear_get_scanline_narrow (iter, NULL);
+ 
+     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+ 
+     return buffer;
+ }
+ 
+ void
+ _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
+ {
+     if (linear_gradient_is_horizontal (
+ 	    iter->image, iter->x, iter->y, iter->width, iter->height))
+     {
+-	if (iter->flags & ITER_NARROW)
++	if (iter->flags & ITER_16)
++	    linear_get_scanline_16 (iter, NULL);
++	else if (iter->flags & ITER_NARROW)
+ 	    linear_get_scanline_narrow (iter, NULL);
+ 	else
+ 	    linear_get_scanline_wide (iter, NULL);
+ 
+ 	iter->get_scanline = _pixman_iter_get_scanline_noop;
+     }
+     else
+     {
+-	if (iter->flags & ITER_NARROW)
++	if (iter->flags & ITER_16)
++	    iter->get_scanline = linear_get_scanline_16;
++	else if (iter->flags & ITER_NARROW)
+ 	    iter->get_scanline = linear_get_scanline_narrow;
+ 	else
+ 	    iter->get_scanline = linear_get_scanline_wide;
+     }
+ }
+ 
+ PIXMAN_EXPORT pixman_image_t *
+ pixman_image_create_linear_gradient (pixman_point_fixed_t *        p1,
+diff --git a/gfx/cairo/libpixman/src/pixman-private.h b/gfx/cairo/libpixman/src/pixman-private.h
+--- a/gfx/cairo/libpixman/src/pixman-private.h
++++ b/gfx/cairo/libpixman/src/pixman-private.h
+@@ -152,24 +152,28 @@ struct bits_image
+     int                        height;
+     uint32_t *                 bits;
+     uint32_t *                 free_me;
+     int                        rowstride;  /* in number of uint32_t's */
+ 
+     fetch_scanline_t           get_scanline_32;
+     fetch_scanline_t           get_scanline_64;
+ 
++    fetch_scanline_t           fetch_scanline_16;
++
+     fetch_scanline_t           fetch_scanline_32;
+     fetch_pixel_32_t	       fetch_pixel_32;
+     store_scanline_t           store_scanline_32;
+ 
+     fetch_scanline_t           fetch_scanline_64;
+     fetch_pixel_64_t	       fetch_pixel_64;
+     store_scanline_t           store_scanline_64;
+ 
++    store_scanline_t           store_scanline_16;
++
+     /* Used for indirect access to the bits */
+     pixman_read_memory_func_t  read_func;
+     pixman_write_memory_func_t write_func;
+ };
+ 
+ union pixman_image
+ {
+     image_type_t       type;
+@@ -202,17 +206,24 @@ typedef enum
+      * destination.
+      *
+      * When he destination is xRGB, this is useful knowledge, because then
+      * we can treat it as if it were ARGB, which means in some cases we can
+      * avoid copying it to a temporary buffer.
+      */
+     ITER_LOCALIZED_ALPHA =	(1 << 1),
+     ITER_IGNORE_ALPHA =		(1 << 2),
+-    ITER_IGNORE_RGB =		(1 << 3)
++    ITER_IGNORE_RGB =		(1 << 3),
++
++    /* With the addition of ITER_16 we now have two flags that to represent
++     * 3 pipelines. This means that there can be an invalid state when
++     * both ITER_NARROW and ITER_16 are set. In this case
++     * ITER_16 overrides NARROW and we should use the 16 bit pipeline.
++     * Note: ITER_16 still has a 32 bit mask, which is a bit weird. */
++    ITER_16 =			(1 << 4)
+ } iter_flags_t;
+ 
+ struct pixman_iter_t
+ {
+     /* These are initialized by _pixman_implementation_{src,dest}_init */
+     pixman_image_t *		image;
+     uint32_t *			buffer;
+     int				x, y;
+@@ -429,16 +440,17 @@ typedef pixman_bool_t (*pixman_fill_func
+ 					     int                      x,
+ 					     int                      y,
+ 					     int                      width,
+ 					     int                      height,
+ 					     uint32_t                 xor);
+ typedef void (*pixman_iter_init_func_t) (pixman_implementation_t *imp,
+                                          pixman_iter_t           *iter);
+ 
++void _pixman_setup_combiner_functions_16 (pixman_implementation_t *imp);
+ void _pixman_setup_combiner_functions_32 (pixman_implementation_t *imp);
+ void _pixman_setup_combiner_functions_64 (pixman_implementation_t *imp);
+ 
+ typedef struct
+ {
+     pixman_op_t             op;
+     pixman_format_code_t    src_format;
+     uint32_t		    src_flags;
+@@ -459,32 +471,34 @@ struct pixman_implementation_t
+     pixman_fill_func_t		fill;
+     pixman_iter_init_func_t     src_iter_init;
+     pixman_iter_init_func_t     dest_iter_init;
+ 
+     pixman_combine_32_func_t	combine_32[PIXMAN_N_OPERATORS];
+     pixman_combine_32_func_t	combine_32_ca[PIXMAN_N_OPERATORS];
+     pixman_combine_64_func_t	combine_64[PIXMAN_N_OPERATORS];
+     pixman_combine_64_func_t	combine_64_ca[PIXMAN_N_OPERATORS];
++    pixman_combine_64_func_t	combine_16[PIXMAN_N_OPERATORS];
+ };
+ 
+ uint32_t
+ _pixman_image_get_solid (pixman_implementation_t *imp,
+ 			 pixman_image_t *         image,
+                          pixman_format_code_t     format);
+ 
+ pixman_implementation_t *
+ _pixman_implementation_create (pixman_implementation_t *delegate,
+ 			       const pixman_fast_path_t *fast_paths);
+ 
+ pixman_combine_32_func_t
+ _pixman_implementation_lookup_combiner (pixman_implementation_t *imp,
+ 					pixman_op_t		 op,
+ 					pixman_bool_t		 component_alpha,
+-					pixman_bool_t		 wide);
++					pixman_bool_t		 wide,
++					pixman_bool_t		 rgb16);
+ 
+ pixman_bool_t
+ _pixman_implementation_blt (pixman_implementation_t *imp,
+                             uint32_t *               src_bits,
+                             uint32_t *               dst_bits,
+                             int                      src_stride,
+                             int                      dst_stride,
+                             int                      src_bpp,
+@@ -613,16 +627,17 @@ uint32_t *
+ #define FAST_PATH_Y_UNIT_ZERO			(1 << 18)
+ #define FAST_PATH_BILINEAR_FILTER		(1 << 19)
+ #define FAST_PATH_ROTATE_90_TRANSFORM		(1 << 20)
+ #define FAST_PATH_ROTATE_180_TRANSFORM		(1 << 21)
+ #define FAST_PATH_ROTATE_270_TRANSFORM		(1 << 22)
+ #define FAST_PATH_SAMPLES_COVER_CLIP_NEAREST	(1 << 23)
+ #define FAST_PATH_SAMPLES_COVER_CLIP_BILINEAR	(1 << 24)
+ #define FAST_PATH_BITS_IMAGE			(1 << 25)
++#define FAST_PATH_16_FORMAT			(1 << 26)
+ 
+ #define FAST_PATH_PAD_REPEAT						\
+     (FAST_PATH_NO_NONE_REPEAT		|				\
+      FAST_PATH_NO_NORMAL_REPEAT		|				\
+      FAST_PATH_NO_REFLECT_REPEAT)
+ 
+ #define FAST_PATH_NORMAL_REPEAT						\
+     (FAST_PATH_NO_NONE_REPEAT		|				\
+diff --git a/gfx/cairo/libpixman/src/pixman-radial-gradient.c b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-radial-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+@@ -395,35 +395,289 @@ radial_get_scanline_narrow (pixman_iter_
+ 	    v.vector[2] += unit.vector[2];
+ 	}
+     }
+ 
+     iter->y++;
+     return iter->buffer;
+ }
+ 
++static uint16_t convert_8888_to_0565(uint32_t color)
++{
++    return CONVERT_8888_TO_0565(color);
++}
++
++static uint32_t *
++radial_get_scanline_16 (pixman_iter_t *iter, const uint32_t *mask)
++{
++    /*
++     * Implementation of radial gradients following the PDF specification.
++     * See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
++     * Manual (PDF 32000-1:2008 at the time of this writing).
++     *
++     * In the radial gradient problem we are given two circles (c₁,r₁) and
++     * (c₂,r₂) that define the gradient itself.
++     *
++     * Mathematically the gradient can be defined as the family of circles
++     *
++     *     ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
++     *
++     * excluding those circles whose radius would be < 0. When a point
++     * belongs to more than one circle, the one with a bigger t is the only
++     * one that contributes to its color. When a point does not belong
++     * to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
++     * Further limitations on the range of values for t are imposed when
++     * the gradient is not repeated, namely t must belong to [0,1].
++     *
++     * The graphical result is the same as drawing the valid (radius > 0)
++     * circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient
++     * is not repeated) using SOURCE operator composition.
++     *
++     * It looks like a cone pointing towards the viewer if the ending circle
++     * is smaller than the starting one, a cone pointing inside the page if
++     * the starting circle is the smaller one and like a cylinder if they
++     * have the same radius.
++     *
++     * What we actually do is, given the point whose color we are interested
++     * in, compute the t values for that point, solving for t in:
++     *
++     *     length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
++     *
++     * Let's rewrite it in a simpler way, by defining some auxiliary
++     * variables:
++     *
++     *     cd = c₂ - c₁
++     *     pd = p - c₁
++     *     dr = r₂ - r₁
++     *     length(t·cd - pd) = r₁ + t·dr
++     *
++     * which actually means
++     *
++     *     hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
++     *
++     * or
++     *
++     *     ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
++     *
++     * If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes:
++     *
++     *     (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
++     *
++     * where we can actually expand the squares and solve for t:
++     *
++     *     t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
++     *       = r₁² + 2·r₁·t·dr + t²·dr²
++     *
++     *     (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
++     *         (pdx² + pdy² - r₁²) = 0
++     *
++     *     A = cdx² + cdy² - dr²
++     *     B = pdx·cdx + pdy·cdy + r₁·dr
++     *     C = pdx² + pdy² - r₁²
++     *     At² - 2Bt + C = 0
++     *
++     * The solutions (unless the equation degenerates because of A = 0) are:
++     *
++     *     t = (B ± ⎷(B² - A·C)) / A
++     *
++     * The solution we are going to prefer is the bigger one, unless the
++     * radius associated to it is negative (or it falls outside the valid t
++     * range).
++     *
++     * Additional observations (useful for optimizations):
++     * A does not depend on p
++     *
++     * A < 0 <=> one of the two circles completely contains the other one
++     *   <=> for every p, the radiuses associated with the two t solutions
++     *       have opposite sign
++     */
++    pixman_image_t *image = iter->image;
++    int x = iter->x;
++    int y = iter->y;
++    int width = iter->width;
++    uint16_t *buffer = iter->buffer;
++
++    gradient_t *gradient = (gradient_t *)image;
++    radial_gradient_t *radial = (radial_gradient_t *)image;
++    uint16_t *end = buffer + width;
++    pixman_gradient_walker_t walker;
++    pixman_vector_t v, unit;
++
++    /* reference point is the center of the pixel */
++    v.vector[0] = pixman_int_to_fixed (x) + pixman_fixed_1 / 2;
++    v.vector[1] = pixman_int_to_fixed (y) + pixman_fixed_1 / 2;
++    v.vector[2] = pixman_fixed_1;
++
++    _pixman_gradient_walker_init (&walker, gradient, image->common.repeat);
++
++    if (image->common.transform)
++    {
++	if (!pixman_transform_point_3d (image->common.transform, &v))
++	    return iter->buffer;
++
++	unit.vector[0] = image->common.transform->matrix[0][0];
++	unit.vector[1] = image->common.transform->matrix[1][0];
++	unit.vector[2] = image->common.transform->matrix[2][0];
++    }
++    else
++    {
++	unit.vector[0] = pixman_fixed_1;
++	unit.vector[1] = 0;
++	unit.vector[2] = 0;
++    }
++
++    if (unit.vector[2] == 0 && v.vector[2] == pixman_fixed_1)
++    {
++	/*
++	 * Given:
++	 *
++	 * t = (B ± ⎷(B² - A·C)) / A
++	 *
++	 * where
++	 *
++	 * A = cdx² + cdy² - dr²
++	 * B = pdx·cdx + pdy·cdy + r₁·dr
++	 * C = pdx² + pdy² - r₁²
++	 * det = B² - A·C
++	 *
++	 * Since we have an affine transformation, we know that (pdx, pdy)
++	 * increase linearly with each pixel,
++	 *
++	 * pdx = pdx₀ + n·ux,
++	 * pdy = pdy₀ + n·uy,
++	 *
++	 * we can then express B, C and det through multiple differentiation.
++	 */
++	pixman_fixed_32_32_t b, db, c, dc, ddc;
++
++	/* warning: this computation may overflow */
++	v.vector[0] -= radial->c1.x;
++	v.vector[1] -= radial->c1.y;
++
++	/*
++	 * B and C are computed and updated exactly.
++	 * If fdot was used instead of dot, in the worst case it would
++	 * lose 11 bits of precision in each of the multiplication and
++	 * summing up would zero out all the bit that were preserved,
++	 * thus making the result 0 instead of the correct one.
++	 * This would mean a worst case of unbound relative error or
++	 * about 2^10 absolute error
++	 */
++	b = dot (v.vector[0], v.vector[1], radial->c1.radius,
++		 radial->delta.x, radial->delta.y, radial->delta.radius);
++	db = dot (unit.vector[0], unit.vector[1], 0,
++		  radial->delta.x, radial->delta.y, 0);
++
++	c = dot (v.vector[0], v.vector[1],
++		 -((pixman_fixed_48_16_t) radial->c1.radius),
++		 v.vector[0], v.vector[1], radial->c1.radius);
++	dc = dot (2 * (pixman_fixed_48_16_t) v.vector[0] + unit.vector[0],
++		  2 * (pixman_fixed_48_16_t) v.vector[1] + unit.vector[1],
++		  0,
++		  unit.vector[0], unit.vector[1], 0);
++	ddc = 2 * dot (unit.vector[0], unit.vector[1], 0,
++		       unit.vector[0], unit.vector[1], 0);
++
++	while (buffer < end)
++	{
++	    if (!mask || *mask++)
++	    {
++		*buffer = convert_8888_to_0565(
++			  radial_compute_color (radial->a, b, c,
++						radial->inva,
++						radial->delta.radius,
++						radial->mindr,
++						&walker,
++						image->common.repeat));
++	    }
++
++	    b += db;
++	    c += dc;
++	    dc += ddc;
++	    ++buffer;
++	}
++    }
++    else
++    {
++	/* projective */
++	/* Warning:
++	 * error propagation guarantees are much looser than in the affine case
++	 */
++	while (buffer < end)
++	{
++	    if (!mask || *mask++)
++	    {
++		if (v.vector[2] != 0)
++		{
++		    double pdx, pdy, invv2, b, c;
++
++		    invv2 = 1. * pixman_fixed_1 / v.vector[2];
++
++		    pdx = v.vector[0] * invv2 - radial->c1.x;
++		    /*    / pixman_fixed_1 */
++
++		    pdy = v.vector[1] * invv2 - radial->c1.y;
++		    /*    / pixman_fixed_1 */
++
++		    b = fdot (pdx, pdy, radial->c1.radius,
++			      radial->delta.x, radial->delta.y,
++			      radial->delta.radius);
++		    /*  / pixman_fixed_1 / pixman_fixed_1 */
++
++		    c = fdot (pdx, pdy, -radial->c1.radius,
++			      pdx, pdy, radial->c1.radius);
++		    /*  / pixman_fixed_1 / pixman_fixed_1 */
++
++		    *buffer = convert_8888_to_0565 (
++			      radial_compute_color (radial->a, b, c,
++						    radial->inva,
++						    radial->delta.radius,
++						    radial->mindr,
++						    &walker,
++						    image->common.repeat));
++		}
++		else
++		{
++		    *buffer = 0;
++		}
++	    }
++
++	    ++buffer;
++
++	    v.vector[0] += unit.vector[0];
++	    v.vector[1] += unit.vector[1];
++	    v.vector[2] += unit.vector[2];
++	}
++    }
++
++    iter->y++;
++    return iter->buffer;
++}
+ static uint32_t *
+ radial_get_scanline_wide (pixman_iter_t *iter, const uint32_t *mask)
+ {
+     uint32_t *buffer = radial_get_scanline_narrow (iter, NULL);
+ 
+     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+ 
+     return buffer;
+ }
+ 
+ void
+ _pixman_radial_gradient_iter_init (pixman_image_t *image, pixman_iter_t *iter)
+ {
+-    if (iter->flags & ITER_NARROW)
++    if (iter->flags & ITER_16)
++	iter->get_scanline = radial_get_scanline_16;
++    else if (iter->flags & ITER_NARROW)
+ 	iter->get_scanline = radial_get_scanline_narrow;
+     else
+ 	iter->get_scanline = radial_get_scanline_wide;
+ }
+ 
++
+ PIXMAN_EXPORT pixman_image_t *
+ pixman_image_create_radial_gradient (pixman_point_fixed_t *        inner,
+                                      pixman_point_fixed_t *        outer,
+                                      pixman_fixed_t                inner_radius,
+                                      pixman_fixed_t                outer_radius,
+                                      const pixman_gradient_stop_t *stops,
+                                      int                           n_stops)
+ {
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/pixman-dither.patch
@@ -0,0 +1,310 @@
+diff --git a/gfx/cairo/libpixman/src/pixman-dither.h b/gfx/cairo/libpixman/src/pixman-dither.h
+new file mode 100644
+--- /dev/null
++++ b/gfx/cairo/libpixman/src/pixman-dither.h
+@@ -0,0 +1,51 @@
++#define R16_BITS     5
++#define G16_BITS     6
++#define B16_BITS     5
++
++#define R16_SHIFT    (B16_BITS + G16_BITS)
++#define G16_SHIFT    (B16_BITS)
++#define B16_SHIFT    0
++
++#define MASK 0xff
++#define ONE_HALF 0x80
++
++#define A_SHIFT 8 * 3
++#define R_SHIFT 8 * 2
++#define G_SHIFT 8
++#define A_MASK 0xff000000
++#define R_MASK 0xff0000
++#define G_MASK 0xff00
++
++#define RB_MASK 0xff00ff
++#define AG_MASK 0xff00ff00
++#define RB_ONE_HALF 0x800080
++#define RB_MASK_PLUS_ONE 0x10000100
++
++#define ALPHA_8(x) ((x) >> A_SHIFT)
++#define RED_8(x) (((x) >> R_SHIFT) & MASK)
++#define GREEN_8(x) (((x) >> G_SHIFT) & MASK)
++#define BLUE_8(x) ((x) & MASK)
++
++// This uses the same dithering technique that Skia does.
++// It is essentially preturbing the lower bit based on the
++// high bit
++static inline uint16_t dither_32_to_16(uint32_t c)
++{
++    uint8_t b = BLUE_8(c);
++    uint8_t g = GREEN_8(c);
++    uint8_t r = RED_8(c);
++    r = ((r << 1) - ((r >> (8 - R16_BITS) << (8 - R16_BITS)) | (r >> R16_BITS))) >> (8 - R16_BITS);
++    g = ((g << 1) - ((g >> (8 - G16_BITS) << (8 - G16_BITS)) | (g >> G16_BITS))) >> (8 - G16_BITS);
++    b = ((b << 1) - ((b >> (8 - B16_BITS) << (8 - B16_BITS)) | (b >> B16_BITS))) >> (8 - B16_BITS);
++    return ((r << R16_SHIFT) | (g << G16_SHIFT) | (b << B16_SHIFT));
++}
++
++static inline uint16_t dither_8888_to_0565(uint32_t color, pixman_bool_t toggle)
++{
++    // alternate between a preturbed truncation and a regular truncation
++    if (toggle) {
++	return dither_32_to_16(color);
++    } else {
++	return CONVERT_8888_TO_0565(color);
++    }
++}
+diff --git a/gfx/cairo/libpixman/src/pixman-linear-gradient.c b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-linear-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-linear-gradient.c
+@@ -26,16 +26,18 @@
+  */
+ 
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+ #include <stdlib.h>
+ #include "pixman-private.h"
+ 
++#include "pixman-dither.h"
++
+ static pixman_bool_t
+ linear_gradient_is_horizontal (pixman_image_t *image,
+ 			       int             x,
+ 			       int             y,
+ 			       int             width,
+ 			       int             height)
+ {
+     linear_gradient_t *linear = (linear_gradient_t *)image;
+@@ -222,25 +224,28 @@ linear_get_scanline_narrow (pixman_iter_
+     return iter->buffer;
+ }
+ 
+ static uint16_t convert_8888_to_0565(uint32_t color)
+ {
+     return CONVERT_8888_TO_0565(color);
+ }
+ 
++
++
+ static uint32_t *
+ linear_get_scanline_16 (pixman_iter_t  *iter,
+ 			const uint32_t *mask)
+ {
+     pixman_image_t *image  = iter->image;
+     int             x      = iter->x;
+     int             y      = iter->y;
+     int             width  = iter->width;
+     uint16_t *      buffer = (uint16_t*)iter->buffer;
++    pixman_bool_t   toggle = ((x ^ y) & 1);
+ 
+     pixman_vector_t v, unit;
+     pixman_fixed_32_32_t l;
+     pixman_fixed_48_16_t dx, dy;
+     gradient_t *gradient = (gradient_t *)image;
+     linear_gradient_t *linear = (linear_gradient_t *)image;
+     uint16_t *end = buffer + width;
+     pixman_gradient_walker_t walker;
+@@ -294,34 +299,47 @@ linear_get_scanline_16 (pixman_iter_t  *
+ 	    t = ((dx * v.vector[0] + dy * v.vector[1]) - 
+ 		 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+ 	    inc = (dx * unit.vector[0] + dy * unit.vector[1]) * invden;
+ 	}
+ 	next_inc = 0;
+ 
+ 	if (((pixman_fixed_32_32_t )(inc * width)) == 0)
+ 	{
+-	    register uint16_t color;
++	    register uint32_t color;
++	    uint16_t dither_diff;
++	    uint16_t color16;
++	    uint16_t color16b;
+ 
+-	    color = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
+-	    while (buffer < end)
+-		*buffer++ = color;
++	    color = _pixman_gradient_walker_pixel (&walker, t);
++	    color16 = dither_8888_to_0565(color, toggle);
++	    color16b = dither_8888_to_0565(color, toggle^1);
++	    // compute the difference
++	    dither_diff =  color16 ^ color16b;
++	    while (buffer < end) {
++		*buffer++ = color16;
++		// use dither_diff to toggle between color16 and color16b
++		color16 ^= dither_diff;
++		toggle ^= 1;
++	    }
+ 	}
+ 	else
+ 	{
+ 	    int i;
+ 
+ 	    i = 0;
+ 	    while (buffer < end)
+ 	    {
+ 		if (!mask || *mask++)
+ 		{
+-		    *buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
+-										  t + next_inc));
++		    *buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker,
++										 t + next_inc),
++						  toggle);
+ 		}
++		toggle ^= 1;
+ 		i++;
+ 		next_inc = inc * i;
+ 		buffer++;
+ 	    }
+ 	}
+     }
+     else
+     {
+@@ -340,18 +358,20 @@ linear_get_scanline_16 (pixman_iter_t  *
+ 
+ 		    invden = pixman_fixed_1 * (double) pixman_fixed_1 /
+ 			(l * (double) v.vector[2]);
+ 		    v2 = v.vector[2] * (1. / pixman_fixed_1);
+ 		    t = ((dx * v.vector[0] + dy * v.vector[1]) - 
+ 			 (dx * linear->p1.x + dy * linear->p1.y) * v2) * invden;
+ 		}
+ 
+-		*buffer = convert_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t));
++		*buffer = dither_8888_to_0565(_pixman_gradient_walker_pixel (&walker, t),
++					      toggle);
+ 	    }
++	    toggle ^= 1;
+ 
+ 	    ++buffer;
+ 
+ 	    v.vector[0] += unit.vector[0];
+ 	    v.vector[1] += unit.vector[1];
+ 	    v.vector[2] += unit.vector[2];
+ 	}
+     }
+@@ -369,17 +389,18 @@ linear_get_scanline_wide (pixman_iter_t 
+     pixman_expand ((uint64_t *)buffer, buffer, PIXMAN_a8r8g8b8, iter->width);
+ 
+     return buffer;
+ }
+ 
+ void
+ _pixman_linear_gradient_iter_init (pixman_image_t *image, pixman_iter_t  *iter)
+ {
+-    if (linear_gradient_is_horizontal (
++    // XXX: we can't use this optimization when dithering
++    if (0 && linear_gradient_is_horizontal (
+ 	    iter->image, iter->x, iter->y, iter->width, iter->height))
+     {
+ 	if (iter->flags & ITER_16)
+ 	    linear_get_scanline_16 (iter, NULL);
+ 	else if (iter->flags & ITER_NARROW)
+ 	    linear_get_scanline_narrow (iter, NULL);
+ 	else
+ 	    linear_get_scanline_wide (iter, NULL);
+diff --git a/gfx/cairo/libpixman/src/pixman-radial-gradient.c b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+--- a/gfx/cairo/libpixman/src/pixman-radial-gradient.c
++++ b/gfx/cairo/libpixman/src/pixman-radial-gradient.c
+@@ -29,16 +29,18 @@
+ 
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+ #endif
+ #include <stdlib.h>
+ #include <math.h>
+ #include "pixman-private.h"
+ 
++#include "pixman-dither.h"
++
+ static inline pixman_fixed_32_32_t
+ dot (pixman_fixed_48_16_t x1,
+      pixman_fixed_48_16_t y1,
+      pixman_fixed_48_16_t z1,
+      pixman_fixed_48_16_t x2,
+      pixman_fixed_48_16_t y2,
+      pixman_fixed_48_16_t z2)
+ {
+@@ -489,16 +491,17 @@ radial_get_scanline_16 (pixman_iter_t *i
+      *   <=> for every p, the radiuses associated with the two t solutions
+      *       have opposite sign
+      */
+     pixman_image_t *image = iter->image;
+     int x = iter->x;
+     int y = iter->y;
+     int width = iter->width;
+     uint16_t *buffer = iter->buffer;
++    pixman_bool_t toggle = ((x ^ y) & 1);
+ 
+     gradient_t *gradient = (gradient_t *)image;
+     radial_gradient_t *radial = (radial_gradient_t *)image;
+     uint16_t *end = buffer + width;
+     pixman_gradient_walker_t walker;
+     pixman_vector_t v, unit;
+ 
+     /* reference point is the center of the pixel */
+@@ -575,25 +578,27 @@ radial_get_scanline_16 (pixman_iter_t *i
+ 		  unit.vector[0], unit.vector[1], 0);
+ 	ddc = 2 * dot (unit.vector[0], unit.vector[1], 0,
+ 		       unit.vector[0], unit.vector[1], 0);
+ 
+ 	while (buffer < end)
+ 	{
+ 	    if (!mask || *mask++)
+ 	    {
+-		*buffer = convert_8888_to_0565(
++		*buffer = dither_8888_to_0565(
+ 			  radial_compute_color (radial->a, b, c,
+ 						radial->inva,
+ 						radial->delta.radius,
+ 						radial->mindr,
+ 						&walker,
+-						image->common.repeat));
++						image->common.repeat),
++			  toggle);
+ 	    }
+ 
++	    toggle ^= 1;
+ 	    b += db;
+ 	    c += dc;
+ 	    dc += ddc;
+ 	    ++buffer;
+ 	}
+     }
+     else
+     {
+@@ -621,31 +626,33 @@ radial_get_scanline_16 (pixman_iter_t *i
+ 			      radial->delta.x, radial->delta.y,
+ 			      radial->delta.radius);
+ 		    /*  / pixman_fixed_1 / pixman_fixed_1 */
+ 
+ 		    c = fdot (pdx, pdy, -radial->c1.radius,
+ 			      pdx, pdy, radial->c1.radius);
+ 		    /*  / pixman_fixed_1 / pixman_fixed_1 */
+ 
+-		    *buffer = convert_8888_to_0565 (
++		    *buffer = dither_8888_to_0565 (
+ 			      radial_compute_color (radial->a, b, c,
+ 						    radial->inva,
+ 						    radial->delta.radius,
+ 						    radial->mindr,
+ 						    &walker,
+-						    image->common.repeat));
++						    image->common.repeat),
++			      toggle);
+ 		}
+ 		else
+ 		{
+ 		    *buffer = 0;
+ 		}
+ 	    }
+ 
+ 	    ++buffer;
++	    toggle ^= 1;
+ 
+ 	    v.vector[0] += unit.vector[0];
+ 	    v.vector[1] += unit.vector[1];
+ 	    v.vector[2] += unit.vector[2];
+ 	}
+     }
+ 
+     iter->y++;
+
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -4980,17 +4980,17 @@ mjit::Compiler::jsop_getprop(PropertyNam
      * wants to read it, and the shapeReg because it could cause a spill that
      * the string path wouldn't sink back.
      */
     RegisterID objReg = frame.copyDataIntoReg(top);
     RegisterID shapeReg = frame.allocReg();
 
     RESERVE_IC_SPACE(masm);
 
-    PICGenInfo pic(ic::PICInfo::GET, JSOp(*PC));
+    PICGenInfo pic(ic::PICInfo::GET, PC);
 
     /*
      * If this access has been on a shape with a getter hook, make preparations
      * so that we can generate a stub to call the hook directly (rather than be
      * forced to make a stub call). Sync the stack up front and kill all
      * registers so that PIC stubs can contain calls, and always generate a
      * type barrier if inference is enabled (known property types do not
      * reflect properties with getter hooks).
@@ -5407,29 +5407,27 @@ mjit::Compiler::jsop_setprop(PropertyNam
                 bumpPropCount(PC, PCCounts::PROP_DEFINITE);
             return true;
         }
     }
 
     if (script->hasScriptCounts)
         bumpPropCount(PC, PCCounts::PROP_OTHER);
 
-    JSOp op = JSOp(*PC);
-
 #ifdef JSGC_INCREMENTAL_MJ
     /* Write barrier. We don't have type information for JSOP_SETNAME. */
     if (cx->compartment->needsBarrier() &&
-        (!types || op == JSOP_SETNAME || types->propertyNeedsBarrier(cx, id)))
+        (!types || JSOp(*PC) == JSOP_SETNAME || types->propertyNeedsBarrier(cx, id)))
     {
         jsop_setprop_slow(name);
         return true;
     }
 #endif
 
-    PICGenInfo pic(ic::PICInfo::SET, op);
+    PICGenInfo pic(ic::PICInfo::SET, PC);
     pic.name = name;
 
     if (monitored(PC)) {
         pic.typeMonitored = true;
         types::TypeSet *types = frame.extra(rhs).types;
         if (!types) {
             /* Handle FORNAME and other compound opcodes. Yuck. */
             types = types::TypeSet::make(cx, "unknownRHS");
@@ -5536,17 +5534,17 @@ mjit::Compiler::jsop_setprop(PropertyNam
 
     pics.append(pic);
     return true;
 }
 
 void
 mjit::Compiler::jsop_name(PropertyName *name, JSValueType type)
 {
-    PICGenInfo pic(ic::PICInfo::NAME, JSOp(*PC));
+    PICGenInfo pic(ic::PICInfo::NAME, PC);
 
     RESERVE_IC_SPACE(masm);
 
     pic.shapeReg = frame.allocReg();
     pic.objReg = frame.allocReg();
     pic.typeReg = Registers::ReturnReg;
     pic.name = name;
     pic.hasTypeCheck = false;
@@ -5592,17 +5590,17 @@ mjit::Compiler::jsop_name(PropertyName *
     pics.append(pic);
 
     finishBarrier(barrier, REJOIN_GETTER, 0);
 }
 
 bool
 mjit::Compiler::jsop_xname(PropertyName *name)
 {
-    PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC));
+    PICGenInfo pic(ic::PICInfo::XNAME, PC);
 
     FrameEntry *fe = frame.peek(-1);
     if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
         return jsop_getprop(name, knownPushedType(0));
     }
 
     if (!fe->isTypeKnown()) {
         Jump notObject = frame.testObject(Assembler::NotEqual, fe);
@@ -5654,17 +5652,17 @@ mjit::Compiler::jsop_xname(PropertyName 
 
     finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
     return true;
 }
 
 void
 mjit::Compiler::jsop_bindname(PropertyName *name)
 {
-    PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC));
+    PICGenInfo pic(ic::PICInfo::BIND, PC);
 
     // This code does not check the frame flags to see if scopeChain has been
     // set. Rather, it relies on the up-front analysis statically determining
     // whether BINDNAME can be used, which reifies the scope chain at the
     // prologue.
     JS_ASSERT(analysis->usesScopeChain());
 
     pic.shapeReg = frame.allocReg();
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -150,65 +150,58 @@ class Compiler : public BaseCompiler
         DataLabelPtr fastNcodePatch;
         DataLabelPtr slowNcodePatch;
         bool hasFastNcode;
         bool hasSlowNcode;
         bool joinSlow;
     };
 
     struct BaseICInfo {
-        BaseICInfo(JSOp op) : op(op), canCallHook(false), forcedTypeBarrier(false)
+        BaseICInfo() : canCallHook(false), forcedTypeBarrier(false)
         { }
         Label fastPathStart;
         Label fastPathRejoin;
         Label slowPathStart;
         Call slowPathCall;
         DataLabelPtr paramAddr;
-        JSOp op;
         bool canCallHook;
         bool forcedTypeBarrier;
 
         void copyTo(ic::BaseIC &to, JSC::LinkBuffer &full, JSC::LinkBuffer &stub) {
             to.fastPathStart = full.locationOf(fastPathStart);
             to.fastPathRejoin = full.locationOf(fastPathRejoin);
             to.slowPathStart = stub.locationOf(slowPathStart);
             to.slowPathCall = stub.locationOf(slowPathCall);
             to.canCallHook = canCallHook;
             to.forcedTypeBarrier = forcedTypeBarrier;
-            to.op = op;
-            JS_ASSERT(to.op == op);
         }
     };
 
     struct GetElementICInfo : public BaseICInfo {
-        GetElementICInfo(JSOp op) : BaseICInfo(op)
-        { }
         RegisterID  typeReg;
         RegisterID  objReg;
         ValueRemat  id;
         MaybeJump   typeGuard;
         Jump        shapeGuard;
     };
 
     struct SetElementICInfo : public BaseICInfo {
-        SetElementICInfo(JSOp op) : BaseICInfo(op)
-        { }
         RegisterID  objReg;
         StateRemat  objRemat;
         ValueRemat  vr;
         Jump        capacityGuard;
         Jump        shapeGuard;
         Jump        holeGuard;
         Int32Key    key;
         uint32_t    volatileMask;
     };
 
     struct PICGenInfo : public BaseICInfo {
-        PICGenInfo(ic::PICInfo::Kind kind, JSOp op)
-          : BaseICInfo(op), kind(kind), typeMonitored(false)
+        PICGenInfo(ic::PICInfo::Kind kind, jsbytecode *pc)
+          : kind(kind), pc(pc), typeMonitored(false)
         { }
         ic::PICInfo::Kind kind;
         Label typeCheck;
         RegisterID shapeReg;
         RegisterID objReg;
         RegisterID typeReg;
         Label shapeGuard;
         jsbytecode *pc;
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -1570,17 +1570,17 @@ mjit::Compiler::jsop_setelem(bool popGua
 #ifdef JSGC_INCREMENTAL_MJ
     // Write barrier.
     if (cx->compartment->needsBarrier()) {
         jsop_setelem_slow();
         return true;
     }
 #endif
 
-    SetElementICInfo ic = SetElementICInfo(JSOp(*PC));
+    SetElementICInfo ic;
 
     // One by one, check if the most important stack entries have registers,
     // and if so, pin them. This is to avoid spilling and reloading from the
     // stack as we incrementally allocate other registers.
     MaybeRegisterID pinnedValueType = frame.maybePinType(value);
     MaybeRegisterID pinnedValueData = frame.maybePinData(value);
 
     // Pin |obj| if it doesn't share a backing with |value|.
@@ -2170,17 +2170,17 @@ mjit::Compiler::jsop_getelem()
 
     frame.forgetMismatchedObject(obj);
 
     if (id->isType(JSVAL_TYPE_DOUBLE) || !globalObj) {
         jsop_getelem_slow();
         return true;
     }
 
-    GetElementICInfo ic = GetElementICInfo(JSOp(*PC));
+    GetElementICInfo ic;
 
     // Pin the top of the stack to avoid spills, before allocating registers.
     MaybeRegisterID pinnedIdData = frame.maybePinData(id);
     MaybeRegisterID pinnedIdType = frame.maybePinType(id);
 
     MaybeJump objTypeGuard;
     if (!obj->isTypeKnown()) {
         // Test the type of the object without spilling the payload.
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1911,17 +1911,17 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
     }
 
     RecompilationMonitor monitor(f.cx);
 
     RootedObject obj(f.cx, ValueToObject(f.cx, f.regs.sp[-1]));
     if (!obj)
         THROW();
 
-    if (!monitor.recompiled() && pic->shouldUpdate(f.cx)) {
+    if (!monitor.recompiled() && pic->shouldUpdate(f)) {
         GetPropCompiler cc(f, obj, *pic, name, stub);
         if (!cc.update())
             THROW();
     }
 
     RootedValue v(f.cx);
     if (cached) {
         if (!GetPropertyOperation(f.cx, f.script(), f.pc(), f.regs.sp[-1], v.address()))
@@ -1955,17 +1955,17 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic
     RecompilationMonitor monitor(f.cx);
 
     JSObject *obj = ValueToObject(f.cx, f.regs.sp[-2]);
     if (!obj)
         THROW();
 
     // Note, we can't use SetName for PROPINC PICs because the property
     // cache can't handle a GET and SET from the same scripted PC.
-    if (!monitor.recompiled() && pic->shouldUpdate(f.cx)) {
+    if (!monitor.recompiled() && pic->shouldUpdate(f)) {
         SetPropCompiler cc(f, obj, *pic, name, stub);
         LookupStatus status = cc.update();
         if (status == Lookup_Error)
             THROW();
     }
 
     nstub(f, name);
 }
@@ -2030,21 +2030,22 @@ ic::BindName(VMFrame &f, ic::PICInfo *pi
     JSObject *obj = cc.update();
     if (!obj)
         THROW();
 
     f.regs.sp[0].setObject(*obj);
 }
 
 void
-BaseIC::spew(JSContext *cx, const char *event, const char *message)
+BaseIC::spew(VMFrame &f, const char *event, const char *message)
 {
 #ifdef JS_METHODJIT_SPEW
     JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
-               js_CodeName[op], event, message, cx->fp()->script()->filename, CurrentLine(cx));
+               js_CodeName[JSOp(*f.pc())], event, message,
+               f.cx->fp()->script()->filename, CurrentLine(f.cx));
 #endif
 }
 
 /* Total length of scripts preceding a frame. */
 inline uint32_t frameCountersOffset(VMFrame &f)
 {
     JSContext *cx = f.cx;
 
@@ -2067,37 +2068,37 @@ inline uint32_t frameCountersOffset(VMFr
 LookupStatus
 BaseIC::disable(VMFrame &f, const char *reason, void *stub)
 {
     if (f.chunk()->pcLengths) {
         uint32_t offset = frameCountersOffset(f);
         f.chunk()->pcLengths[offset].picsLength = 0;
     }
 
-    spew(f.cx, "disabled", reason);
+    spew(f, "disabled", reason);
     Repatcher repatcher(f.chunk());
     repatcher.relink(slowPathCall, FunctionPtr(stub));
     return Lookup_Uncacheable;
 }
 
 void
 BaseIC::updatePCCounters(VMFrame &f, Assembler &masm)
 {
     if (f.chunk()->pcLengths) {
         uint32_t offset = frameCountersOffset(f);
         f.chunk()->pcLengths[offset].picsLength += masm.size();
     }
 }
 
 bool
-BaseIC::shouldUpdate(JSContext *cx)
+BaseIC::shouldUpdate(VMFrame &f)
 {
     if (!hit) {
         hit = true;
-        spew(cx, "ignored", "first hit");
+        spew(f, "ignored", "first hit");
         return false;
     }
     JS_ASSERT(stubsGenerated < MAX_PIC_STUBS);
     return true;
 }
 
 void
 PICInfo::purge(Repatcher &repatcher)
@@ -2125,21 +2126,21 @@ PICInfo::purge(Repatcher &repatcher)
 
 static void JS_FASTCALL
 DisabledGetElem(VMFrame &f, ic::GetElementIC *ic)
 {
     stubs::GetElem(f);
 }
 
 bool
-GetElementIC::shouldUpdate(JSContext *cx)
+GetElementIC::shouldUpdate(VMFrame &f)
 {
     if (!hit) {
         hit = true;
-        spew(cx, "ignored", "first hit");
+        spew(f, "ignored", "first hit");
         return false;
     }
     JS_ASSERT(stubsGenerated < MAX_GETELEM_IC_STUBS);
     return true;
 }
 
 LookupStatus
 GetElementIC::disable(VMFrame &f, const char *reason)
@@ -2237,23 +2238,16 @@ GetElementIC::attachGetProp(VMFrame &f, 
         holderReg = typeReg;
         masm.move(ImmPtr(holder), holderReg);
         typeRegHasBaseShape = false;
 
         // Guard on the holder's shape.
         protoGuard = masm.guardShape(holderReg, holder);
     }
 
-    if (op == JSOP_CALLELEM) {
-        // Emit a write of |obj| to the top of the stack, before we lose it.
-        Value *thisVp = &cx->regs().sp[-1];
-        Address thisSlot(JSFrameReg, StackFrame::offsetOfFixed(thisVp - cx->fp()->slots()));
-        masm.storeValueFromComponents(ImmType(JSVAL_TYPE_OBJECT), objReg, thisSlot);
-    }
-
     // Load the value.
     Shape *shape = getprop.shape;
     masm.loadObjProp(holder, holderReg, shape, typeReg, objReg);
 
     Jump done = masm.jump();
 
     updatePCCounters(f, masm);
 
@@ -2274,17 +2268,17 @@ GetElementIC::attachGetProp(VMFrame &f, 
     for (Jump *pj = otherGuards.begin(); pj != otherGuards.end(); ++pj)
         buffer.link(*pj, slowPathStart);
     buffer.link(done, fastPathRejoin);
 
     CodeLocationLabel cs = buffer.finalize(f);
 #if DEBUG
     char *chars = DeflateString(cx, v.toString()->getChars(cx), v.toString()->length());
     JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom %p (\"%s\") shape %p (%s: %d)\n",
-               js_CodeName[op], cs.executableAddress(), (void*)name, chars,
+               js_CodeName[JSOp(*f.pc())], cs.executableAddress(), (void*)name, chars,
                (void*)holder->lastProperty(), cx->fp()->script()->filename, CurrentLine(cx));
     cx->free_(chars);
 #endif
 
     // Update the inline guards, if needed.
     if (shouldPatchInlineTypeGuard() || shouldPatchUnconditionalShapeGuard()) {
         Repatcher repatcher(f.chunk());
 
@@ -2357,17 +2351,17 @@ GetElementIC::attachGetProp(VMFrame &f, 
 LookupStatus
 GetElementIC::attachTypedArray(VMFrame &f, HandleObject obj, HandleValue v, HandleId id, Value *vp)
 {
     JSContext *cx = f.cx;
 
     if (!v.isInt32())
         return disable(f, "typed array with string key");
 
-    if (op == JSOP_CALLELEM)
+    if (JSOp(*f.pc()) == JSOP_CALLELEM)
         return disable(f, "typed array with call");
 
     // The fast-path guarantees that after the dense shape guard, the type is
     // known to be int32, either via type inference or the inline type check.
     JS_ASSERT(hasInlineTypeGuard() || idRemat.knownType() == JSVAL_TYPE_INT32);
 
     Assembler masm;
 
@@ -2513,17 +2507,17 @@ ic::GetElement(VMFrame &f, ic::GetElemen
     Rooted<jsid> id(cx);
     if (idval.isInt32() && INT_FITS_IN_JSID(idval.toInt32())) {
         id = INT_TO_JSID(idval.toInt32());
     } else {
         if (!InternNonIntElementId(cx, obj, idval, id.address()))
             THROW();
     }
 
-    if (!monitor.recompiled() && ic->shouldUpdate(cx)) {
+    if (!monitor.recompiled() && ic->shouldUpdate(f)) {
 #ifdef DEBUG
         f.regs.sp[-2] = MagicValue(JS_GENERIC_MAGIC);
 #endif
         LookupStatus status = ic->update(f, obj, idval_, id, &f.regs.sp[-2]);
         if (status != Lookup_Uncacheable) {
             if (status == Lookup_Error)
                 THROW();
 
@@ -2797,37 +2791,35 @@ SetElementIC::update(VMFrame &f, const V
     if (!f.cx->typeInferenceEnabled() && obj->isTypedArray())
         return attachTypedArray(f, obj, key);
 #endif
 
     return disable(f, "unsupported object type");
 }
 
 bool
-SetElementIC::shouldUpdate(JSContext *cx)
+SetElementIC::shouldUpdate(VMFrame &f)
 {
     if (!hit) {
         hit = true;
-        spew(cx, "ignored", "first hit");
+        spew(f, "ignored", "first hit");
         return false;
     }
 #ifdef JSGC_INCREMENTAL_MJ
-    JS_ASSERT(!cx->compartment->needsBarrier());
+    JS_ASSERT(!f.cx->compartment->needsBarrier());
 #endif
     JS_ASSERT(stubsGenerated < MAX_PIC_STUBS);
     return true;
 }
 
 template<JSBool strict>
 void JS_FASTCALL
 ic::SetElement(VMFrame &f, ic::SetElementIC *ic)
 {
-    JSContext *cx = f.cx;
-
-    if (ic->shouldUpdate(cx)) {
+    if (ic->shouldUpdate(f)) {
         LookupStatus status = ic->update(f, f.regs.sp[-3], f.regs.sp[-2]);
         if (status == Lookup_Error)
             THROW();
     }
 
     stubs::SetElem<strict>(f);
 }
 
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -61,24 +61,20 @@ struct BaseIC : public MacroAssemblerTyp
     bool canCallHook : 1;
 
     // Whether a type barrier is in place for the result of the op.
     bool forcedTypeBarrier : 1;
 
     // Number of stubs generated.
     uint32_t stubsGenerated : 5;
 
-    // Opcode this was compiled for.
-    JSOp op : 9;
-
-    bool shouldUpdate(JSContext *cx);
-    void spew(JSContext *cx, const char *event, const char *reason);
+    bool shouldUpdate(VMFrame &f);
+    void spew(VMFrame &f, const char *event, const char *reason);
     LookupStatus disable(VMFrame &f, const char *reason, void *stub);
     void updatePCCounters(VMFrame &f, Assembler &masm);
-    bool isCallOp();
 
   protected:
     void reset() {
         hit = false;
         slowCallPatched = false;
         forcedTypeBarrier = false;
         stubsGenerated = 0;
         secondShapeGuard = 0;
@@ -244,17 +240,17 @@ struct GetElementIC : public BasePolyIC 
 
     void purge(Repatcher &repatcher);
     LookupStatus update(VMFrame &f, HandleObject obj, HandleValue v, HandleId id, Value *vp);
     LookupStatus attachGetProp(VMFrame &f, HandleObject obj, HandleValue v, HandlePropertyName name,
                                Value *vp);
     LookupStatus attachTypedArray(VMFrame &f, HandleObject obj, HandleValue v, HandleId id, Value *vp);
     LookupStatus disable(VMFrame &f, const char *reason);
     LookupStatus error(JSContext *cx);
-    bool shouldUpdate(JSContext *cx);
+    bool shouldUpdate(VMFrame &f);
 
   protected:
     void reset() {
         BasePolyIC::reset();
         inlineTypeGuardPatched = false;
         inlineShapeGuardPatched = false;
         typeRegHasBaseShape = false;
         hasLastStringStub = false;
@@ -306,17 +302,17 @@ struct SetElementIC : public BaseIC {
     JSC::ExecutablePool *execPool;
 
     void purge(Repatcher &repatcher);
     LookupStatus attachTypedArray(VMFrame &f, JSObject *obj, int32_t key);
     LookupStatus attachHoleStub(VMFrame &f, JSObject *obj, int32_t key);
     LookupStatus update(VMFrame &f, const Value &objval, const Value &idval);
     LookupStatus disable(VMFrame &f, const char *reason);
     LookupStatus error(JSContext *cx);
-    bool shouldUpdate(JSContext *cx);
+    bool shouldUpdate(VMFrame &f);
 
   protected:
     void reset() {
         BaseIC::reset();
         if (execPool) {
             execPool->release();
             execPool = NULL;
         }
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -32,17 +32,17 @@
 
 /**
  * Note: This is not really an XPCOM interface.  For example, callers must
  * guarantee that they set the *_retval of the various methods that return a
  * boolean to PR_TRUE before making the call.  Implementations may skip writing
  * to *_retval unless they want to return PR_FALSE.
  */
 
-[uuid(782d317e-0727-4ccd-8a5b-f5d237f3b03c)]
+[uuid(c4788e02-3239-490a-8aeb-60fad08303fd)]
 interface nsIXPCScriptable : nsISupports
 {
     /* bitflags used for 'flags' (only 32 bits available!) */
 
     const PRUint32 WANT_PRECREATE                   = 1 <<  0;
     const PRUint32 WANT_CREATE                      = 1 <<  1;
     const PRUint32 WANT_POSTCREATE                  = 1 <<  2;
     const PRUint32 WANT_ADDPROPERTY                 = 1 <<  3;
@@ -83,19 +83,26 @@ interface nsIXPCScriptable : nsISupports
     [notxpcom,nostdcall] PRUint32 getScriptableFlags();
 
     void   preCreate(in nsISupports nativeObj, in JSContextPtr cx,
                      in JSObjectPtr globalObj, out JSObjectPtr parentObj);
 
     void   create(in nsIXPConnectWrappedNative wrapper,
                   in JSContextPtr cx, in JSObjectPtr obj);
 
+    // Both methods here are protected by WANT_POSTCREATE. If you want to do
+    // something after a wrapper is created, there's a good chance you also
+    // want to do something when the wrapper is transplanted to a new
+    // compartment.
     void   postCreate(in nsIXPConnectWrappedNative wrapper,
                       in JSContextPtr cx, in JSObjectPtr obj);
 
+    void postTransplant(in nsIXPConnectWrappedNative wrapper,
+                        in JSContextPtr cx, in JSObjectPtr obj);
+
     boolean addProperty(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in JSValPtr vp);
 
     boolean delProperty(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in JSValPtr vp);
 
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -100,16 +100,19 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCrea
 #ifndef XPC_MAP_WANT_CREATE
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Create(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #ifndef XPC_MAP_WANT_POSTCREATE
 NS_IMETHODIMP XPC_MAP_CLASSNAME::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
+
+NS_IMETHODIMP XPC_MAP_CLASSNAME::PostTransplant(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj)
+    {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #ifndef XPC_MAP_WANT_ADDPROPERTY
 NS_IMETHODIMP XPC_MAP_CLASSNAME::AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, jsid id, jsval * vp, bool *_retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #ifndef XPC_MAP_WANT_DELPROPERTY
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -482,18 +482,24 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
     //
     // To handle this we need to get the scriptable helper early and ask it.
     // It is possible that we will then end up forwarding this entire call
     // to this same function but with a different scope.
 
     // If we are making a wrapper for the nsIClassInfo interface then
     // We *don't* want to have it use the prototype meant for instances
     // of that class.
-    JSBool isClassInfo = Interface &&
-                         Interface->GetIID()->Equals(NS_GET_IID(nsIClassInfo));
+    bool iidIsClassInfo = Interface &&
+                          Interface->GetIID()->Equals(NS_GET_IID(nsIClassInfo));
+    PRUint32 classInfoFlags;
+    bool isClassInfoSingleton = helper.GetClassInfo() == helper.Object() &&
+                                NS_SUCCEEDED(helper.GetClassInfo()
+                                                   ->GetFlags(&classInfoFlags)) &&
+                                (classInfoFlags & nsIClassInfo::SINGLETON_CLASSINFO);
+    bool isClassInfo = iidIsClassInfo || isClassInfoSingleton;
 
     nsIClassInfo *info = helper.GetClassInfo();
 
     XPCNativeScriptableCreateInfo sciProto;
     XPCNativeScriptableCreateInfo sci;
 
     // Gather scriptable create info if we are wrapping something
     // other than an nsIClassInfo object. We need to not do this for
@@ -1647,16 +1653,21 @@ XPCWrappedNative::ReparentWrapperIfFound
             SetSlimWrapperProto(flat, newProto.get());
             if (!JS_SetPrototype(ccx, flat, newProto->GetJSProtoObject())) {
                 // this is bad, very bad
                 SetSlimWrapperProto(flat, nsnull);
                 NS_ERROR("JS_SetPrototype failed");
                 return NS_ERROR_FAILURE;
             }
         }
+
+        // Call the scriptable hook to indicate that we transplanted.
+        XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
+        if (si->GetFlags().WantPostCreate())
+            (void) si->GetCallback()->PostTransplant(wrapper, ccx, flat);
     }
 
     // Now we can just fix up the parent and return the wrapper
 
     if (aNewParent) {
         if (!JS_SetParent(ccx, flat, aNewParent))
             return NS_ERROR_FAILURE;
 
--- a/js/xpconnect/tests/chrome/Makefile.in
+++ b/js/xpconnect/tests/chrome/Makefile.in
@@ -7,55 +7,56 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 relativesrcdir  = js/xpconnect/tests/chrome
 
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_CHROME_FILES = \
 		test_bug448587.xul \
+		test_bug484459.xul \
 		test_bug500931.xul \
 		bug503926.xul \
 		test_bug503926.xul \
+		test_bug517163.xul \
 		test_bug533596.xul \
-		test_doublewrappedcompartments.xul \
-		test_evalInSandbox.xul \
-		file_evalInSandbox.html \
-		test_sandboxImport.xul \
-		test_wrappers.xul \
-		test_bug484459.xul \
-		test_cows.xul \
-		test_bug517163.xul \
 		test_bug571849.xul \
+		test_bug596580.xul \
 		test_bug601803.xul \
 		test_bug610390.xul \
 		test_bug614757.xul \
 		test_bug616992.xul \
 		test_bug618176.xul \
 		file_bug618176.xul \
-		test_bug596580.xul \
 		test_bug654370.xul \
 		test_bug658560.xul \
+		test_bug664689.xul \
 		test_bug679861.xul \
-		test_bug664689.xul \
 		test_bug706301.xul \
+		test_bug726949.xul \
 		test_bug743843.xul \
+		test_bug758563.xul \
+		test_bug760076.xul \
+		test_bug763343.xul \
 		test_APIExposer.xul \
-		test_precisegc.xul \
-		test_nodelists.xul \
-		test_getweakmapkeys.xul \
-		test_weakmaps.xul \
+		test_cows.xul \
+		test_documentdomain.xul \
+		test_doublewrappedcompartments.xul \
+		test_evalInSandbox.xul \
+		file_evalInSandbox.html \
 		test_exnstack.xul \
-		test_weakref.xul \
-		test_bug726949.xul \
 		test_expandosharing.xul \
 		file_expandosharing.jsm \
-		test_bug758563.xul \
-		test_bug760076.xul \
-		test_documentdomain.xul \
+		test_getweakmapkeys.xul \
+		test_nodelists.xul \
+		test_precisegc.xul \
+		test_sandboxImport.xul \
+		test_weakmaps.xul \
+		test_weakref.xul \
+		test_wrappers.xul \
 		$(NULL)
 
 # Disabled until this test gets updated to test the new proxy based
 # wrappers.
 #		test_wrappers-2.xul \
 
 # Disabled due to apparent conservative stack scanner false positives on Linux64 debug.
 #		test_watchpoints.xul \
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/chrome/test_bug763343.xul
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=763343
+-->
+<window title="Mozilla Bug 763343"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=763343"
+     target="_blank">Mozilla Bug 763343</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  /** Test for Cross-compartment nsIClassInfo singleton wrapping. **/
+  const Cc = Components.classes;
+  const Ci = Components.interfaces;
+  const Cu = Components.utils;
+
+  var singleton = window.QueryInterface(Ci.nsIClassInfo);
+  var sb = new Cu.Sandbox(window);
+
+  // Don't crash.
+  sb.singleton = singleton;
+  ok(true, "didn't crash");
+
+  ]]>
+  </script>
+</window>
--- a/layout/reftests/css-gradients/reftest.list
+++ b/layout/reftests/css-gradients/reftest.list
@@ -56,25 +56,25 @@ fuzzy-if(azureSkia,1,7860) fuzzy-if(azur
 fuzzy-if(azureSkia,1,7860) fuzzy-if(azureQuartz,1,1926) fails-if(Android) == radial-2d.html radial-2-ref.html
 fuzzy-if(azureSkia,1,7860) fuzzy-if(azureQuartz,1,1926) fails-if(Android) == radial-2e.html radial-2-ref.html
 fuzzy-if(azureSkia,1,7860) fuzzy-if(azureQuartz,1,1926) fails-if(Android) == radial-2f.html radial-2-ref.html
 == radial-position-1a.html radial-position-1-ref.html
 == radial-position-1b.html radial-position-1-ref.html
 == radial-shape-closest-corner-1a.html radial-shape-closest-corner-1-ref.html
 == radial-shape-closest-corner-1b.html radial-shape-closest-corner-1-ref.html
 == radial-shape-closest-corner-1c.html radial-shape-closest-corner-1-ref.html
-== radial-shape-closest-side-1a.html radial-shape-closest-side-1-ref.html
-== radial-shape-closest-side-1b.html radial-shape-closest-side-1-ref.html
-== radial-shape-closest-side-1c.html radial-shape-closest-side-1-ref.html
+fuzzy-if(Android,17,3880) == radial-shape-closest-side-1a.html radial-shape-closest-side-1-ref.html
+fuzzy-if(Android,17,3880) == radial-shape-closest-side-1b.html radial-shape-closest-side-1-ref.html
+fuzzy-if(Android,17,3880) == radial-shape-closest-side-1c.html radial-shape-closest-side-1-ref.html
 == radial-shape-farthest-corner-1a.html radial-shape-farthest-corner-1-ref.html
 fails-if(cocoaWidget&&/x86-/.test(xulRuntime.XPCOMABI)||gtk2Widget&&/x86_64-/.test(xulRuntime.XPCOMABI)) == radial-shape-farthest-corner-1b.html radial-shape-farthest-corner-1-ref.html
 == radial-shape-farthest-corner-1c.html radial-shape-farthest-corner-1-ref.html
-== radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html
-== radial-shape-farthest-side-1b.html radial-shape-farthest-side-1-ref.html
-== radial-shape-farthest-side-1c.html radial-shape-farthest-side-1-ref.html
+fuzzy-if(Android,17,13314) == radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html
+fuzzy-if(Android,17,13314) == radial-shape-farthest-side-1b.html radial-shape-farthest-side-1-ref.html
+fuzzy-if(Android,17,13314) == radial-shape-farthest-side-1c.html radial-shape-farthest-side-1-ref.html
 == radial-size-1a.html radial-size-1-ref.html
 == radial-size-1b.html radial-size-1-ref.html
 == radial-zero-length-1a.html radial-zero-length-1-ref.html
 == radial-zero-length-1b.html radial-zero-length-1-ref.html
 == radial-zero-length-1c.html radial-zero-length-1-ref.html
 == radial-zero-length-1d.html radial-zero-length-1-ref.html
 == radial-zero-length-1e.html radial-zero-length-1-ref.html
 == radial-zero-length-1f.html radial-zero-length-1-ref.html
--- a/layout/reftests/fonts/Makefile.in
+++ b/layout/reftests/fonts/Makefile.in
@@ -2,17 +2,17 @@
 # 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/.
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir  = fonts
 
 include $(DEPTH)/config/autoconf.mk
-include $(topsrcdir)/config/rules.mk
 
-_TEST_FILES =	Ahem.ttf \
-		$(NULL)
+MOCHITEST_FILES = \
+  Ahem.ttf \
+  $(NULL)
 
-libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/fonts
+include $(topsrcdir)/config/rules.mk
--- a/layout/reftests/fonts/mplus/Makefile.in
+++ b/layout/reftests/fonts/mplus/Makefile.in
@@ -2,17 +2,17 @@
 # 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/.
 
 DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
+relativesrcdir  = fonts/mplus
 
 include $(DEPTH)/config/autoconf.mk
-include $(topsrcdir)/config/rules.mk
 
-_TEST_FILES =	mplus-1p-regular.ttf \
-		$(NULL)
+MOCHITEST_FILES = \
+  mplus-1p-regular.ttf \
+  $(NULL)
 
-libs:: $(_TEST_FILES)
-	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/fonts/mplus
+include $(topsrcdir)/config/rules.mk
--- a/layout/reftests/image-element/reftest.list
+++ b/layout/reftests/image-element/reftest.list
@@ -35,12 +35,12 @@ random-if(d2d) == element-paint-transfor
 random-if(!cocoaWidget) == gradient-html-03.html gradient-html-03-ref.svg
 == gradient-html-04.html gradient-html-04-ref.html
 == gradient-html-05.html gradient-html-05-ref.html
 random-if(!cocoaWidget) == gradient-html-06a.html gradient-html-06b.html
 random-if(!cocoaWidget) == gradient-html-06b.html gradient-html-06c.html
 == gradient-html-06c.html gradient-html-06d.html
 == gradient-html-06d.html gradient-html-06e.html
 random-if(!cocoaWidget) == gradient-html-07a.html gradient-html-07b.html
-== gradient-html-07b.html gradient-html-07c.html
+fuzzy-if(Android,9,23112) == gradient-html-07b.html gradient-html-07c.html
 == pattern-html-01.html pattern-html-01-ref.svg
 == pattern-html-02.html pattern-html-02-ref.svg
 == referenced-from-binding-01.html referenced-from-binding-01-ref.html
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -173,17 +173,17 @@ fails == inline-in-xul-basic-01.xul pass
 == mask-basic-01.svg pass.svg
 == mask-basic-02.svg mask-basic-02-ref.svg
 == mask-extref-dataURI-01.svg pass.svg
 == mask-containing-masked-content-01.svg pass.svg
 == mask-transformed-01.svg mask-transformed-01-ref.svg
 == nested-viewBox-01.svg pass.svg
 == nesting-invalid-01.svg nesting-invalid-01-ref.svg
 == non-scaling-stroke-01.svg non-scaling-stroke-01-ref.svg 
-fuzzy-if(Android,9,38) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg 
+fuzzy-if(Android,9,61) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg 
 == objectBoundingBox-and-clipPath.svg pass.svg
 # Bug 588684
 random-if(gtk2Widget) == objectBoundingBox-and-fePointLight-01.svg objectBoundingBox-and-fePointLight-01-ref.svg
 random-if(gtk2Widget) == objectBoundingBox-and-fePointLight-02.svg objectBoundingBox-and-fePointLight-02-ref.svg
 == objectBoundingBox-and-mask.svg pass.svg
 == objectBoundingBox-and-mask-02.svg pass.svg
 == objectBoundingBox-and-pattern-01a.svg objectBoundingBox-and-pattern-01-ref.svg
 == objectBoundingBox-and-pattern-01b.svg objectBoundingBox-and-pattern-01-ref.svg
--- a/mobile/android/base/DoorHanger.java
+++ b/mobile/android/base/DoorHanger.java
@@ -163,17 +163,17 @@ public class DoorHanger extends LinearLa
             mCheckBox.setText(checkBoxText);
             mCheckBox.setVisibility(VISIBLE);
         } catch (JSONException e) { }
     }
 
     // This method checks with persistence and timeout options to see if
     // it's okay to remove a doorhanger.
     public boolean shouldRemove() {
-        if (mPersistWhileVisible && GeckoApp.mDoorHangerPopup.isShowing()) {
+        if (mPersistWhileVisible && GeckoApp.mAppContext.mDoorHangerPopup.isShowing()) {
             // We still want to decrement mPersistence, even if the popup is showing
             if (mPersistence != 0)
                 mPersistence--;
             return false;
         }
 
         // If persistence is set to -1, the doorhanger will never be
         // automatically removed.
--- a/mobile/android/base/FormAssistPopup.java
+++ b/mobile/android/base/FormAssistPopup.java
@@ -70,16 +70,22 @@ public class FormAssistPopup extends Rel
 
         setFocusable(false);
 
         GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", this);
         GeckoAppShell.registerGeckoEventListener("FormAssist:ValidationMessage", this);
         GeckoAppShell.registerGeckoEventListener("FormAssist:Hide", this);
     }
 
+    void destroy() {
+        GeckoAppShell.unregisterGeckoEventListener("FormAssist:AutoComplete", this);
+        GeckoAppShell.unregisterGeckoEventListener("FormAssist:ValidationMessage", this);
+        GeckoAppShell.unregisterGeckoEventListener("FormAssist:Hide", this);
+    }
+
     public void handleMessage(String event, JSONObject message) {
         try {
             if (event.equals("FormAssist:AutoComplete")) {
                 handleAutoCompleteMessage(message);
             } else if (event.equals("FormAssist:ValidationMessage")) {
                 handleValidationMessage(message);
             } else if (event.equals("FormAssist:Hide")) {
                 handleHideMessage(message);
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -77,40 +77,41 @@ abstract public class GeckoApp
     public static final String ACTION_UPDATE        = "org.mozilla.gecko.UPDATE";
     public static final String ACTION_INIT_PW       = "org.mozilla.gecko.INIT_PW";
     public static final String SAVED_STATE_TITLE    = "title";
 
     StartupMode mStartupMode = null;
     protected LinearLayout mMainLayout;
     protected RelativeLayout mGeckoLayout;
     public View getView() { return mGeckoLayout; }
-    public static SurfaceView cameraView;
+    public SurfaceView cameraView;
     public static GeckoApp mAppContext;
-    public static boolean mDOMFullScreen = false;
+    public boolean mDOMFullScreen = false;
     protected MenuPanel mMenuPanel;
     protected Menu mMenu;
     private static GeckoThread sGeckoThread;
     public Handler mMainHandler;
     private GeckoProfile mProfile;
     public static boolean sIsGeckoReady = false;
     public static int mOrientation;
+    private boolean mIsRestoringActivity;
 
     private GeckoConnectivityReceiver mConnectivityReceiver;
     private GeckoBatteryManager mBatteryReceiver;
     private PromptService mPromptService;
 
-    public static DoorHangerPopup mDoorHangerPopup;
-    public static FormAssistPopup mFormAssistPopup;
+    public DoorHangerPopup mDoorHangerPopup;
+    public FormAssistPopup mFormAssistPopup;
     public TabsPanel mTabsPanel;
     public Favicons mFavicons;
 
-    private static LayerController mLayerController;
-    private static GeckoLayerClient mLayerClient;
-    private static AbsoluteLayout mPluginContainer;
-    private static FindInPageBar mFindInPageBar;
+    private LayerController mLayerController;
+    private GeckoLayerClient mLayerClient;
+    private AbsoluteLayout mPluginContainer;
+    private FindInPageBar mFindInPageBar;
 
     private FullScreenHolder mFullScreenPluginContainer;
     private View mFullScreenPluginView;
 
     private HashMap<String, PowerManager.WakeLock> mWakeLocks = new HashMap<String, PowerManager.WakeLock>();
 
     protected int mRestoreMode = GeckoAppShell.RESTORE_NONE;
     private boolean mInitialized = false;
@@ -1757,20 +1758,26 @@ abstract public class GeckoApp
         }
 
         // StrictMode is set by defaults resource flag |enableStrictMode|.
         if (getResources().getBoolean(R.bool.enableStrictMode)) {
             enableStrictMode();
         }
 
         GeckoAppShell.loadMozGlue();
-        sGeckoThread = new GeckoThread();
-        String uri = getURIFromIntent(getIntent());
-        if (uri != null && uri.length() > 0 && !uri.equals("about:home"))
-            sGeckoThread.start();
+        if (sGeckoThread == null) {
+            sGeckoThread = new GeckoThread();
+            String uri = getURIFromIntent(getIntent());
+            if (uri != null && uri.length() > 0 && !uri.equals("about:home"))
+                sGeckoThread.start();
+        } else {
+            // this happens when the GeckoApp activity is destroyed by android
+            // without killing the entire application (see bug 769269)
+            mIsRestoringActivity = true;
+        }
 
         mMainHandler = new Handler();
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onCreate");
 
         LayoutInflater.from(this).setFactory(GeckoViewsFactory.getInstance());
 
         super.onCreate(savedInstanceState);
 
@@ -2007,16 +2014,26 @@ abstract public class GeckoApp
                     GeckoAppShell.setSelectedLocale(localeCode);
                 */
 
                 if (!checkLaunchState(LaunchState.Launched)) {
                     return;
                 }
             }
         }, 50);
+
+        if (mIsRestoringActivity) {
+            setLaunchState(GeckoApp.LaunchState.GeckoRunning);
+            Tab selectedTab = Tabs.getInstance().getSelectedTab();
+            if (selectedTab != null)
+                Tabs.getInstance().selectTab(selectedTab.getId());
+            connectGeckoLayerClient();
+            GeckoAppShell.setLayerClient(getLayerClient());
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Viewport:Flush", null));
+        }
     }
 
     public GeckoProfile getProfile() {
         // fall back to default profile if we didn't load a specific one
         if (mProfile == null) {
             mProfile = GeckoProfile.get(this);
         }
         return mProfile;
@@ -2289,42 +2306,56 @@ abstract public class GeckoApp
         GeckoAppShell.unregisterGeckoEventListener("Content:LocationChange", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Content:SecurityChange", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Content:ReaderEnabled", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Content:StateChange", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Content:LoadError", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Content:PageShow", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("onCameraCapture", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Doorhanger:Add", GeckoApp.mAppContext);
+        GeckoAppShell.unregisterGeckoEventListener("Doorhanger:Remove", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Menu:Add", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Menu:Remove", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Gecko:Ready", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Toast:Show", GeckoApp.mAppContext);
+        GeckoAppShell.unregisterGeckoEventListener("DOMFullScreen:Start", GeckoApp.mAppContext);
+        GeckoAppShell.unregisterGeckoEventListener("DOMFullScreen:Stop", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Focus", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext);
+        GeckoAppShell.unregisterGeckoEventListener("Update:Restart", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Tab:HasTouchListener", GeckoApp.mAppContext);
+        GeckoAppShell.unregisterGeckoEventListener("Tab:ViewportMetadata", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Session:StatePurged", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Bookmark:Insert", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Accessibility:Event", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Accessibility:Ready", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Shortcut:Remove", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("WebApps:Open", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("WebApps:Install", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("WebApps:Uninstall", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("DesktopMode:Changed", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Share:Text", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Share:Image", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Sanitize:ClearHistory", GeckoApp.mAppContext);
 
         deleteTempFiles();
 
+        if (mLayerController != null)
+            mLayerController.destroy();
+        if (mLayerClient != null)
+            mLayerClient.destroy();
+        if (mFormAssistPopup != null)
+            mFormAssistPopup.destroy();
+        if (mPromptService != null)
+            mPromptService.destroy();
+
         if (mFavicons != null)
             mFavicons.close();
 
         if (SmsManager.getInstance() != null) {
             SmsManager.getInstance().stop();
             if (isFinishing())
                 SmsManager.getInstance().shutdown();
         }
@@ -2876,18 +2907,16 @@ abstract public class GeckoApp
             // The back button should always return to the parent (not a sibling).
             tabs.closeTab(tab, parent);
             return;
         }
 
         moveTaskToBack(true);
     }
 
-    static int kCaptureIndex = 0;
-
     public interface ActivityResultHandler {
         public void onActivityResult(int resultCode, Intent data);
     }
 
     class ActivityResultHandlerMap {
         private Map<Integer, ActivityResultHandler> mMap = new HashMap<Integer, ActivityResultHandler>();
         private int mCounter = 0;
 
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -1840,17 +1840,17 @@ public class GeckoAppShell
                 if (Math.abs(size.width * size.height - aWidth * aHeight) < sizeDelta) {
                     sizeDelta = Math.abs(size.width * size.height - aWidth * aHeight);
                     params.setPreviewSize(size.width, size.height);
                     bufferSize = size.width * size.height;
                 }
             }
 
             try {
-                sCamera.setPreviewDisplay(GeckoApp.cameraView.getHolder());
+                sCamera.setPreviewDisplay(GeckoApp.mAppContext.cameraView.getHolder());
             } catch(IOException e) {
                 Log.e(LOGTAG, "Error setPreviewDisplay:", e);
             } catch(RuntimeException e) {
                 Log.e(LOGTAG, "Error setPreviewDisplay:", e);
             }
 
             sCamera.setParameters(params);
             sCameraBuffer = new byte[(bufferSize * 12) / 8];
@@ -2341,17 +2341,22 @@ class ScreenshotHandler {
                 mDirtyLeft = Float.POSITIVE_INFINITY;
                 mDirtyBottom = Float.NEGATIVE_INFINITY;
                 mDirtyRight = Float.NEGATIVE_INFINITY;
                 mIsRepaintRunnablePosted = false;
             }
 
 
             Tab tab = Tabs.getInstance().getSelectedTab();
-            ImmutableViewportMetrics viewport = GeckoApp.mAppContext.getLayerController().getViewportMetrics();
+            if (tab == null)
+                return;
+            LayerController layerController = GeckoApp.mAppContext.getLayerController();
+            if (layerController == null)
+                return;
+            ImmutableViewportMetrics viewport = layerController.getViewportMetrics();
             
             if (RectUtils.fuzzyEquals(sCheckerboardPageRect, viewport.getCssPageRect())) {
                 float width = right - left;
                 float height = bottom - top;
                 scheduleCheckerboardScreenshotEvent(tab.getId(), 
                                                     (int)left, (int)top, (int)width, (int)height, 
                                                     (int)(sLastCheckerboardWidthRatio * (left - viewport.cssPageRectLeft)),
                                                     (int)(sLastCheckerboardHeightRatio * (top - viewport.cssPageRectTop)),
--- a/mobile/android/base/PromptService.java
+++ b/mobile/android/base/PromptService.java
@@ -80,16 +80,20 @@ public class PromptService implements On
                                                           res.getDisplayMetrics());
         mIconSize = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                                                    ICON_SIZE,
                                                    res.getDisplayMetrics());
 
         GeckoAppShell.registerGeckoEventListener("Prompt:Show", this);
     }
 
+    void destroy() {
+        GeckoAppShell.unregisterGeckoEventListener("Prompt:Show", this);
+    }
+
     private class PromptButton {
         public String label = "";
         PromptButton(JSONObject aJSONButton) {
             try {
                 label = aJSONButton.getString("label");
             } catch(Exception ex) { }
         }
     }
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -96,17 +96,17 @@ public class Tabs implements GeckoEventL
         // This avoids a NPE below, but callers need to be careful to
         // handle this case
         if (tab == null)
             return null;
 
         selectedTab = tab;
         GeckoApp.mAppContext.mMainHandler.post(new Runnable() { 
             public void run() {
-                GeckoApp.mFormAssistPopup.hide();
+                GeckoApp.mAppContext.mFormAssistPopup.hide();
                 if (isSelectedTab(tab)) {
                     String url = tab.getURL();
                     notifyListeners(tab, TabEvents.SELECTED);
 
                     if (oldTab != null)
                         notifyListeners(oldTab, TabEvents.UNSELECTED);
                 }
             }
--- a/mobile/android/base/gfx/GLController.java
+++ b/mobile/android/base/gfx/GLController.java
@@ -71,18 +71,25 @@ public class GLController {
         }
 
         mEGLDisplay = null;
         mEGLConfig = null;
         mEGLSurface = null;
         return true;
     }
 
+    // This function is invoked by JNI
+    public synchronized void resumeCompositorIfValid() {
+        if (mSurfaceValid) {
+            mView.getListener().compositionResumeRequested(mWidth, mHeight);
+        }
+    }
+
     // Wait until we are allowed to use EGL functions on the Surface backing
-    // this window.
+    // this window. This function is invoked by JNI
     public synchronized void waitForValidSurface() {
         while (!mSurfaceValid) {
             try {
                 wait();
             } catch (InterruptedException e) {
                 throw new RuntimeException(e);
             }
         }
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -101,16 +101,24 @@ public class GeckoLayerClient implements
         sendResizeEventIfNecessary(true);
 
         JSONArray prefs = new JSONArray();
         DisplayPortCalculator.addPrefNames(prefs);
         PluginLayer.addPrefNames(prefs);
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Preferences:Get", prefs.toString()));
     }
 
+    public void destroy() {
+        GeckoAppShell.unregisterGeckoEventListener("Viewport:Update", this);
+        GeckoAppShell.unregisterGeckoEventListener("Viewport:PageSize", this);
+        GeckoAppShell.unregisterGeckoEventListener("Viewport:CalculateDisplayPort", this);
+        GeckoAppShell.unregisterGeckoEventListener("Checkerboard:Toggle", this);
+        GeckoAppShell.unregisterGeckoEventListener("Preferences:Data", this);
+    }
+
     DisplayPortMetrics getDisplayPort() {
         return mDisplayPort;
     }
 
     /* Informs Gecko that the screen size has changed. */
     private void sendResizeEventIfNecessary(boolean force) {
         DisplayMetrics metrics = GeckoApp.mAppContext.getDisplayMetrics();
         View view = mLayerController.getView();
--- a/mobile/android/base/gfx/LayerController.java
+++ b/mobile/android/base/gfx/LayerController.java
@@ -77,16 +77,20 @@ public class LayerController {
 
     public void setRoot(Layer layer) { mRootLayer = layer; }
 
     public void setLayerClient(GeckoLayerClient layerClient) {
         mLayerClient = layerClient;
         layerClient.setLayerController(this);
     }
 
+    public void destroy() {
+        mPanZoomController.destroy();
+    }
+
     public void setForceRedraw() {
         mForceRedraw = true;
     }
 
     public Layer getRoot()                        { return mRootLayer; }
     public LayerView getView()                    { return mView; }
     public Context getContext()                   { return mContext; }
     public ImmutableViewportMetrics getViewportMetrics()   { return mViewportMetrics; }
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -79,18 +79,18 @@ public class LayerView extends SurfaceVi
     }
 
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN)
             requestFocus();
 
         /** We need to manually hide FormAssistPopup because it is not a regular PopupWindow. */
-        if (GeckoApp.mFormAssistPopup != null)
-            GeckoApp.mFormAssistPopup.hide();
+        if (GeckoApp.mAppContext != null && GeckoApp.mAppContext.mFormAssistPopup != null)
+            GeckoApp.mAppContext.mFormAssistPopup.hide();
 
         return mTouchEventHandler.handleEvent(event);
     }
 
     @Override
     public boolean onHoverEvent(MotionEvent event) {
         return mTouchEventHandler.handleEvent(event);
     }
@@ -199,16 +199,20 @@ public class LayerView extends SurfaceVi
     public LayerRenderer getRenderer() {
         return mRenderer;
     }
 
     public void setListener(Listener listener) {
         mListener = listener;
     }
 
+    Listener getListener() {
+        return mListener;
+    }
+
     public GLController getGLController() {
         return mGLController;
     }
 
     /** Implementation of SurfaceHolder.Callback */
     public synchronized void surfaceChanged(SurfaceHolder holder, int format, int width,
                                             int height) {
         mGLController.surfaceChanged(width, height);
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -137,16 +137,23 @@ public class PanZoomController
         GeckoAppShell.registerGeckoEventListener(MESSAGE_PREFS_DATA, this);
 
         JSONArray prefs = new JSONArray();
         prefs.put(PREF_ZOOM_ANIMATION_FRAMES);
         Axis.addPrefNames(prefs);
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(MESSAGE_PREFS_GET, prefs.toString()));
     }
 
+    public void destroy() {
+        GeckoAppShell.unregisterGeckoEventListener(MESSAGE_ZOOM_RECT, this);
+        GeckoAppShell.unregisterGeckoEventListener(MESSAGE_ZOOM_PAGE, this);
+        GeckoAppShell.unregisterGeckoEventListener(MESSAGE_PREFS_DATA, this);
+        mSubscroller.destroy();
+    }
+
     // for debugging bug 713011; it can be taken out once that is resolved.
     private void checkMainThread() {
         if (mMainThread != Thread.currentThread()) {
             // log with full stack trace
             Log.e(LOGTAG, "Uh-oh, we're running on the wrong thread!", new Exception());
         }
     }
 
@@ -863,17 +870,17 @@ public class PanZoomController
         mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
         cancelTouch();
 
         return true;
     }
 
     @Override
     public boolean onScale(SimpleScaleGestureDetector detector) {
-        if (GeckoApp.mDOMFullScreen)
+        if (GeckoApp.mAppContext == null || GeckoApp.mAppContext.mDOMFullScreen)
             return false;
 
         if (mState != PanZoomState.PINCHING)
             return false;
 
         float prevSpan = detector.getPreviousSpan();
         if (FloatUtils.fuzzyEquals(prevSpan, 0.0f)) {
             // let's eat this one to avoid setting the new zoom to infinity (bug 711453)
--- a/mobile/android/base/ui/SubdocumentScrollHelper.java
+++ b/mobile/android/base/ui/SubdocumentScrollHelper.java
@@ -51,16 +51,22 @@ class SubdocumentScrollHelper implements
         mUiHandler = new Handler();
         mPendingDisplacement = new PointF();
 
         GeckoAppShell.registerGeckoEventListener(MESSAGE_PANNING_OVERRIDE, this);
         GeckoAppShell.registerGeckoEventListener(MESSAGE_CANCEL_OVERRIDE, this);
         GeckoAppShell.registerGeckoEventListener(MESSAGE_SCROLL_ACK, this);
     }
 
+    void destroy() {
+        GeckoAppShell.unregisterGeckoEventListener(MESSAGE_PANNING_OVERRIDE, this);
+        GeckoAppShell.unregisterGeckoEventListener(MESSAGE_CANCEL_OVERRIDE, this);
+        GeckoAppShell.unregisterGeckoEventListener(MESSAGE_SCROLL_ACK, this);
+    }
+
     boolean scrollBy(PointF displacement) {
         if (! mOverridePanning) {
             return false;
         }
 
         if (! mOverrideScrollAck) {
             mOverrideScrollPending = true;
             mPendingDisplacement.x += displacement.x;
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -154,16 +154,17 @@ var BrowserApp = {
     Services.obs.addObserver(this, "Browser:Quit", false);
     Services.obs.addObserver(this, "Preferences:Get", false);
     Services.obs.addObserver(this, "Preferences:Set", false);
     Services.obs.addObserver(this, "ScrollTo:FocusedInput", false);
     Services.obs.addObserver(this, "Sanitize:ClearData", false);
     Services.obs.addObserver(this, "PanZoom:PanZoom", false);
     Services.obs.addObserver(this, "FullScreen:Exit", false);
     Services.obs.addObserver(this, "Viewport:Change", false);
+    Services.obs.addObserver(this, "Viewport:Flush", false);
     Services.obs.addObserver(this, "Passwords:Init", false);
     Services.obs.addObserver(this, "FormHistory:Init", false);
     Services.obs.addObserver(this, "ToggleProfiling", false);
 
     Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
 
     function showFullScreenWarning() {
       NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short");
@@ -926,16 +927,18 @@ var BrowserApp = {
       this.scrollToFocusedInput(browser);
     } else if (aTopic == "Sanitize:ClearData") {
       this.sanitize(aData);
     } else if (aTopic == "FullScreen:Exit") {
       browser.contentDocument.mozCancelFullScreen();
     } else if (aTopic == "Viewport:Change") {
       if (this.isBrowserContentDocumentDisplayed())
         this.selectedTab.setViewport(JSON.parse(aData));
+    } else if (aTopic == "Viewport:Flush") {
+      this.displayedDocumentChanged();
     } else if (aTopic == "Passwords:Init") {
       let storage = Components.classes["@mozilla.org/login-manager/storage/mozStorage;1"].
         getService(Components.interfaces.nsILoginManagerStorage);
       storage.init();
 
       sendMessageToJava({gecko: { type: "Passwords:Init:Return" }});
       Services.obs.removeObserver(this, "Passwords:Init", false);
     } else if (aTopic == "FormHistory:Init") {
@@ -4438,16 +4441,18 @@ var ViewportHandler = {
     }
   },
 
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "Window:Resize":
         if (window.outerWidth == gScreenWidth && window.outerHeight == gScreenHeight)
           break;
+        if (window.outerWidth == 0 || window.outerHeight == 0)
+          break;
 
         let oldScreenWidth = gScreenWidth;
         gScreenWidth = window.outerWidth;
         gScreenHeight = window.outerHeight;
         let tabs = BrowserApp.tabs;
         for (let i = 0; i < tabs.length; i++)
           tabs[i].updateViewportSize(oldScreenWidth);
         break;
--- a/toolkit/components/microformats/tests/Makefile.in
+++ b/toolkit/components/microformats/tests/Makefile.in
@@ -4,26 +4,23 @@
 
 DEPTH     = ../../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
-include $(topsrcdir)/config/rules.mk
-
-_TEST_FILES = \
+MOCHITEST_FILES = \
 		test_Microformats.html \
 		test_Microformats_hCard.html \
 		test_Microformats_geo.html \
 		test_Microformats_hCalendar.html \
 		test_Microformats_adr.html \
 		test_Microformats_count.html \
 		test_Microformats_getters.html \
 		test_Microformats_negative.html \
 		test_Microformats_add.html \
 		test_framerecursion.html \
 		geo.html \
 		$(NULL)
 
-libs::	$(_TEST_FILES)
-	$(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/toolkit/components/microformats/tests
+include $(topsrcdir)/config/rules.mk
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -462,27 +462,19 @@
           this.hidePopup();
           this.style.removeProperty("opacity");
         }
       ]]>
       </handler>
       <handler event="popupshown" phase="target">
         this.setAttribute("panelopen", "true");
       </handler>
-      <handler event="popuphidden" phase="target"><![CDATA[
+      <handler event="popuphidden" phase="target">
         this.removeAttribute("panelopen");
-
-        if (this.state == "closed") {
-          // Destroy the widget to prevent the current state from being rendered
-          // briefly when the panel reopens.
-          this.hidden = true;
-          this.clientHeight; // flush layout
-          this.hidden = false;
-        }
-      ]]></handler>
+      </handler>
     </handlers>
   </binding>
 
   <binding id="tooltip" extends="chrome://global/content/bindings/popup.xml#popup-base">
     <content>
       <children>
         <xul:label class="tooltip-label" xbl:inherits="xbl:text=label" flex="1"/>
       </children>
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -42,16 +42,31 @@
 #define IME_FULLSCREEN_THRESHOLD_PREF "widget.ime.android.fullscreen_threshold"
 
 using namespace mozilla;
 
 NS_IMPL_THREADSAFE_ISUPPORTS0(nsFilePickerCallback)
 
 AndroidBridge *AndroidBridge::sBridge = 0;
 
+AndroidBridge::AndroidBridge()
+  : mLayerClient(NULL)
+  , mJavaVM(NULL)
+  , mJNIEnv(NULL)
+  , mThread(NULL)
+  , mJNIForCompositorThread(NULL)
+  , mCompositorThread(NULL)
+  , mCompositorJNICreationMutex("AndroidBridge.CompositorJNICreation")
+{
+}
+
+AndroidBridge::~AndroidBridge()
+{
+}
+
 AndroidBridge *
 AndroidBridge::ConstructBridge(JNIEnv *jEnv,
                                jclass jGeckoAppShellClass)
 {
     /* NSS hack -- bionic doesn't handle recursive unloads correctly,
      * because library finalizer functions are called with the dynamic
      * linker lock still held.  This results in a deadlock when trying
      * to call dlclose() while we're already inside dlclose().
@@ -71,18 +86,16 @@ bool
 AndroidBridge::Init(JNIEnv *jEnv,
                     jclass jGeckoAppShellClass)
 {
     ALOG_BRIDGE("AndroidBridge::Init");
     jEnv->GetJavaVM(&mJavaVM);
 
     AutoLocalJNIFrame jniFrame(jEnv);
 
-    mJNIEnv = nsnull;
-    mThread = nsnull;
     mOpenedGraphicsLibraries = false;
     mHasNativeBitmapAccess = false;
     mHasNativeWindowAccess = false;
     mHasNativeWindowFallback = false;
 
     mGeckoAppShellClass = (jclass) jEnv->NewGlobalRef(jGeckoAppShellClass);
 
     jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V");
@@ -1090,21 +1103,38 @@ AndroidBridge::GetShowPasswordSetting()
 
 void
 AndroidBridge::SetSurfaceView(jobject obj)
 {
     mSurfaceView.Init(obj);
 }
 
 void
-AndroidBridge::SetLayerClient(jobject obj)
+AndroidBridge::SetLayerClient(JNIEnv* env, jobject jobj)
 {
+    // if resetting is true, that means Android destroyed our GeckoApp activity
+    // and we had to recreate it, but all the Gecko-side things were not destroyed.
+    // We therefore need to link up the new java objects to Gecko, and that's what
+    // we do here.
+    bool resetting = (mLayerClient != NULL);
+
+    if (resetting) {
+        // clear out the old layer client
+        env->DeleteGlobalRef(mLayerClient->wrappedObject());
+        delete mLayerClient;
+        mLayerClient = NULL;
+    }
+
     AndroidGeckoLayerClient *client = new AndroidGeckoLayerClient();
-    client->Init(obj);
+    client->Init(env->NewGlobalRef(jobj));
     mLayerClient = client;
+
+    if (resetting) {
+        RegisterCompositor(env, true);
+    }
 }
 
 void
 AndroidBridge::ShowInputMethodPicker()
 {
     ALOG_BRIDGE("AndroidBridge::ShowInputMethodPicker");
 
     JNIEnv *env = GetJNIEnv();
@@ -1115,17 +1145,17 @@ AndroidBridge::ShowInputMethodPicker()
     env->CallStaticVoidMethod(mGeckoAppShellClass, jShowInputMethodPicker);
 }
 
 void *
 AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView &sview)
 {
     ALOG_BRIDGE("AndroidBridge::CallEglCreateWindowSurface");
 
-    JNIEnv *env = GetJNIForThread();        // called on the compositor thread
+    JNIEnv *env = GetJNIForCompositorThread();
     if (!env)
         return NULL;
 
     AutoLocalJNIFrame jniFrame(env);
 
     /*
      * This is basically:
      *
@@ -1165,33 +1195,38 @@ AndroidBridge::CallEglCreateWindowSurfac
     jint realSurface = env->GetIntField(surf, sfield);
 
     return (void*) realSurface;
 }
 
 static AndroidGLController sController;
 
 void
-AndroidBridge::RegisterCompositor()
+AndroidBridge::RegisterCompositor(JNIEnv *env, bool resetting)
 {
     ALOG_BRIDGE("AndroidBridge::RegisterCompositor");
-    JNIEnv *env = GetJNIForThread();    // called on the compositor thread
+    if (!env)
+        env = GetJNIForCompositorThread();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
 
     jmethodID registerCompositor = env->GetStaticMethodID(jLayerView, "registerCxxCompositor", "()Lorg/mozilla/gecko/gfx/GLController;");
 
     jobject glController = env->CallStaticObjectMethod(jLayerView, registerCompositor);
     if (jniFrame.CheckForException())
         return;
 
-    sController.Acquire(env, glController);
-    sController.SetGLVersion(2);
+    if (resetting) {
+        sController.Reacquire(env, glController);
+    } else {
+        sController.Acquire(env, glController);
+        sController.SetGLVersion(2);
+    }
 }
 
 EGLSurface
 AndroidBridge::ProvideEGLSurface()
 {
     sController.WaitForValidSurface();
     return sController.ProvideEGLSurface();
 }
@@ -2074,25 +2109,16 @@ AndroidBridge::SyncViewportInfo(const ns
 {
     AndroidGeckoLayerClient *client = mLayerClient;
     if (!client)
         return;
 
     client->SyncViewportInfo(aDisplayPort, aDisplayResolution, aLayersUpdated, aScrollOffset, aScaleX, aScaleY);
 }
 
-AndroidBridge::AndroidBridge()
-  : mLayerClient(NULL)
-{
-}
-
-AndroidBridge::~AndroidBridge()
-{
-}
-
 /* Implementation file */
 NS_IMPL_ISUPPORTS1(nsAndroidBridge, nsIAndroidBridge)
 
 nsAndroidBridge::nsAndroidBridge()
 {
 }
 
 nsAndroidBridge::~nsAndroidBridge()
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -123,16 +123,58 @@ public:
                                     (void*)pthread_self(), (void*)sBridge->mThread);
                 return nsnull;
             }
             return sBridge->mJNIEnv;
 
         }
         return nsnull;
     }
+
+    static JNIEnv* GetJNIForCompositorThread() {
+        if (NS_LIKELY(sBridge)) {
+            if (sBridge->mCompositorThread) {
+                if ((void*)pthread_self() != sBridge->mCompositorThread) {
+                    __android_log_print(ANDROID_LOG_ERROR, "AndroidBridge", "Non-compositor thread calling GetJNIForCompositorThread!");
+                    NS_ABORT();
+                    return NULL;
+                }
+                return sBridge->mJNIForCompositorThread;
+            }
+
+            // first time this is being called, so create the JNI object for the compositor thread.
+            // make sure to do it in a thread-safe manner in case two different threads call this function
+            // at the same time during startup.
+            MutexAutoLock lock(sBridge->mCompositorJNICreationMutex);
+
+            if (sBridge->mCompositorThread) {
+                // this means that another thread executed this function between the time we started executing
+                // it and the time we acquired the mutex. fail.
+                __android_log_print(ANDROID_LOG_ERROR, "AndroidBridge", "Two threads called GetJNIForCompositorThread on startup!");
+                NS_ABORT();
+                return NULL;
+            }
+
+            JavaVM *jVm = mozilla::AndroidBridge::GetVM();
+            if (!jVm) {
+                __android_log_print(ANDROID_LOG_ERROR, "AndroidBridge", "Null VM in GetJNIForCompositorThread");
+                return NULL;
+            }
+            JNIEnv* env;
+            if (jVm->AttachCurrentThread(&env, NULL)) {
+                __android_log_print(ANDROID_LOG_ERROR, "AndroidBridge", "Unable to attach to VM in GetJNIForCompositorThread");
+                return NULL;
+            }
+
+            sBridge->mCompositorThread = (void*)pthread_self();
+            sBridge->mJNIForCompositorThread = env;
+            return env;
+        }
+        return NULL;
+    }
     
     static jclass GetGeckoAppShellClass() {
         return sBridge->mGeckoAppShellClass;
     }
 
     // The bridge needs to be constructed via ConstructBridge first,
     // and then once the Gecko main thread is spun up (Gecko side),
     // SetMainThread should be called which will create the JNIEnv for
@@ -169,17 +211,17 @@ public:
     void DisableSensor(int aSensorType);
 
     void ReturnIMEQueryResult(const PRUnichar *aResult, PRUint32 aLen, int aSelStart, int aSelLen);
 
     void NotifyXreExit();
 
     void ScheduleRestart();
 
-    void SetLayerClient(jobject jobj);
+    void SetLayerClient(JNIEnv* env, jobject jobj);
     AndroidGeckoLayerClient &GetLayerClient() { return *mLayerClient; }
 
     void SetSurfaceView(jobject jobj);
     AndroidGeckoSurfaceView& SurfaceView() { return mSurfaceView; }
 
     bool GetHandlersForURL(const char *aURL, 
                              nsIMutableArray* handlersArray = nsnull,
                              nsIHandlerApp **aDefaultApp = nsnull,
@@ -255,17 +297,17 @@ public:
     bool GetShowPasswordSetting();
 
     void FireAndWaitForTracerEvent();
 
     /* See GLHelpers.java as to why this is needed */
     void *CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView& surfaceView);
 
     // Switch Java to composite with the Gecko Compositor thread
-    void RegisterCompositor();
+    void RegisterCompositor(JNIEnv* env = NULL, bool resetting = false);
     EGLSurface ProvideEGLSurface();
 
     bool GetStaticStringField(const char *classID, const char *field, nsAString &result, JNIEnv* env = nsnull);
 
     bool GetStaticIntField(const char *className, const char *fieldName, PRInt32* aInt, JNIEnv* env = nsnull);
 
     void SetKeepScreenOn(bool on);
 
@@ -364,16 +406,21 @@ protected:
 
     // the global JavaVM
     JavaVM *mJavaVM;
 
     // the JNIEnv for the main thread
     JNIEnv *mJNIEnv;
     void *mThread;
 
+    // the JNIEnv for the compositor thread and the lock used when creating it
+    JNIEnv *mJNIForCompositorThread;
+    void* mCompositorThread;
+    Mutex mCompositorJNICreationMutex;
+
     // the GeckoSurfaceView
     AndroidGeckoSurfaceView mSurfaceView;
 
     AndroidGeckoLayerClient *mLayerClient;
 
     // the GeckoAppShell java class
     jclass mGeckoAppShellClass;
 
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -75,17 +75,17 @@ NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_setSurfaceView(JNIEnv *jenv, jclass, jobject obj)
 {
     AndroidBridge::Bridge()->SetSurfaceView(jenv->NewGlobalRef(obj));
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_setLayerClient(JNIEnv *jenv, jclass, jobject obj)
 {
-    AndroidBridge::Bridge()->SetLayerClient(jenv->NewGlobalRef(obj));
+    AndroidBridge::Bridge()->SetLayerClient(jenv, obj);
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_GeckoAppShell_onLowMemory(JNIEnv *jenv, jclass jc)
 {
     if (nsAppShell::gAppShell) {
         nsAppShell::gAppShell->NotifyObservers(nsnull,
                                                "memory-pressure",
--- a/widget/android/AndroidJavaWrappers.cpp
+++ b/widget/android/AndroidJavaWrappers.cpp
@@ -658,45 +658,45 @@ AndroidGeckoSurfaceView::Draw2D(jobject 
     AutoLocalJNIFrame jniFrame(env, 0);
     env->CallVoidMethod(wrapped_obj, jDraw2DBufferMethod, buffer, stride);
 }
 
 void
 AndroidGeckoLayerClient::SetFirstPaintViewport(const nsIntPoint& aOffset, float aZoom, const nsIntRect& aPageRect, const gfx::Rect& aCssPageRect)
 {
     NS_ASSERTION(!isNull(), "SetFirstPaintViewport called on null layer client!");
-    JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
+    JNIEnv *env = AndroidBridge::GetJNIForCompositorThread();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
     return env->CallVoidMethod(wrapped_obj, jSetFirstPaintViewport, (float)aOffset.x, (float)aOffset.y, aZoom,
                                (float)aPageRect.x, (float)aPageRect.y, (float)aPageRect.XMost(), (float)aPageRect.YMost(),
                                aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost());
 }
 
 void
 AndroidGeckoLayerClient::SetPageRect(const gfx::Rect& aCssPageRect)
 {
     NS_ASSERTION(!isNull(), "SetPageRect called on null layer client!");
-    JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
+    JNIEnv *env = AndroidBridge::GetJNIForCompositorThread();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env, 0);
     return env->CallVoidMethod(wrapped_obj, jSetPageRect,
                                aCssPageRect.x, aCssPageRect.y, aCssPageRect.XMost(), aCssPageRect.YMost());
 }
 
 void
 AndroidGeckoLayerClient::SyncViewportInfo(const nsIntRect& aDisplayPort, float aDisplayResolution, bool aLayersUpdated,
                                           nsIntPoint& aScrollOffset, float& aScaleX, float& aScaleY)
 {
     NS_ASSERTION(!isNull(), "SyncViewportInfo called on null layer client!");
-    JNIEnv *env = GetJNIForThread();    // this is called on the compositor thread
+    JNIEnv *env = AndroidBridge::GetJNIForCompositorThread();
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
 
     jobject viewTransformJObj = env->CallObjectMethod(wrapped_obj, jSyncViewportInfoMethod,
                                                       aDisplayPort.x, aDisplayPort.y,
                                                       aDisplayPort.width, aDisplayPort.height,
--- a/widget/android/AndroidLayerViewWrapper.cpp
+++ b/widget/android/AndroidLayerViewWrapper.cpp
@@ -19,25 +19,27 @@ void AndroidEGLObject::Init(JNIEnv* aJEn
     jClass = reinterpret_cast<jclass>
         (aJEnv->NewGlobalRef(aJEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl")));
     jEGLSurfacePointerField = aJEnv->GetFieldID(jClass, "mEGLSurface", "I");
 }
 
 jmethodID AndroidGLController::jSetGLVersionMethod = 0;
 jmethodID AndroidGLController::jWaitForValidSurfaceMethod = 0;
 jmethodID AndroidGLController::jProvideEGLSurfaceMethod = 0;
+jmethodID AndroidGLController::jResumeCompositorIfValidMethod = 0;
 
 void
 AndroidGLController::Init(JNIEnv* aJEnv)
 {
     jclass jClass = reinterpret_cast<jclass>(aJEnv->NewGlobalRef(aJEnv->FindClass("org/mozilla/gecko/gfx/GLController")));
 
     jSetGLVersionMethod = aJEnv->GetMethodID(jClass, "setGLVersion", "(I)V");
     jProvideEGLSurfaceMethod = aJEnv->GetMethodID(jClass, "provideEGLSurface",
                                                   "()Ljavax/microedition/khronos/egl/EGLSurface;");
+    jResumeCompositorIfValidMethod = aJEnv->GetMethodID(jClass, "resumeCompositorIfValid", "()V");
     jWaitForValidSurfaceMethod = aJEnv->GetMethodID(jClass, "waitForValidSurface", "()V");
 }
 
 void
 AndroidGLController::Acquire(JNIEnv* aJEnv, jobject aJObj)
 {
     mJEnv = aJEnv;
     mThread = pthread_self();
@@ -47,16 +49,26 @@ AndroidGLController::Acquire(JNIEnv* aJE
 void
 AndroidGLController::SetGLVersion(int aVersion)
 {
     ASSERT_THREAD();
     AutoLocalJNIFrame jniFrame(mJEnv, 0);
     mJEnv->CallVoidMethod(mJObj, jSetGLVersionMethod, aVersion);
 }
 
+void
+AndroidGLController::Reacquire(JNIEnv *aJEnv, jobject aJObj)
+{
+    aJEnv->DeleteGlobalRef(mJObj);
+    mJObj = aJEnv->NewGlobalRef(aJObj);
+
+    AutoLocalJNIFrame jniFrame(aJEnv, 0);
+    aJEnv->CallVoidMethod(mJObj, jResumeCompositorIfValidMethod);
+}
+
 EGLSurface
 AndroidGLController::ProvideEGLSurface()
 {
     ASSERT_THREAD();
     AutoLocalJNIFrame jniFrame(mJEnv);
     jobject jObj = mJEnv->CallObjectMethod(mJObj, jProvideEGLSurfaceMethod);
     if (jniFrame.CheckForException())
         return NULL;
--- a/widget/android/AndroidLayerViewWrapper.h
+++ b/widget/android/AndroidLayerViewWrapper.h
@@ -17,23 +17,25 @@ public:
 typedef void* EGLSurface;
 
 class AndroidGLController {
 public:
     static void Init(JNIEnv* aJEnv);
 
     void Acquire(JNIEnv* aJEnv, jobject aJObj);
     void SetGLVersion(int aVersion);
+    void Reacquire(JNIEnv* aJEnv, jobject aJObj);
     EGLSurface ProvideEGLSurface();
     void WaitForValidSurface();
 
 private:
     static jmethodID jSetGLVersionMethod;
     static jmethodID jWaitForValidSurfaceMethod;
     static jmethodID jProvideEGLSurfaceMethod;
+    static jmethodID jResumeCompositorIfValidMethod;
 
     // the JNIEnv for the compositor thread
     JNIEnv* mJEnv;
     pthread_t mThread;
     jobject mJObj;
 };
 
 #endif
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2233,17 +2233,17 @@ nsWindow::GetIMEUpdatePreference()
 {
     return nsIMEUpdatePreference(true, true);
 }
 
 #ifdef MOZ_JAVA_COMPOSITOR
 void
 nsWindow::DrawWindowUnderlay(LayerManager* aManager, nsIntRect aRect)
 {
-    JNIEnv *env = GetJNIForThread();
+    JNIEnv *env = AndroidBridge::GetJNIForCompositorThread();
     NS_ABORT_IF_FALSE(env, "No JNI environment at DrawWindowUnderlay()!");
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
 
     AndroidGeckoLayerClient& client = AndroidBridge::Bridge()->GetLayerClient();
     if (!client.CreateFrame(&jniFrame, mLayerRendererFrame)) return;
@@ -2251,17 +2251,17 @@ nsWindow::DrawWindowUnderlay(LayerManage
     if (!mLayerRendererFrame.BeginDrawing(&jniFrame)) return;
     if (!mLayerRendererFrame.DrawBackground(&jniFrame)) return;
     if (!client.DeactivateProgram(&jniFrame)) return; // redundant, but in case somebody adds code after this...
 }
 
 void
 nsWindow::DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect)
 {
-    JNIEnv *env = GetJNIForThread();
+    JNIEnv *env = AndroidBridge::GetJNIForCompositorThread();
     NS_ABORT_IF_FALSE(env, "No JNI environment at DrawWindowOverlay()!");
     if (!env)
         return;
 
     AutoLocalJNIFrame jniFrame(env);
 
     NS_ABORT_IF_FALSE(!mLayerRendererFrame.isNull(),
                       "Frame should have been created in DrawWindowUnderlay()!");
--- a/xpcom/components/nsIClassInfo.idl
+++ b/xpcom/components/nsIClassInfo.idl
@@ -78,16 +78,17 @@ interface nsIClassInfo : nsISupports
     /**
      * Bitflags for 'flags' attribute.
      */
     const PRUint32 SINGLETON            = 1 << 0;
     const PRUint32 THREADSAFE           = 1 << 1;
     const PRUint32 MAIN_THREAD_ONLY     = 1 << 2;
     const PRUint32 DOM_OBJECT           = 1 << 3;
     const PRUint32 PLUGIN_OBJECT        = 1 << 4;
+    const PRUint32 SINGLETON_CLASSINFO  = 1 << 5;
 
     /**
      * 'flags' attribute bitflag: whether objects of this type implement
      * nsIContent.
      */
     const PRUint32 CONTENT_NODE         = 1 << 6;
     
     // The high order bit is RESERVED for consumers of these flags. 
--- a/xpcom/glue/nsIClassInfoImpl.h
+++ b/xpcom/glue/nsIClassInfoImpl.h
@@ -111,17 +111,17 @@ private:
   extern NS_IMETHODIMP NS_CI_INTERFACE_GETTER_NAME(_class)                    \
      (PRUint32 * NS_OUTPARAM, nsIID *** NS_OUTPARAM);
 
 #define NS_IMPL_CLASSINFO(_class, _getlanguagehelper, _flags, _cid)     \
   NS_DECL_CI_INTERFACE_GETTER(_class)                                   \
   static const GenericClassInfo::ClassInfoData k##_class##ClassInfoData = { \
     NS_CI_INTERFACE_GETTER_NAME(_class),                                \
     _getlanguagehelper,                                                 \
-    _flags,                                                             \
+    _flags | nsIClassInfo::SINGLETON_CLASSINFO,                         \
     _cid,                                                               \
   };                                                                    \
   static char k##_class##ClassInfoDataPlace[sizeof(GenericClassInfo)];  \
   nsIClassInfo* NS_CLASSINFO_NAME(_class) = NULL;
 
 #define NS_IMPL_QUERY_CLASSINFO(_class)                                       \
   if ( aIID.Equals(NS_GET_IID(nsIClassInfo)) ) {                              \
     if (!NS_CLASSINFO_NAME(_class))                                           \