Merge last PGO-green changeset of mozilla-inbound to mozilla-central
authorEd Morley <emorley@mozilla.com>
Fri, 28 Sep 2012 15:49:57 +0100
changeset 108495 10279bd74ee0a5504ac2bfcd5efd97794f3bf6e4
parent 108449 7fffa1ef35d926fb1961dc0c0b274aa79309ca95 (current diff)
parent 108494 1949b59f3d12ab81b7c7bcec5bfb9fd8a17faf69 (diff)
child 108496 cdd4506bc66c3752620dd608893db2938df9e4b0
child 108599 7d4e659d6fbfb3cdfa7431377d1d710c3dcc64d9
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
Merge last PGO-green changeset of mozilla-inbound to mozilla-central
extensions/cookie/test/test_app_uninstall.html
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -489,16 +489,17 @@
 
 @BINPATH@/components/Activities.manifest
 @BINPATH@/components/ActivityOptions.js
 @BINPATH@/components/ActivityProxy.js
 @BINPATH@/components/ActivityRequestHandler.js
 @BINPATH@/components/ActivityWrapper.js
 
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 @BINPATH@/components/AppProtocolHandler.js
 @BINPATH@/components/AppProtocolHandler.manifest
 
 @BINPATH@/components/Payment.js
 @BINPATH@/components/PaymentFlowInfo.js
 @BINPATH@/components/PaymentRequestInfo.js
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -3646,18 +3646,20 @@ let SessionStoreInternal = {
 
     // If crash recovery is disabled, we only want to resume with pinned tabs
     // if we crash.
     let pinnedOnly = this._loadState == STATE_RUNNING && !this._resume_from_crash;
 
     TelemetryStopwatch.start("FX_SESSION_RESTORE_COLLECT_DATA_MS");
 
     var oState = this._getCurrentState(aUpdateAll, pinnedOnly);
-    if (!oState)
+    if (!oState) {
+      TelemetryStopwatch.cancel("FX_SESSION_RESTORE_COLLECT_DATA_MS");
       return;
+    }
 
 #ifndef XP_MACOSX
     // We want to restore closed windows that are marked with _shouldRestore.
     // We're doing this here because we want to control this only when saving
     // the file.
     while (oState._closedWindows.length) {
       let i = oState._closedWindows.length - 1;
       if (oState._closedWindows[i]._shouldRestore) {
@@ -4424,33 +4426,34 @@ let SessionStoreInternal = {
   /**
    * write file to disk
    * @param aFile
    *        nsIFile
    * @param aData
    *        String data
    */
   _writeFile: function ssi_writeFile(aFile, aData) {
-    TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_MS");
+    let refObj = {};
+    TelemetryStopwatch.start("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
     // Initialize the file output stream.
     var ostream = Cc["@mozilla.org/network/safe-file-output-stream;1"].
                   createInstance(Ci.nsIFileOutputStream);
     ostream.init(aFile, 0x02 | 0x08 | 0x20, 0600, ostream.DEFER_OPEN);
 
     // Obtain a converter to convert our data to a UTF-8 encoded input stream.
     var converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                     createInstance(Ci.nsIScriptableUnicodeConverter);
     converter.charset = "UTF-8";
 
     // Asynchronously copy the data to the file.
     var istream = converter.convertToInputStream(aData);
     var self = this;
     NetUtil.asyncCopy(istream, ostream, function(rc) {
       if (Components.isSuccessCode(rc)) {
-        TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_MS");
+        TelemetryStopwatch.finish("FX_SESSION_RESTORE_WRITE_FILE_MS", refObj);
         Services.obs.notifyObservers(null,
                                      "sessionstore-state-write-complete",
                                      "");
       }
     });
   }
 };
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -482,16 +482,17 @@
 
 @BINPATH@/components/PermissionSettings.js
 @BINPATH@/components/PermissionSettings.manifest
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
 @BINPATH@/components/AlarmsManager.js
 @BINPATH@/components/AlarmsManager.manifest
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 #ifdef ENABLE_MARIONETTE
 @BINPATH@/chrome/marionette@JAREXT@
 @BINPATH@/chrome/marionette.manifest
 @BINPATH@/components/MarionetteComponents.manifest
 @BINPATH@/components/marionettecomponent.js
 #endif
--- a/browser/modules/SignInToWebsite.jsm
+++ b/browser/modules/SignInToWebsite.jsm
@@ -21,27 +21,39 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 function log(...aMessageArgs) {
   Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs));
 }
 
 let SignInToWebsiteUX = {
 
   init: function SignInToWebsiteUX_init() {
+
+    /*
+     * bug 793906 - temporarily disabling desktop UI so we can
+     * focus on b2g without worrying about desktop as well
+     *
     Services.obs.addObserver(this, "identity-request", false);
     Services.obs.addObserver(this, "identity-auth", false);
     Services.obs.addObserver(this, "identity-auth-complete", false);
     Services.obs.addObserver(this, "identity-login-state-changed", false);
+     */
   },
 
   uninit: function SignInToWebsiteUX_uninit() {
+    /*
+     * As above:
+     * bug 793906 - temporarily disabling desktop UI so we can
+     * focus on b2g without worrying about desktop as well
+     *
     Services.obs.removeObserver(this, "identity-request");
     Services.obs.removeObserver(this, "identity-auth");
     Services.obs.removeObserver(this, "identity-auth-complete");
     Services.obs.removeObserver(this, "identity-login-state-changed");
+     */
   },
 
   observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) {
     log("observe: received", aTopic, "with", aData, "for", aSubject);
     let options = null;
     if (aSubject) {
       options = aSubject.wrappedJSObject;
     }
--- a/browser/modules/test/Makefile.in
+++ b/browser/modules/test/Makefile.in
@@ -9,17 +9,18 @@ VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
                  browser_NetworkPrioritizer.js \
                  browser_TelemetryTimestamps.js \
-                 browser_SignInToWebsite.js \
+                 # bug 793906 - temporarily disabling desktop UI while working on b2g
+                 # browser_SignInToWebsite.js \
                  $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 _BROWSER_FILES += \
                  browser_taskbar_preview.js \
                  $(NULL)
 endif
 
--- a/configure.in
+++ b/configure.in
@@ -4975,28 +4975,25 @@ fi
 AC_SUBST(MOZ_ENABLE_GNOME_COMPONENT)
 
 dnl ========================================================
 dnl = libgnomeui support module
 dnl ========================================================
 
 if test "$MOZ_ENABLE_GTK2"
 then
-    MOZ_ENABLE_GNOMEUI=1
-
-    MOZ_ARG_DISABLE_BOOL(gnomeui,
-    [  --disable-gnomeui       Disable libgnomeui support (default: auto, optional at runtime) ],
-        MOZ_ENABLE_GNOMEUI=,
-        MOZ_ENABLE_GNOMEUI=force)
+    MOZ_ARG_ENABLE_BOOL(gnomeui,
+    [  --enable-gnomeui        Enable libgnomeui instead of GIO & GTK for icon theme support ],
+        MOZ_ENABLE_GNOMEUI=force,
+        MOZ_ENABLE_GNOMEUI=)
 
     if test "$MOZ_ENABLE_GNOMEUI"
     then
         PKG_CHECK_MODULES(MOZ_GNOMEUI, libgnomeui-2.0 >= $GNOMEUI_VERSION,
         [
-            MOZ_GNOMEUI_LIBS=`echo $MOZ_GNOMEUI_LIBS | sed 's/-llinc\>//'`
             MOZ_ENABLE_GNOMEUI=1
         ],[
             if test "$MOZ_ENABLE_GNOMEUI" = "force"
             then
                 AC_MSG_ERROR([* * * Could not find libgnomeui-2.0 >= $GNOMEUI_VERSION])
             fi
             MOZ_ENABLE_GNOMEUI=
         ])
@@ -5004,17 +5001,16 @@ then
 
     if test "$MOZ_ENABLE_GNOMEUI"; then
         AC_DEFINE(MOZ_ENABLE_GNOMEUI)
     fi
 fi
 
 AC_SUBST(MOZ_ENABLE_GNOMEUI)
 AC_SUBST(MOZ_GNOMEUI_CFLAGS)
-AC_SUBST(MOZ_GNOMEUI_LIBS)
 
 dnl ========================================================
 dnl = dbus support
 dnl ========================================================
 
 if test "$MOZ_ENABLE_GTK2" -o "$MOZ_ENABLE_QT"
 then
     MOZ_ENABLE_DBUS=1
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -55,16 +55,17 @@ EXPORTS = \
   nsIScriptContext.h	\
   nsIScriptExternalNameSet.h \
   nsIScriptGlobalObject.h \
   nsIScriptGlobalObjectOwner.h \
   nsIScriptNameSpaceManager.h \
   nsIScriptObjectPrincipal.h \
   nsIScriptRuntime.h \
   nsIScriptTimeoutHandler.h \
+  nsJSUtils.h \
   nsPIDOMWindow.h \
   nsPIWindowRoot.h \
   nsFocusManager.h \
   nsWrapperCache.h \
   nsWrapperCacheInlines.h \
   nsContentPermissionHelper.h \
   nsStructuredCloneContainer.h \
   nsWindowMemoryReporter.h \
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -147,17 +147,17 @@ let DOMContactManager = {
           function() { mm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this),
           function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this)
         );
         break;
       case "Contacts:GetSimContacts":
         mRIL.getICCContacts(
           msg.options.contactType,
           function (aErrorMsg, aType, aContacts) {
-            if (aErrorMsg) {
+            if (aErrorMsg !== 'undefined') {
               mm.sendAsyncMessage("Contacts:GetSimContacts:Return:KO",
                                   {requestID: msg.requestID,
                                    errorMsg: aErrorMsg});
             } else {
               mm.sendAsyncMessage("Contacts:GetSimContacts:Return:OK",
                                   {requestID: msg.requestID,
                                    contacts: aContacts});
             }
--- a/dom/network/interfaces/Makefile.in
+++ b/dom/network/interfaces/Makefile.in
@@ -13,16 +13,18 @@ XPIDL_MODULE = dom_network
 
 include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
   nsIDOMNavigatorNetwork.idl \
   nsIDOMConnection.idl \
   nsIDOMUSSDReceivedEvent.idl \
   nsIDOMTCPSocket.idl \
+  nsITCPSocketParent.idl \
+  nsITCPSocketChild.idl \
   nsIDOMDataErrorEvent.idl \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 XPIDLSRCS += \
   nsIDOMMobileConnection.idl \
   nsIMobileConnectionProvider.idl \
   nsINavigatorMobileConnection.idl \
--- a/dom/network/interfaces/nsIDOMTCPSocket.idl
+++ b/dom/network/interfaces/nsIDOMTCPSocket.idl
@@ -169,16 +169,40 @@ interface nsIDOMTCPSocket : nsISupports
    * close.
    *
    * If onerror was not called before onclose, then either side cleanly
    * closed the connection.
    */
   attribute jsval onclose;
 };
 
+/*
+ * Internal interfaces for use in cross-process socket implementation.
+ * Needed to account for multiple possible types that can be provided to
+ * the socket callbacks as arguments.
+ */
+[scriptable, uuid(fff9ed4a-5e94-4fc0-84e4-7f4d35d0a26c)]
+interface nsITCPSocketInternal : nsISupports {
+  // Trigger the callback for |type| and provide an Error() object with the given data
+  void callListenerError(in DOMString type, in DOMString message, in DOMString filename,
+                         in uint32_t lineNumber, in uint32_t columnNumber);
+
+  // Trigger the callback for |type| and provide a string argument
+  void callListenerData(in DOMString type, in DOMString data);
+
+  // Trigger the callback for |type| and provide a Uint8Array argument
+  void callListenerArrayBuffer(in DOMString type, in jsval data);
+
+  // Trigger the callback for |type| with no argument
+  void callListenerVoid(in DOMString type);
+
+  // Update the DOM object's readyState and bufferedAmount values with the provided data
+  void updateReadyStateAndBuffered(in DOMString readyState, in uint32_t bufferedAmount);
+};
+
 /**
  * nsITCPSocketEvent is the event object which is passed as the
  * first argument to all the event handler callbacks. It contains
  * the socket that was associated with the event, the type of event,
  * and the data associated with the event (if any).
  */
 
 [scriptable, uuid(0f2abcca-b483-4539-a3e8-345707f75c44)]
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsITCPSocketChild.idl
@@ -0,0 +1,25 @@
+/* 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 "domstubs.idl"
+
+interface nsITCPSocketInternal;
+interface nsIDOMWindow;
+
+// Interface to allow the content process socket to reach the IPC bridge.
+[scriptable, uuid(a589d96f-7e09-4edf-a01a-eb4951f42f37)]
+interface nsITCPSocketChild : nsISupports
+{
+  // Tell the chrome process to open a corresponding connection with the given parameters
+  [implicit_jscontext]
+  void open(in nsITCPSocketInternal socket, in DOMString host,
+            in unsigned short port, in boolean ssl, in DOMString binaryType,
+            in nsIDOMWindow window, in jsval socketVal);
+
+  // Tell the chrome process to perform equivalent operations to all following methods
+  [implicit_jscontext] void send(in jsval data);
+  void resume();
+  void suspend();
+  void close();
+};
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsITCPSocketParent.idl
@@ -0,0 +1,39 @@
+/* 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 "domstubs.idl"
+
+interface nsIDOMTCPSocket;
+
+// Interface required to allow the TCP socket object in the parent process
+// to talk to the parent IPC actor
+[scriptable, uuid(4e7246c6-a8b3-426d-9c17-76dab1e1e14a)]
+interface nsITCPSocketParent : nsISupports
+{
+  [implicit_jscontext] void initJS(in jsval intermediary);
+
+  // Trigger a callback in the content process for |type|, providing a serialized
+  // argument of |data|, and update the child's readyState and bufferedAmount values
+  // with the given values.
+  [implicit_jscontext] void sendCallback(in DOMString type,
+                                         in jsval data,
+                                         in DOMString readyState,
+                                         in uint32_t bufferedAmount);
+};
+
+// Intermediate class to handle sending multiple possible data types
+// and kicking off the chrome process socket object's connection.
+[scriptable, uuid(afa42841-a6cb-4a91-912f-93099f6a3d18)]
+interface nsITCPSocketIntermediary : nsISupports {
+  // Open the connection to the server with the given parameters
+  nsIDOMTCPSocket open(in nsITCPSocketParent parent,
+                       in DOMString host, in unsigned short port,
+                       in boolean useSSL, in DOMString binaryType);
+
+  // Send a basic string along the connection
+  void sendString(in DOMString data);
+
+  // Send a typed array
+  void sendArrayBuffer(in jsval data);
+};
--- a/dom/network/src/Makefile.in
+++ b/dom/network/src/Makefile.in
@@ -11,34 +11,39 @@ include $(DEPTH)/config/autoconf.mk
 
 LIBRARY_NAME     = dom_network_s
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 FAIL_ON_WARNINGS := 1
 
 EXTRA_COMPONENTS = \
 	TCPSocket.js \
+	TCPSocketParentIntermediary.js \
 	TCPSocket.manifest \
 	$(NULL)
 
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/network
 
 EXPORTS_mozilla/dom/network = \
   Utils.h \
   Types.h \
   Constants.h \
+  TCPSocketChild.h \
+  TCPSocketParent.h \
   $(NULL)
 
 CPPSRCS = \
   Connection.cpp \
   Utils.cpp \
   USSDReceivedEvent.cpp \
   DataErrorEvent.cpp \
+  TCPSocketParent.cpp \
+  TCPSocketChild.cpp \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 CPPSRCS += \
   MobileConnection.cpp \
   $(NULL)
 endif
 
new file mode 100644
--- /dev/null
+++ b/dom/network/src/PTCPSocket.ipdl
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using mozilla::void_t;
+
+struct JSError {
+  nsString message;
+  nsString filename;
+  uint32_t lineNumber;
+  uint32_t columnNumber;
+};
+
+union SendableData {
+  uint8_t[];
+  nsString;
+};
+
+union CallbackData {
+  void_t;
+  SendableData;
+  JSError;
+};
+
+namespace mozilla {
+namespace net {
+
+//-------------------------------------------------------------------
+protocol PTCPSocket
+{
+  manager PNecko;
+
+parent:
+  Data(SendableData data);
+  Suspend();
+  Resume();
+  Close();
+  RequestDelete();
+
+child:
+  Callback(nsString type, CallbackData data,
+           nsString readyState, uint32_t bufferedAmount);
+  __delete__();
+};
+
+
+} // namespace net
+} // namespace mozilla
+
--- a/dom/network/src/TCPSocket.js
+++ b/dom/network/src/TCPSocket.js
@@ -137,16 +137,22 @@ TCPSocket.prototype = {
   // Output stream machinery
   _multiplexStream: null,
   _multiplexStreamCopier: null,
 
   _asyncCopierActive: false,
   _waitingForDrain: false,
   _suspendCount: 0,
 
+  // Reported parent process buffer
+  _bufferedAmount: 0,
+
+  // IPC socket actor
+  _socketBridge: null,
+
   // Public accessors.
   get readyState() {
     return this._readyState;
   },
   get binaryType() {
     return this._binaryType;
   },
   get host() {
@@ -154,16 +160,19 @@ TCPSocket.prototype = {
   },
   get port() {
     return this._port;
   },
   get ssl() {
     return this._ssl;
   },
   get bufferedAmount() {
+    if (this._inChild) {
+      return this._bufferedAmount;
+    }
     return this._multiplexStream.available();
   },
   get onopen() {
     return this._onopen;
   },
   set onopen(f) {
     this._onopen = f;
   },
@@ -248,18 +257,46 @@ TCPSocket.prototype = {
 
   callListener: function ts_callListener(type, data) {
     if (!this[type])
       return;
 
     this[type].call(null, new TCPSocketEvent(type, this, data || ""));
   },
 
+  /* nsITCPSocketInternal methods */
+  callListenerError: function ts_callListenerError(type, message, filename,
+                                                   lineNumber, columnNumber) {
+    this.callListener(type, new Error(message, filename, lineNumber, columnNumber));
+  },
+
+  callListenerData: function ts_callListenerString(type, data) {
+    this.callListener(type, data);
+  },
+
+  callListenerArrayBuffer: function ts_callListenerArrayBuffer(type, data) {
+    this.callListener(type, data);
+  },
+
+  callListenerVoid: function ts_callListenerVoid(type) {
+    this.callListener(type);
+  },
+
+  updateReadyStateAndBuffered: function ts_setReadyState(readyState, bufferedAmount) {
+    this._readyState = readyState;
+    this._bufferedAmount = bufferedAmount;
+  },
+  /* end nsITCPSocketInternal methods */
+
+  initWindowless: function ts_initWindowless() {
+    return Services.prefs.getBoolPref("dom.mozTCPSocket.enabled");
+  },
+
   init: function ts_init(aWindow) {
-    if (!Services.prefs.getBoolPref("dom.mozTCPSocket.enabled"))
+    if (!this.initWindowless())
       return null;
 
     let principal = aWindow.document.nodePrincipal;
     let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
                    .getService(Ci.nsIScriptSecurityManager);
 
     let perm = principal == secMan.getSystemPrincipal()
                  ? Ci.nsIPermissionManager.ALLOW_ACTION
@@ -296,25 +333,33 @@ TCPSocket.prototype = {
         // Clean up our socket
         this.close();
       }
     }
   },
 
   // nsIDOMTCPSocket
   open: function ts_open(host, port, options) {
+    if (!this.initWindowless())
+      return null;
+
+    this._inChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                       .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+    LOG("content process: " + (this._inChild ? "true" : "false") + "\n");
+
     // in the testing case, init won't be called and
     // hasPrivileges will be null. We want to proceed to test.
     if (this._hasPrivileges !== true && this._hasPrivileges !== null) {
       throw new Error("TCPSocket does not have permission in this context.\n");
     }
     let that = new TCPSocket();
 
     that.useWin = this.useWin;
     that.innerWindowID = this.innerWindowID;
+    that._inChild = this._inChild;
 
     LOG("window init: " + that.innerWindowID);
     Services.obs.addObserver(that, "inner-window-destroyed", true);
 
     LOG("startup called\n");
     LOG("Host info: " + host + ":" + port + "\n");
 
     that._readyState = kCONNECTING;
@@ -326,16 +371,24 @@ TCPSocket.prototype = {
       } else {
           that._ssl = false;
       }
       that._binaryType = options.binaryType || that._binaryType;
     }
 
     LOG("SSL: " + that.ssl + "\n");
 
+    if (this._inChild) {
+      that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"]
+                             .createInstance(Ci.nsITCPSocketChild);
+      that._socketBridge.open(that, host, port, !!that._ssl,
+                              that._binaryType, this.useWin, this);
+      return that;
+    }
+
     let transport = that._transport = this._createTransport(host, port, that._ssl);
     transport.setEventSink(that, Services.tm.currentThread);
     transport.securityCallbacks = new SecurityCallbacks(that);
 
     that._socketInputStream = transport.openInputStream(0, 0, 0);
     that._socketOutputStream = transport.openOutputStream(
       Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
 
@@ -367,27 +420,36 @@ TCPSocket.prototype = {
 
   close: function ts_close() {
     if (this._readyState === kCLOSED || this._readyState === kCLOSING)
       return;
 
     LOG("close called\n");
     this._readyState = kCLOSING;
 
+    if (this._inChild) {
+      this._socketBridge.close();
+      return;
+    }
+
     if (!this._multiplexStream.count) {
       this._socketOutputStream.close();
     }
     this._socketInputStream.close();
   },
 
   send: function ts_send(data) {
     if (this._readyState !== kOPEN) {
       throw new Error("Socket not open.");
     }
 
+    if (this._inChild) {
+      this._socketBridge.send(data);
+    }
+
     let new_stream = new StringInputStream();
     if (this._binaryType === "arraybuffer") {
       // It would be really nice if there were an interface
       // that took an ArrayBuffer like StringInputStream takes
       // a string. There is one, but only in C++ and not exposed
       // to js as far as I can tell
       var dataLen = data.length;
       var offset = 0;
@@ -400,52 +462,66 @@ TCPSocket.prototype = {
 
         var fragment = data.subarray(offset, offset + fragmentLen);
         offset += fragmentLen;
         result += String.fromCharCode.apply(null, fragment);
       }
       data = result;
     }
     var newBufferedAmount = this.bufferedAmount + data.length;
+    var bufferNotFull = newBufferedAmount < BUFFER_SIZE;
+    if (this._inChild) {
+      return bufferNotFull;
+    }
+
     new_stream.setData(data, data.length);
     this._multiplexStream.appendStream(new_stream);
 
     if (newBufferedAmount >= BUFFER_SIZE) {
       // If we buffered more than some arbitrary amount of data,
       // (65535 right now) we should tell the caller so they can
       // wait until ondrain is called if they so desire. Once all the
       //buffered data has been written to the socket, ondrain is
       // called.
       this._waitingForDrain = true;
     }
 
     this._ensureCopying();
-    return newBufferedAmount < BUFFER_SIZE;
+    return bufferNotFull;
   },
 
   suspend: function ts_suspend() {
+    if (this._inChild) {
+      this._socketBridge.suspend();
+      return;
+    }
+
     if (this._inputStreamPump) {
       this._inputStreamPump.suspend();
     } else {
       ++this._suspendCount;
     }
   },
 
   resume: function ts_resume() {
+    if (this._inChild) {
+      this._socketBridge.resume();
+      return;
+    }
+
     if (this._inputStreamPump) {
       this._inputStreamPump.resume();
     } else {
       --this._suspendCount;
     }
   },
 
   // nsITransportEventSink (Triggered by transport.setEventSink)
   onTransportStatus: function ts_onTransportStatus(
     transport, status, progress, max) {
-
     if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
       this._readyState = kOPEN;
       this.callListener("onopen");
 
       this._inputStreamPump = new InputStreamPump(
         this._socketInputStream, -1, -1, 0, 0, false
       );
 
@@ -521,16 +597,17 @@ TCPSocket.prototype = {
       Ci.nsIObserver,
       Ci.nsISupportsWeakReference
     ],
     flags: Ci.nsIClassInfo.DOM_OBJECT,
   }),
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIDOMTCPSocket,
+    Ci.nsITCPSocketInternal,
     Ci.nsIDOMGlobalPropertyInitializer,
     Ci.nsIObserver,
     Ci.nsISupportsWeakReference
   ])
 }
 
 
 function SecurityCallbacks(socket) {
--- a/dom/network/src/TCPSocket.manifest
+++ b/dom/network/src/TCPSocket.manifest
@@ -1,4 +1,8 @@
 # TCPSocket.js
 component {cda91b22-6472-11e1-aa11-834fec09cd0a} TCPSocket.js
 contract @mozilla.org/tcp-socket;1 {cda91b22-6472-11e1-aa11-834fec09cd0a}
 category JavaScript-navigator-property mozTCPSocket @mozilla.org/tcp-socket;1
+
+# TCPSocketParentIntermediary.js
+component {afa42841-a6cb-4a91-912f-93099f6a3d18} TCPSocketParentIntermediary.js
+contract @mozilla.org/tcp-socket-intermediary;1 {afa42841-a6cb-4a91-912f-93099f6a3d18}
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketChild.cpp
@@ -0,0 +1,209 @@
+/* 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 "TCPSocketChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/dom/PBrowserChild.h"
+#include "mozilla/dom/TabChild.h"
+#include "nsIDOMTCPSocket.h"
+#include "nsJSUtils.h"
+#include "nsContentUtils.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+
+using mozilla::net::gNeckoChild;
+
+namespace IPC {
+
+bool
+DeserializeUint8Array(JSRawObject aObj,
+                      const nsTArray<uint8_t>& aBuffer,
+                      jsval* aVal)
+{
+  JSContext* cx = nsContentUtils::GetSafeJSContext();
+  JSAutoRequest ar(cx);
+  JSAutoCompartment ac(cx, aObj);
+
+  JSObject* obj = JS_NewArrayBuffer(cx, aBuffer.Length());
+  if (!obj)
+    return false;
+  uint8_t* data = JS_GetArrayBufferData(obj, cx);
+  if (!data)
+    return false;
+  memcpy(data, aBuffer.Elements(), aBuffer.Length());
+  JSObject* arr = JS_NewUint8ArrayWithBuffer(cx, obj, 0, aBuffer.Length());
+  if (!arr)
+    return false;
+  *aVal = OBJECT_TO_JSVAL(arr);
+  return true;
+}
+
+} // namespace IPC
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_1(TCPSocketChildBase, mSocket)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketChildBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketChildBase)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketChildBase)
+  NS_INTERFACE_MAP_ENTRY(nsITCPSocketChild)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TCPSocketChildBase::TCPSocketChildBase()
+: mIPCOpen(false)
+{
+}
+
+TCPSocketChildBase::~TCPSocketChildBase()
+{
+}
+
+NS_IMETHODIMP_(nsrefcnt) TCPSocketChild::Release(void)
+{
+  nsrefcnt refcnt = TCPSocketChildBase::Release();
+  if (refcnt == 1 && mIPCOpen) {
+    PTCPSocketChild::SendRequestDelete();
+    return 1;
+  }
+  return refcnt;
+}
+
+TCPSocketChild::TCPSocketChild()
+: mSocketObj(nullptr)
+{
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost,
+                     uint16_t aPort, bool aUseSSL, const nsAString& aBinaryType,
+                     nsIDOMWindow* aWindow, const JS::Value& aSocketObj,
+                     JSContext* aCx)
+{
+  mSocket = aSocket;
+  MOZ_ASSERT(aSocketObj.isObject());
+  mSocketObj = &aSocketObj.toObject();
+  AddIPDLReference();
+  gNeckoChild->SendPTCPSocketConstructor(this, nsString(aHost), aPort,
+                                         aUseSSL, nsString(aBinaryType),
+                                         GetTabChildFrom(aWindow));
+  return NS_OK;
+}
+
+void
+TCPSocketChildBase::ReleaseIPDLReference()
+{
+  MOZ_ASSERT(mIPCOpen);
+  mIPCOpen = false;
+  this->Release();
+}
+
+void
+TCPSocketChildBase::AddIPDLReference()
+{
+  MOZ_ASSERT(!mIPCOpen);
+  mIPCOpen = true;
+  this->AddRef();
+}
+
+TCPSocketChild::~TCPSocketChild()
+{
+}
+
+bool
+TCPSocketChild::RecvCallback(const nsString& aType,
+                             const CallbackData& aData,
+                             const nsString& aReadyState,
+                             const uint32_t& aBuffered)
+{
+  if (NS_FAILED(mSocket->UpdateReadyStateAndBuffered(aReadyState, aBuffered)))
+    NS_ERROR("Shouldn't fail!");
+
+  nsresult rv = NS_ERROR_FAILURE;
+  if (aData.type() == CallbackData::Tvoid_t) {
+    rv = mSocket->CallListenerVoid(aType);
+
+  } else if (aData.type() == CallbackData::TJSError) {
+    const JSError& err(aData.get_JSError());
+    rv = mSocket->CallListenerError(aType, err.message(), err.filename(),
+                                   err.lineNumber(), err.columnNumber());
+
+  } else if (aData.type() == CallbackData::TSendableData) {
+    const SendableData& data = aData.get_SendableData();
+
+    if (data.type() == SendableData::TArrayOfuint8_t) {
+      jsval val;
+      IPC::DeserializeUint8Array(mSocketObj, data.get_ArrayOfuint8_t(), &val);
+      rv = mSocket->CallListenerArrayBuffer(aType, val);
+
+    } else if (data.type() == SendableData::TnsString) {
+      rv = mSocket->CallListenerData(aType, data.get_nsString());
+
+    } else {
+      MOZ_NOT_REACHED("Invalid callback data type!");
+    }
+
+  } else {
+    MOZ_NOT_REACHED("Invalid callback type!");
+  }
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Suspend()
+{
+  SendSuspend();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Resume()
+{
+  SendResume();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Close()
+{
+  SendClose();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+TCPSocketChild::Send(const JS::Value& aData, JSContext* aCx)
+{
+  if (aData.isString()) {
+    JSString* jsstr = aData.toString();
+    nsDependentJSString str;
+    bool ok = str.init(aCx, jsstr);
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+    SendData(str);
+
+  } else {
+    NS_ENSURE_TRUE(aData.isObject(), NS_ERROR_FAILURE);
+    JSObject* obj = &aData.toObject();
+    NS_ENSURE_TRUE(JS_IsTypedArrayObject(obj, aCx), NS_ERROR_FAILURE);
+    NS_ENSURE_TRUE(JS_IsUint8Array(obj, aCx), NS_ERROR_FAILURE);
+    uint32_t nbytes = JS_GetTypedArrayByteLength(obj, aCx);
+    uint8_t* data = JS_GetUint8ArrayData(obj, aCx);
+    if (!data) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    FallibleTArray<uint8_t> fallibleArr;
+    if (!fallibleArr.InsertElementsAt(0, data, nbytes)) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    InfallibleTArray<uint8_t> arr;
+    arr.SwapElements(fallibleArr);
+    SendData(arr);
+  }
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketChild.h
@@ -0,0 +1,55 @@
+/* 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 "mozilla/net/PTCPSocketChild.h"
+#include "nsITCPSocketChild.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+
+#define TCPSOCKETCHILD_CID \
+  { 0xa589d96f, 0x7e09, 0x4edf, { 0xa0, 0x1a, 0xeb, 0x49, 0x51, 0xf4, 0x2f, 0x37 } }
+
+class nsITCPSocketInternal;
+class JSContext;
+class JSObject;
+
+namespace mozilla {
+namespace dom {
+
+class TCPSocketChildBase : public nsITCPSocketChild {
+public:
+  NS_DECL_CYCLE_COLLECTION_CLASS(TCPSocketChildBase)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+
+  void AddIPDLReference();
+  void ReleaseIPDLReference();
+
+protected:
+  TCPSocketChildBase();
+  virtual ~TCPSocketChildBase();
+
+  nsCOMPtr<nsITCPSocketInternal> mSocket;
+  bool mIPCOpen;
+};
+
+class TCPSocketChild : public mozilla::net::PTCPSocketChild
+                     , public TCPSocketChildBase
+{
+public:
+  NS_DECL_NSITCPSOCKETCHILD
+  NS_IMETHOD_(nsrefcnt) Release() MOZ_OVERRIDE;
+
+  TCPSocketChild();
+  ~TCPSocketChild();
+
+  virtual bool RecvCallback(const nsString& aType,
+                            const CallbackData& aData,
+                            const nsString& aReadyState,
+                            const uint32_t& aBuffered) MOZ_OVERRIDE;
+private:
+  JSObject* mSocketObj;
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketParent.cpp
@@ -0,0 +1,244 @@
+/* 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 "TCPSocketParent.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "nsJSUtils.h"
+#include "nsIDOMTCPSocket.h"
+#include "mozilla/unused.h"
+#include "mozilla/AppProcessPermissions.h"
+
+namespace IPC {
+
+//Defined in TCPSocketChild.cpp
+extern bool
+DeserializeUint8Array(JSRawObject aObj,
+                      const nsTArray<uint8_t>& aBuffer,
+                      jsval* aVal);
+
+}
+
+namespace mozilla {
+namespace dom {
+
+static void
+FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
+{
+  mozilla::unused <<
+      aActor->SendCallback(NS_LITERAL_STRING("onerror"),
+                          JSError(NS_LITERAL_STRING("Internal error"),
+                                  NS_LITERAL_STRING(__FILE__), aLineNo, 0),
+                          NS_LITERAL_STRING("connecting"), 0);
+}
+
+NS_IMPL_CYCLE_COLLECTION_2(TCPSocketParent, mSocket, mIntermediary)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketParent)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketParent)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketParent)
+  NS_INTERFACE_MAP_ENTRY(nsITCPSocketParent)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+bool
+TCPSocketParent::Init(const nsString& aHost, const uint16_t& aPort, const bool& aUseSSL,
+                      const nsString& aBinaryType, PBrowserParent* aBrowser)
+{
+  // We don't have browser actors in xpcshell, and hence can't run automated
+  // tests without this loophole.
+  if (aBrowser && !AssertAppProcessPermission(aBrowser, "tcp-socket")) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  nsresult rv;
+  mIntermediary = do_CreateInstance("@mozilla.org/tcp-socket-intermediary;1", &rv);
+  if (NS_FAILED(rv)) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  rv = mIntermediary->Open(this, aHost, aPort, aUseSSL, aBinaryType, getter_AddRefs(mSocket));
+  if (NS_FAILED(rv) || !mSocket) {
+    FireInteralError(this, __LINE__);
+    return true;
+  }
+
+  return true;
+}
+
+NS_IMETHODIMP
+TCPSocketParent::InitJS(const JS::Value& aIntermediary, JSContext* aCx)
+{
+  MOZ_ASSERT(aIntermediary.isObject());
+  mIntermediaryObj = &aIntermediary.toObject();
+  return NS_OK;
+}
+
+bool
+TCPSocketParent::RecvSuspend()
+{
+  NS_ENSURE_TRUE(mSocket, true);
+  nsresult rv = mSocket->Suspend();
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+bool
+TCPSocketParent::RecvResume()
+{
+  NS_ENSURE_TRUE(mSocket, true);
+  nsresult rv = mSocket->Resume();
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+bool
+TCPSocketParent::RecvData(const SendableData& aData)
+{
+  NS_ENSURE_TRUE(mIntermediary, true);
+
+  nsresult rv;
+  switch (aData.type()) {
+    case SendableData::TArrayOfuint8_t: {
+      jsval val;
+      IPC::DeserializeUint8Array(mIntermediaryObj, aData.get_ArrayOfuint8_t(), &val);
+      rv = mIntermediary->SendArrayBuffer(val);
+      NS_ENSURE_SUCCESS(rv, true);
+      break;
+    }
+
+    case SendableData::TnsString:
+      rv = mIntermediary->SendString(aData.get_nsString());
+      NS_ENSURE_SUCCESS(rv, true);
+      break;
+
+    default:
+      MOZ_NOT_REACHED();
+      return false;
+  }
+  return true;
+}
+
+bool
+TCPSocketParent::RecvClose()
+{
+  NS_ENSURE_TRUE(mSocket, true);
+  nsresult rv = mSocket->Close();
+  NS_ENSURE_SUCCESS(rv, true);
+  return true;
+}
+
+NS_IMETHODIMP
+TCPSocketParent::SendCallback(const nsAString& aType, const JS::Value& aDataVal,
+                              const nsAString& aReadyState, uint32_t aBuffered,
+                              JSContext* aCx)
+{
+  if (!mIPCOpen) {
+    NS_WARNING("Dropping callback due to no IPC connection");
+    return NS_OK;
+  }
+
+  CallbackData data;
+  if (aDataVal.isString()) {
+    JSString* jsstr = aDataVal.toString();
+    nsDependentJSString str;
+    if (!str.init(aCx, jsstr)) {
+      FireInteralError(this, __LINE__);
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    data = str;
+
+  } else if (aDataVal.isUndefined() || aDataVal.isNull()) {
+    data = mozilla::void_t();
+
+  } else if (aDataVal.isObject()) {
+    JSObject* obj = &aDataVal.toObject();
+    if (JS_IsTypedArrayObject(obj, aCx)) {
+      NS_ENSURE_TRUE(JS_IsUint8Array(obj, aCx), NS_ERROR_FAILURE);
+      uint32_t nbytes = JS_GetTypedArrayByteLength(obj, aCx);
+      uint8_t* buffer = JS_GetUint8ArrayData(obj, aCx);
+      if (!buffer) {
+        FireInteralError(this, __LINE__);
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      FallibleTArray<uint8_t> fallibleArr;
+      if (!fallibleArr.InsertElementsAt(0, buffer, nbytes)) {
+        FireInteralError(this, __LINE__);
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      InfallibleTArray<uint8_t> arr;
+      arr.SwapElements(fallibleArr);
+      data = SendableData(arr);
+
+    } else {
+      nsDependentJSString message, filename;
+      uint32_t lineNumber = 0;
+      uint32_t columnNumber = 0;
+
+      jsval val;
+      if (!JS_GetProperty(aCx, obj, "message", &val)) {
+        NS_ERROR("No message property on supposed error object");
+      } else if (JSVAL_IS_STRING(val)) {
+        if (!message.init(aCx, JSVAL_TO_STRING(val))) {
+          NS_WARNING("couldn't initialize string");
+        }
+      }
+
+      if (!JS_GetProperty(aCx, obj, "fileName", &val)) {
+        NS_ERROR("No fileName property on supposed error object");
+      } else if (JSVAL_IS_STRING(val)) {
+        if (!filename.init(aCx, JSVAL_TO_STRING(val))) {
+          NS_WARNING("couldn't initialize string");
+        }
+      }
+
+      if (!JS_GetProperty(aCx, obj, "lineNumber", &val)) {
+        NS_ERROR("No lineNumber property on supposed error object");
+      } else if (JSVAL_IS_INT(val)) {
+        lineNumber = JSVAL_TO_INT(val);
+      }
+
+      if (!JS_GetProperty(aCx, obj, "columnNumber", &val)) {
+        NS_ERROR("No columnNumber property on supposed error object");
+      } else if (JSVAL_IS_INT(val)) {
+        columnNumber = JSVAL_TO_INT(val);
+      }
+
+      data = JSError(message, filename, lineNumber, columnNumber);
+    }
+  } else {
+    NS_ERROR("Unexpected JS value encountered");
+    FireInteralError(this, __LINE__);
+    return NS_ERROR_FAILURE;
+  }
+  mozilla::unused <<
+      PTCPSocketParent::SendCallback(nsString(aType), data,
+                                     nsString(aReadyState), aBuffered);
+  return NS_OK;
+}
+
+void
+TCPSocketParent::ActorDestroy(ActorDestroyReason why)
+{
+  MOZ_ASSERT(mIPCOpen);
+  mIPCOpen = false;
+  if (mSocket) {
+    mSocket->Close();
+  }
+  mSocket = nullptr;
+  mIntermediaryObj = nullptr;
+  mIntermediary = nullptr;
+}
+
+bool
+TCPSocketParent::RecvRequestDelete()
+{
+  mozilla::unused << Send__delete__(this);
+  return true;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketParent.h
@@ -0,0 +1,49 @@
+/* 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 "mozilla/net/PTCPSocketParent.h"
+#include "nsITCPSocketParent.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+#include "nsIDOMTCPSocket.h"
+
+class JSContext;
+class JSObject;
+
+namespace mozilla {
+namespace dom {
+
+class PBrowserParent;
+
+class TCPSocketParent : public mozilla::net::PTCPSocketParent
+                      , public nsITCPSocketParent
+{
+public:
+  NS_DECL_CYCLE_COLLECTION_CLASS(TCPSocketParent)
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSITCPSOCKETPARENT
+
+  TCPSocketParent() : mIntermediaryObj(nullptr), mIPCOpen(true) {}
+
+  bool Init(const nsString& aHost, const uint16_t& aPort,
+            const bool& useSSL, const nsString& aBinaryType,
+            PBrowserParent* aBrowser);
+
+  virtual bool RecvSuspend() MOZ_OVERRIDE;
+  virtual bool RecvResume() MOZ_OVERRIDE;
+  virtual bool RecvClose() MOZ_OVERRIDE;
+  virtual bool RecvData(const SendableData& aData) MOZ_OVERRIDE;
+  virtual bool RecvRequestDelete() MOZ_OVERRIDE;
+
+private:
+  virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
+
+  nsCOMPtr<nsITCPSocketIntermediary> mIntermediary;
+  nsCOMPtr<nsIDOMTCPSocket> mSocket;
+  JSObject* mIntermediaryObj;
+  bool mIPCOpen;
+};
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/TCPSocketParentIntermediary.js
@@ -0,0 +1,55 @@
+/* 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/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function TCPSocketParentIntermediary() {
+}
+
+TCPSocketParentIntermediary.prototype = {
+  open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType) {
+    aParentSide.initJS(this);
+
+    let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
+    let socket = this._socket = baseSocket.open(aHost, aPort,
+                                                {useSSL: aUseSSL,
+                                                binaryType: aBinaryType});
+    if (!socket)
+      return null;
+
+    // Create handlers for every possible callback that attempt to trigger
+    // corresponding callbacks on the child object.
+    ["onopen", "ondrain", "ondata", "onerror", "onclose"].forEach(
+      function(p) {
+        socket[p] = function(data) {
+          aParentSide.sendCallback(p, data.data, socket.readyState,
+                                   socket.bufferedAmount);
+        };
+      }
+    );
+
+    return socket;
+  },
+
+  sendString: function(aData) {
+    return this._socket.send(aData);
+  },
+
+  sendArrayBuffer: function(aData) {
+    return this._socket.send(aData);
+  },
+
+  classID: Components.ID("{afa42841-a6cb-4a91-912f-93099f6a3d18}"),
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsITCPSocketIntermediary
+  ])
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([TCPSocketParentIntermediary]);
new file mode 100644
--- /dev/null
+++ b/dom/network/src/ipdl.mk
@@ -0,0 +1,7 @@
+# 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/.
+
+IPDLSRCS = \
+  PTCPSocket.ipdl \
+  $(NULL)
--- a/dom/network/tests/Makefile.in
+++ b/dom/network/tests/Makefile.in
@@ -18,11 +18,11 @@ MOCHITEST_FILES = \
   test_network_basics.html \
   test_tcpsocket_default_permissions.html \
   test_tcpsocket_enabled_no_perm.html \
   test_tcpsocket_enabled_with_perm.html \
   $(NULL)
 
 MODULE = test_dom_socket
 
-XPCSHELL_TESTS = unit
+XPCSHELL_TESTS = unit unit_ipc
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/network/tests/marionette/manifest.ini
+++ b/dom/network/tests/marionette/manifest.ini
@@ -2,8 +2,9 @@
 b2g = true
 browser = false
 qemu = true
 
 [test_mobile_networks.js]
 [test_mobile_voice_state.js]
 [test_mobile_iccinfo.js]
 [test_mobile_operator_names.js]
+[test_mobile_preferred_network_type.js]
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/marionette/test_mobile_preferred_network_type.js
@@ -0,0 +1,68 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+
+const KEY = "ril.radio.preferredNetworkType";
+
+let gSettingsEnabled = SpecialPowers.getBoolPref("dom.mozSettings.enabled");
+if (!gSettingsEnabled) {
+  SpecialPowers.setBoolPref("dom.mozSettings.enabled", true);
+}
+SpecialPowers.addPermission("mobileconnection", true, document);
+SpecialPowers.addPermission("settings", true, document);
+
+let settings = window.navigator.mozSettings;
+
+function test_revert_previous_setting_on_invalid_value() {
+  log("Testing reverting to previous setting on invalid value received");
+
+  let getLock = settings.createLock();
+  let getReq = getLock.get(KEY);
+  getReq.addEventListener("success", function onGetSuccess() {
+    let originalValue = getReq.result[KEY] || "wcdma/gsm";
+
+    let setDone = false;
+    settings.addObserver(KEY, function observer(setting) {
+      // Mark if the invalid value has been set in db and wait.
+      if (setting.settingValue == obj[KEY]) {
+        setDone = true;
+        return;
+      }
+
+      // Skip any change before marking but keep it as original value.
+      if (!setDone) {
+        originalValue = setting.settingValue;
+        return;
+      }
+
+      settings.removeObserver(KEY, observer);
+      is(setting.settingValue, originalValue, "Settings reverted");
+      window.setTimeout(cleanUp, 0);
+    });
+
+    let obj = {};
+    obj[KEY] = "AnInvalidValue";
+    let setLock = settings.createLock();
+    setLock.set(obj);
+    setLock.addEventListener("error", function onSetError() {
+      ok(false, "cannot set '" + KEY + "'");
+    });
+  });
+  getReq.addEventListener("error", function onGetError() {
+    ok(false, "cannot get default value of '" + KEY + "'");
+  });
+}
+
+function cleanUp() {
+  SpecialPowers.removePermission("mobileconnection", document);
+  SpecialPowers.removePermission("settings", document);
+  SpecialPowers.clearUserPref("dom.mozSettings.enabled");
+
+  finish();
+}
+
+waitFor(test_revert_previous_setting_on_invalid_value, function () {
+  return navigator.mozMobileConnection.voice.connected;
+});
+
--- a/dom/network/tests/unit/test_tcpsocket.js
+++ b/dom/network/tests/unit/test_tcpsocket.js
@@ -51,16 +51,21 @@ const ServerSocket = CC("@mozilla.org/ne
                              "nsIBinaryInputStream",
                              "setInputStream"),
       BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
                               "nsIBinaryOutputStream",
                               "setOutputStream"),
       TCPSocket = new (CC("@mozilla.org/tcp-socket;1",
                      "nsIDOMTCPSocket"))();
 
+const gInChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
+                  .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
 /**
  *
  * Helper functions
  *
  */
 
 /**
  * Spin up a listening socket and associate at most one live, accepted socket
@@ -361,17 +366,23 @@ function bufferedClose() {
 function badConnect() {
   // There's probably nothing listening on tcp port 2.
   sock = TCPSocket.open('127.0.0.1', 2);
 
   sock.onopen = makeFailureCase('open');
   sock.ondata = makeFailureCase('data');
   sock.onclose = makeFailureCase('close');
 
-  sock.onerror = makeSuccessCase('error');
+  let success = makeSuccessCase('error');
+  sock.onerror = function(data) {
+    do_check_neq(data.data.message, '');
+    do_check_neq(data.data.fileName, '');
+    do_check_neq(data.data.lineNumber, 0);
+    success();
+  };
 }
 
 /**
  * Test that calling send with enough data to buffer causes ondrain to
  * be invoked once the data has been sent, and then test that calling send
  * and buffering again causes ondrain to be fired again.
  */
 
@@ -405,16 +416,18 @@ function drainTwice() {
   if (sock.send(BIG_TYPED_ARRAY)) {
     throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
   }
 }
 
 function cleanup() {
   do_print("Cleaning up");
   sock.close();
+  if (!gInChild)
+    Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
   run_next_test();
 }
 
 /**
  * Test that calling send with enough data to buffer twice in a row without
  * waiting for ondrain still results in ondrain being invoked at least once.
  */
 
@@ -468,16 +481,19 @@ add_test(drainTwice);
 // send a buffer, get a drain, send a buffer, get a drain
 add_test(connectSock);
 add_test(bufferTwice);
 
 // clean up
 add_test(cleanup);
 
 function run_test() {
+  if (!gInChild)
+    Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
+  
   server = new TestServer();
 
   run_next_test();
 
   do_timeout(10000, function() {
     do_throw(
       "The test should never take this long unless the system is hosed.");
   });
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/unit_ipc/test_tcpsocket_ipc.js
@@ -0,0 +1,9 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function run_test() {
+  Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
+  run_test_in_child("../unit/test_tcpsocket.js", function() {
+    Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
+    do_test_finished();
+  });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/unit_ipc/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head =
+tail =
+
+[test_tcpsocket_ipc.js]
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -147,16 +147,17 @@ public:
 };
 
 AudioManager::AudioManager() : mPhoneState(PHONE_STATE_CURRENT),
                  mObserver(new HeadphoneSwitchObserver())
 {
   RegisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
 
   InternalSetAudioRoutes(GetCurrentSwitchState(SWITCH_HEADPHONES));
+  NotifyHeadphonesStatus(GetCurrentSwitchState(SWITCH_HEADPHONES));
 }
 
 AudioManager::~AudioManager() {
   UnregisterSwitchObserver(SWITCH_HEADPHONES, mObserver);
 }
 
 NS_IMETHODIMP
 AudioManager::GetMicrophoneMuted(bool* aMicrophoneMuted)
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -194,16 +194,19 @@ function RadioInterfaceLayer() {
 
   this.callWaitingStatus = null;
 
   // Read the 'ril.radio.disabled' setting in order to start with a known
   // value at boot time.
   let lock = gSettingsService.createLock();
   lock.get("ril.radio.disabled", this);
 
+  // Read preferred network type from the setting DB.
+  lock.get("ril.radio.preferredNetworkType", this);
+
   // Read the APN data from the settings DB.
   lock.get("ril.data.apn", this);
   lock.get("ril.data.user", this);
   lock.get("ril.data.passwd", this);
   lock.get("ril.data.httpProxyHost", this);
   lock.get("ril.data.httpProxyPort", this);
   lock.get("ril.data.roaming_enabled", this);
   lock.get("ril.data.enabled", this);
@@ -489,16 +492,19 @@ RadioInterfaceLayer.prototype = {
         this.handleCancelUSSD(message);
         break;
       case "stkcommand":
         this.handleStkProactiveCommand(message);
         break;
       case "stksessionend":
         ppmm.broadcastAsyncMessage("RIL:StkSessionEnd", null);
         break;
+      case "setPreferredNetworkType":
+        this.handleSetPreferredNetworkType(message);
+        break;
       default:
         throw new Error("Don't know about this message type: " +
                         message.rilMessageType);
     }
   },
 
   _messageManagerByRequest: null,
   saveRequestTarget: function saveRequestTarget(msg) {
@@ -674,16 +680,49 @@ RadioInterfaceLayer.prototype = {
     // Notify data call error only for data APN
     if (message.apn == this.dataCallSettings["apn"]) {
       ppmm.broadcastAsyncMessage("RIL:DataError", message);
     }
 
     this._deliverDataCallCallback("dataCallError", [message]);
   },
 
+  _preferredNetworkType: null,
+  setPreferredNetworkType: function setPreferredNetworkType(value) {
+    let networkType = RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(value);
+    if (networkType < 0) {
+      networkType = (this._preferredNetworkType != null)
+                    ? RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO[this._preferredNetworkType]
+                    : RIL.GECKO_PREFERRED_NETWORK_TYPE_DEFAULT;
+      gSettingsService.createLock().set("ril.radio.preferredNetworkType",
+                                        networkType, null);
+      return;
+    }
+
+    if (networkType == this._preferredNetworkType) {
+      return;
+    }
+
+    this.worker.postMessage({rilMessageType: "setPreferredNetworkType",
+                             networkType: networkType});
+  },
+
+  handleSetPreferredNetworkType: function handleSetPreferredNetworkType(message) {
+    if ((this._preferredNetworkType != null) && !message.success) {
+      gSettingsService.createLock().set("ril.radio.preferredNetworkType",
+                                        this._preferredNetworkType,
+                                        null);
+      return;
+    }
+
+    this._preferredNetworkType = message.networkType;
+    debug("_preferredNetworkType is now " +
+          RIL.RIL_PREFERRED_NETWORK_TYPE_TO_GECKO[this._preferredNetworkType]);
+  },
+
   handleSignalStrengthChange: function handleSignalStrengthChange(message) {
     let voiceInfo = this.rilContext.voice;
     // TODO CDMA, EVDO, LTE, etc. (see bug 726098)
     if (voiceInfo.signalStrength != message.gsmDBM ||
         voiceInfo.relSignalStrength != message.gsmRelative) {
       voiceInfo.signalStrength = message.gsmDBM;
       voiceInfo.relSignalStrength = message.gsmRelative;
       ppmm.broadcastAsyncMessage("RIL:VoiceInfoChanged", voiceInfo);
@@ -1257,16 +1296,20 @@ RadioInterfaceLayer.prototype = {
 
   handle: function handle(aName, aResult) {
     switch(aName) {
       case "ril.radio.disabled":
         debug("'ril.radio.disabled' is now " + aResult);
         this._radioEnabled = !aResult;
         this._ensureRadioState();
         break;
+      case "ril.radio.preferredNetworkType":
+        debug("'ril.radio.preferredNetworkType' is now " + aResult);
+        this.setPreferredNetworkType(aResult);
+        break;
       case "ril.data.enabled":
         this._oldRilDataEnabledState = this.dataCallSettings["enabled"];
         // Fall through!
       case "ril.data.roaming_enabled":
       case "ril.data.apn":
       case "ril.data.user":
       case "ril.data.passwd":
       case "ril.data.httpProxyHost":
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -330,28 +330,25 @@ const NETWORK_INFO_OPERATOR = "operator"
 const NETWORK_INFO_NETWORK_SELECTION_MODE = "networkSelectionMode";
 const NETWORK_INFO_MESSAGE_TYPES = [
   NETWORK_INFO_VOICE_REGISTRATION_STATE,
   NETWORK_INFO_DATA_REGISTRATION_STATE,
   NETWORK_INFO_OPERATOR,
   NETWORK_INFO_NETWORK_SELECTION_MODE
 ];
 
-const PREFERRED_NETWORK_TYPE_GSM_WCDMA = 0;
-const PREFERRED_NETWORK_TYPE_GSM_ONLY = 1;
-const PREFERRED_NETWORK_TYPE_WCDMA = 2;
-const PREFERRED_NETWORK_TYPE_GSM_WCDMA_AUTO = 3;
-const PREFERRED_NETWORK_TYPE_CDMA_EVDO_AUTO = 4;
-const PREFERRED_NETWORK_TYPE_CDMA_ONLY = 5;
-const PREFERRED_NETWORK_TYPE_EVDO_ONLY = 6;
-const PREFERRED_NETWORK_TYPE_GSM_WCDMA_CDMA_EVDO_AUTO = 7;
-const PREFERRED_NETWORK_TYPE_LTE_CDMA_EVDO = 8;
-const PREFERRED_NETWORK_TYPE_LTE_GSM_WCDMA = 9;
-const PREFERRED_NETWORK_TYPE_LTE_CMDA_EVDO_GSM_WCDMA = 10;
-const PREFERRED_NETWORK_TYPE_LTE_ONLY = 11;
+const GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM = "wcdma/gsm";
+const GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY = "gsm";
+const GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY = "wcdma";
+const GECKO_PREFERRED_NETWORK_TYPE_DEFAULT = GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM;
+const RIL_PREFERRED_NETWORK_TYPE_TO_GECKO = [
+  GECKO_PREFERRED_NETWORK_TYPE_WCDMA_GSM,
+  GECKO_PREFERRED_NETWORK_TYPE_GSM_ONLY,
+  GECKO_PREFERRED_NETWORK_TYPE_WCDMA_ONLY
+];
 
 // Network registration states. See TS 27.007 7.2
 const NETWORK_CREG_STATE_NOT_SEARCHING = 0;
 const NETWORK_CREG_STATE_REGISTERED_HOME = 1;
 const NETWORK_CREG_STATE_SEARCHING = 2;
 const NETWORK_CREG_STATE_DENIED = 3;
 const NETWORK_CREG_STATE_UNKNOWN = 4;
 const NETWORK_CREG_STATE_REGISTERED_ROAMING = 5;
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -643,16 +643,22 @@ let RIL = {
    */
   _receivedSmsSegmentsMap: {},
 
   /**
    * Outgoing messages waiting for SMS-STATUS-REPORT.
    */
   _pendingSentSmsMap: {},
 
+  /**
+   * Index of the RIL_PREFERRED_NETWORK_TYPE_TO_GECKO. Its value should be
+   * preserved over rild reset.
+   */
+  preferredNetworkType: null,
+
   initRILState: function initRILState() {
     /**
      * One of the RADIO_STATE_* constants.
      */
     this.radioState = GECKO_RADIOSTATE_UNAVAILABLE;
     this._isInitialRadioState = true;
 
     /**
@@ -1717,26 +1723,43 @@ let RIL = {
 
   getOperator: function getOperator() {
     Buf.simpleRequest(REQUEST_OPERATOR);
   },
 
   /**
    * Set the preferred network type.
    *
-   * @param network_type
-   *        The network type. One of the PREFERRED_NETWORK_TYPE_* constants.
-   */
-  setPreferredNetworkType: function setPreferredNetworkType(network_type) {
-    Buf.newParcel(REQUEST_SET_PREFERRED_NETWORK_TYPE);
-    Buf.writeUint32(network_type);
+   * @param options An object contains a valid index of
+   *                RIL_PREFERRED_NETWORK_TYPE_TO_GECKO as its `networkType`
+   *                attribute, or undefined to set current preferred network
+   *                type.
+   */
+  setPreferredNetworkType: function setPreferredNetworkType(options) {
+    if (options) {
+      this.preferredNetworkType = options.networkType;
+    }
+    if (this.preferredNetworkType == null) {
+      return;
+    }
+
+    Buf.newParcel(REQUEST_SET_PREFERRED_NETWORK_TYPE, options);
+    Buf.writeUint32(1);
+    Buf.writeUint32(this.preferredNetworkType);
     Buf.sendParcel();
   },
 
   /**
+   * Get the preferred network type.
+   */
+  getPreferredNetworkType: function getPreferredNetworkType() {
+    Buf.simpleRequest(REQUEST_GET_PREFERRED_NETWORK_TYPE);
+  },
+
+  /**
    * Request various states about the network.
    */
   requestNetworkInfo: function requestNetworkInfo() {
     if (this._processingNetworkInfo) {
       if (DEBUG) debug("Network info requested, but we're already requesting network info.");
       return;
     }
 
@@ -4280,18 +4303,44 @@ RIL[REQUEST_DELETE_SMS_ON_SIM] = null;
 RIL[REQUEST_SET_BAND_MODE] = null;
 RIL[REQUEST_QUERY_AVAILABLE_BAND_MODE] = null;
 RIL[REQUEST_STK_GET_PROFILE] = null;
 RIL[REQUEST_STK_SET_PROFILE] = null;
 RIL[REQUEST_STK_SEND_ENVELOPE_COMMAND] = null;
 RIL[REQUEST_STK_SEND_TERMINAL_RESPONSE] = null;
 RIL[REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM] = null;
 RIL[REQUEST_EXPLICIT_CALL_TRANSFER] = null;
-RIL[REQUEST_SET_PREFERRED_NETWORK_TYPE] = null;
-RIL[REQUEST_GET_PREFERRED_NETWORK_TYPE] = null;
+RIL[REQUEST_SET_PREFERRED_NETWORK_TYPE] = function REQUEST_SET_PREFERRED_NETWORK_TYPE(length, options) {
+  if (options.networkType == null) {
+    // The request was made by ril_worker itself automatically. Don't report.
+    return;
+  }
+
+  this.sendDOMMessage({
+    rilMessageType: "setPreferredNetworkType",
+    networkType: options.networkType,
+    success: options.rilRequestError == ERROR_SUCCESS
+  });
+};
+RIL[REQUEST_GET_PREFERRED_NETWORK_TYPE] = function REQUEST_GET_PREFERRED_NETWORK_TYPE(length, options) {
+  let networkType;
+  if (!options.rilRequestError) {
+    networkType = RIL_PREFERRED_NETWORK_TYPE_TO_GECKO.indexOf(GECKO_PREFERRED_NETWORK_TYPE_DEFAULT);
+    let responseLen = Buf.readUint32(); // Number of INT32 responsed.
+    if (responseLen) {
+      this.preferredNetworkType = networkType = Buf.readUint32();
+    }
+  }
+
+  this.sendDOMMessage({
+    rilMessageType: "getPreferredNetworkType",
+    networkType: networkType,
+    success: options.rilRequestError == ERROR_SUCCESS
+  });
+};
 RIL[REQUEST_GET_NEIGHBORING_CELL_IDS] = null;
 RIL[REQUEST_SET_LOCATION_UPDATES] = null;
 RIL[REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE] = null;
 RIL[REQUEST_CDMA_SET_ROAMING_PREFERENCE] = null;
 RIL[REQUEST_CDMA_QUERY_ROAMING_PREFERENCE] = null;
 RIL[REQUEST_SET_TTY_MODE] = null;
 RIL[REQUEST_QUERY_TTY_MODE] = null;
 RIL[REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE] = null;
@@ -4544,16 +4593,18 @@ RIL[UNSOLICITED_RIL_CONNECTED] = functio
   let version = Buf.readUint32List()[0];
   RILQUIRKS_V5_LEGACY = (version < 5);
   if (DEBUG) {
     debug("Detected RIL version " + version);
     debug("RILQUIRKS_V5_LEGACY is " + RILQUIRKS_V5_LEGACY);
   }
 
   this.initRILState();
+
+  this.setPreferredNetworkType();
 };
 
 /**
  * This object exposes the functionality to parse and serialize PDU strings
  *
  * A PDU is a string containing a series of hexadecimally encoded octets
  * or nibble-swapped binary-coded decimals (BCDs). It contains not only the
  * message text but information about the sender, the SMS service center,
--- a/dom/system/gonk/systemlibs.js
+++ b/dom/system/gonk/systemlibs.js
@@ -8,16 +8,23 @@
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
+let EXPORTED_SYMBOLS;
+if (!this.ctypes) {
+  // We're likely being loaded as a JSM.
+  EXPORTED_SYMBOLS = [ "libcutils", "libnetutils", "netHelpers" ];
+  Components.utils.import("resource://gre/modules/ctypes.jsm");
+}
+
 const SYSTEM_PROPERTY_KEY_MAX = 32;
 const SYSTEM_PROPERTY_VALUE_MAX = 92;
 
 // We leave this as 'undefined' instead of setting it to 'false'. That
 // way a file that includes us can have it defined already without us
 // overriding the value here.
 let DEBUG;
 
--- a/dom/tests/mochitest/ajax/offline/test_updatingManifest.html
+++ b/dom/tests/mochitest/ajax/offline/test_updatingManifest.html
@@ -104,17 +104,17 @@ function whitelistOnLoad(version)
 }
 
 
 // Start of the test function chain
 // ================================
 
 function manifestCached()
 {
-  OfflineTest.is(gStep, 0);
+  OfflineTest.is(gStep, 0, "Got manifestCached in step 0, gStep=" + gStep);
 
   reloadLocations([fallbackFrame1, fallbackFrame2]);
   waitForLocations([fallbackFrame1, fallbackFrame2],
       fallbackLoaded);
 }
 
 function fallbackLoaded()
 {
@@ -185,17 +185,17 @@ function implicitCached()
 
       applicationCache.update();
       // Invokes manifestUpdated()
     });
 }
 
 function manifestUpdated()
 {
-  OfflineTest.ok(gStep == 1 || gStep == 2);
+  OfflineTest.ok(gStep == 1 || gStep == 2, "Got manifestUpdated in step 1 or 2, gStep=" + gStep);
 
   switch (gStep)
   {
   case 1:
     // Processing second version of the manifest.
 
     // Whitelist entries
     checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", "", false);
@@ -292,17 +292,17 @@ function manifestUpdated()
     break;
   }
 }
 
 function manifestNoUpdate()
 {
   applicationCache.onnoupdate = null;
 
-  OfflineTest.ok(gStep == 3);
+  OfflineTest.ok(gStep == 3, "Got manifestNoUpdate in step 3, gStep=" + gStep);
 
   OfflineTest.is(applicationCache.status, SpecialPowers.Ci.nsIDOMOfflineResourceList.UPDATEREADY,
         "we have associated application cache and update is pending (4)");
   applicationCache.swapCache();
   OfflineTest.is(applicationCache.status, SpecialPowers.Ci.nsIDOMOfflineResourceList.IDLE,
         "we have associated application cache (4)");
 
   gGotFrameVersion = 0;
--- a/dom/wifi/WifiWorker.js
+++ b/dom/wifi/WifiWorker.js
@@ -36,40 +36,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 // The libraries that we use in this file are intended for C code. For
 // C code, it is natural to return -1 for errors and 0 for success.
 // Therefore, the code that interacts directly with the worker uses this
 // convention (note: command functions do get boolean results since the
 // command always succeeds and we do a string/boolean check for the
 // expected results).
 var WifiManager = (function() {
   function getSdkVersionAndDevice() {
-    Cu.import("resource://gre/modules/ctypes.jsm");
-    try {
-      let cutils = ctypes.open("libcutils.so");
-      let cbuf = ctypes.char.array(4096)();
-      let c_property_get = cutils.declare("property_get", ctypes.default_abi,
-                                          ctypes.int,       // return value: length
-                                          ctypes.char.ptr,  // key
-                                          ctypes.char.ptr,  // value
-                                          ctypes.char.ptr); // default
-      let property_get = function (key, defaultValue) {
-        if (defaultValue === undefined) {
-          defaultValue = null;
-        }
-        c_property_get(key, cbuf, defaultValue);
-        return cbuf.readString();
-      }
-      return { sdkVersion: parseInt(property_get("ro.build.version.sdk")),
-               device: property_get("ro.product.device") };
-    } catch(e) {
-      // Eat it.  Hopefully we're on a non-Gonk system ...
-      //
-      // XXX we should check that
-      return 0;
-    }
+    Cu.import("resource://gre/modules/systemlibs.js");
+    let sdkVersion = libcutils.property_get("ro.build.version.sdk");
+    return { sdkVersion: parseInt(sdkVersion, 10),
+               device: libcutils.property_get("ro.product.device") };
   }
 
   let { sdkVersion, device } = getSdkVersionAndDevice();
 
   var controlWorker = new ChromeWorker(WIFIWORKER_WORKER);
   var eventWorker = new ChromeWorker(WIFIWORKER_WORKER);
 
   // Callbacks to invoke when a reply arrives from the controlWorker.
--- a/extensions/cookie/test/Makefile.in
+++ b/extensions/cookie/test/Makefile.in
@@ -49,17 +49,19 @@ MOCHITEST_FILES = \
   file_localhost_inner.html \
   test_same_base_domain_5.html \
   test_same_base_domain_6.html \
   file_loopback_inner.html \
   $(NULL)
 
 MOCHITEST_CHROME_FILES = \
   test_permissionmanager_app_isolation.html \
-  test_app_uninstall.html \
+  test_app_uninstall_permissions.html \
+  test_app_uninstall_cookies.html \
+  channel_utils.js \
   $(NULL)
 
 MOCHITEST_BROWSER_FILES = \
   browser_test_favicon.js \
   $(NULL)
 
 XPCSHELL_TESTS = unit
 
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/channel_utils.js
@@ -0,0 +1,195 @@
+/*
+ * This file is a modified version of netwerk/test/unit/head_channels.js
+ * The changes consist of making it work in mochitest files and removing
+ * unused code.
+ */
+
+/**
+ * Read count bytes from stream and return as a String object
+ */
+function read_stream(stream, count) {
+  /* assume stream has non-ASCII data */
+  var wrapper =
+      Components.classes["@mozilla.org/binaryinputstream;1"]
+                .createInstance(Components.interfaces.nsIBinaryInputStream);
+  wrapper.setInputStream(stream);
+  /* JS methods can be called with a maximum of 65535 arguments, and input
+     streams don't have to return all the data they make .available() when
+     asked to .read() that number of bytes. */
+  var data = [];
+  while (count > 0) {
+    var bytes = wrapper.readByteArray(Math.min(65535, count));
+    data.push(String.fromCharCode.apply(null, bytes));
+    count -= bytes.length;
+    if (bytes.length == 0)
+      throw("Nothing read from input stream!");
+  }
+  return data.join('');
+}
+
+const CL_EXPECT_FAILURE = 0x1;
+const CL_EXPECT_GZIP = 0x2;
+const CL_EXPECT_3S_DELAY = 0x4;
+const CL_SUSPEND = 0x8;
+const CL_ALLOW_UNKNOWN_CL = 0x10;
+const CL_EXPECT_LATE_FAILURE = 0x20;
+
+const SUSPEND_DELAY = 3000;
+
+/**
+ * A stream listener that calls a callback function with a specified
+ * context and the received data when the channel is loaded.
+ *
+ * Signature of the closure:
+ *   void closure(in nsIRequest request, in ACString data, in JSObject context);
+ *
+ * This listener makes sure that various parts of the channel API are
+ * implemented correctly and that the channel's status is a success code
+ * (you can pass CL_EXPECT_FAILURE or CL_EXPECT_LATE_FAILURE as flags
+ * to allow a failure code)
+ *
+ * Note that it also requires a valid content length on the channel and
+ * is thus not fully generic.
+ */
+function ChannelListener(closure, ctx, flags) {
+  this._closure = closure;
+  this._closurectx = ctx;
+  this._flags = flags;
+}
+ChannelListener.prototype = {
+  _closure: null,
+  _closurectx: null,
+  _buffer: "",
+  _got_onstartrequest: false,
+  _got_onstoprequest: false,
+  _contentLen: -1,
+  _lastEvent: 0,
+
+  QueryInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIStreamListener) ||
+        iid.equals(Components.interfaces.nsIRequestObserver) ||
+        iid.equals(Components.interfaces.nsISupports))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  onStartRequest: function(request, context) {
+    try {
+      if (this._got_onstartrequest)
+        throw("Got second onStartRequest event!");
+      this._got_onstartrequest = true;
+      this._lastEvent = Date.now();
+
+      request.QueryInterface(Components.interfaces.nsIChannel);
+      try {
+        this._contentLen = request.contentLength;
+      }
+      catch (ex) {
+        if (!(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)))
+          throw("Could not get contentLength");
+      }
+      if (this._contentLen == -1 && !(this._flags & (CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL)))
+        throw("Content length is unknown in onStartRequest!");
+
+      if (this._flags & CL_SUSPEND) {
+        request.suspend();
+        do_timeout(SUSPEND_DELAY, function() { request.resume(); });
+      }
+
+    } catch (ex) {
+      throw("Error in onStartRequest: " + ex);
+    }
+  },
+
+  onDataAvailable: function(request, context, stream, offset, count) {
+    try {
+      var current = Date.now();
+
+      if (!this._got_onstartrequest)
+        throw("onDataAvailable without onStartRequest event!");
+      if (this._got_onstoprequest)
+        throw("onDataAvailable after onStopRequest event!");
+      if (!request.isPending())
+        throw("request reports itself as not pending from onDataAvailable!");
+      if (this._flags & CL_EXPECT_FAILURE)
+        throw("Got data despite expecting a failure");
+
+      if (current - this._lastEvent >= SUSPEND_DELAY &&
+          !(this._flags & CL_EXPECT_3S_DELAY))
+       throw("Data received after significant unexpected delay");
+      else if (current - this._lastEvent < SUSPEND_DELAY &&
+               this._flags & CL_EXPECT_3S_DELAY)
+        throw("Data received sooner than expected");
+      else if (current - this._lastEvent >= SUSPEND_DELAY &&
+               this._flags & CL_EXPECT_3S_DELAY)
+        this._flags &= ~CL_EXPECT_3S_DELAY; // No more delays expected
+
+      this._buffer = this._buffer.concat(read_stream(stream, count));
+      this._lastEvent = current;
+    } catch (ex) {
+      throw("Error in onDataAvailable: " + ex);
+    }
+  },
+
+  onStopRequest: function(request, context, status) {
+    try {
+      var success = Components.isSuccessCode(status);
+      if (!this._got_onstartrequest)
+        throw("onStopRequest without onStartRequest event!");
+      if (this._got_onstoprequest)
+        throw("Got second onStopRequest event!");
+      this._got_onstoprequest = true;
+      if ((this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && success)
+        throw("Should have failed to load URL (status is " + status.toString(16) + ")");
+      else if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) && !success)
+        throw("Failed to load URL: " + status.toString(16));
+      if (status != request.status)
+        throw("request.status does not match status arg to onStopRequest!");
+      if (request.isPending())
+        throw("request reports itself as pending from onStopRequest!");
+      if (!(this._flags & (CL_EXPECT_FAILURE | CL_EXPECT_LATE_FAILURE)) &&
+          !(this._flags & CL_EXPECT_GZIP) &&
+          this._contentLen != -1)
+          is(this._buffer.length, this._contentLen);
+    } catch (ex) {
+      throw("Error in onStopRequest: " + ex);
+    }
+    try {
+      this._closure(request, this._buffer, this._closurectx);
+    } catch (ex) {
+      throw("Error in closure function: " + ex);
+    }
+  }
+};
+
+/**
+ * Class that implements nsILoadContext.  Use it as callbacks for channel when
+ * test needs it.
+ */
+function LoadContextCallback(appId, inBrowserElement, isPrivate, isContent) {
+  this.appId = appId;
+  this.isInBrowserElement = inBrowserElement;
+  this.usePrivateBrowsing = isPrivate;
+  this.isContent = isContent;
+}
+
+LoadContextCallback.prototype = {
+  associatedWindow: null,
+  topWindow : null,
+  isAppOfType: function(appType) {
+    throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+  },
+  QueryInterface: function(iid) {
+    if (iid == Ci.nsILoadContext ||
+               Ci.nsIInterfaceRequestor ||
+               Ci.nsISupports) {
+        return this;
+    }
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+  getInterface: function(iid) {
+    if (iid.equals(Ci.nsILoadContext))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+}
new file mode 100644
--- /dev/null
+++ b/extensions/cookie/test/test_app_uninstall_cookies.html
@@ -0,0 +1,215 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=783408
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests that uninstalling app removes the cookies</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+  <script src="channel_utils.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786296">Mozilla Bug 783408</a>
+<p id="display"></p>
+<div id="content">
+  
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+/** Test for Bug 783408 **/
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+SimpleTest.waitForExplicitFinish();
+
+Cu.import("resource://testing-common/httpd.js");
+var httpserver = new HttpServer();
+
+var cookieSetPath = "/setcookie";
+var cookieCheckPath = "/checkcookie";
+
+var permManager = Cc["@mozilla.org/permissionmanager;1"]
+                    .getService(Ci.nsIPermissionManager);
+var cookieMng = Cc["@mozilla.org/cookiemanager;1"]
+                  .getService(Ci.nsICookieManager2);
+var appsService = Cc['@mozilla.org/AppsService;1']
+                    .getService(Ci.nsIAppsService);
+
+var cookies = [
+  { cookieName: 'LCC_App_BrowF_PrivF',
+    loadContext: null },
+  { cookieName: 'LCC_App_BrowT_PrivF',
+    loadContext: null },
+  { cookieName: 'AppUninstall_Witness',
+    loadContext: new LoadContextCallback(0, false, false, 1) },
+];
+var counter = 0;
+
+function confirmNextInstall() {
+  var panel = window.top.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIWebNavigation)
+                        .QueryInterface(Ci.nsIDocShell)
+                        .chromeEventHandler.ownerDocument.defaultView
+                        .PopupNotifications.panel
+
+  panel.addEventListener("popupshown", function() {
+    panel.removeEventListener("popupshown", arguments.callee, false);
+    this.childNodes[0].button.doCommand();
+  }, false);
+}
+
+// If aAppId = -1, returns permissions count, regardless of app.
+function getPermissionCountForApp(aAppId) {
+  var nbPermissions = 0;
+  var enumerator = permManager.enumerator;
+
+  while (enumerator.hasMoreElements()) {
+    var permission = enumerator.getNext().QueryInterface(Ci.nsIPermission);
+
+    if (permission.appId == aAppId || aAppId == -1) {
+      nbPermissions++;
+    }
+  }
+
+  return nbPermissions;
+}
+
+function getCookiesCountForApp(aAppId) {
+  var nbCookies = 0;
+  var enumerator = cookieMng.getCookiesForApp(aAppId, false);
+
+  while (enumerator.hasMoreElements()) {
+    enumerator.getNext();
+    nbCookies++;
+  }
+
+  return nbCookies;
+}
+
+function getCookiesCount() {
+  var nbCookies = 0;
+  var enumerator = cookieMng.enumerator;
+
+  while (enumerator.hasMoreElements()) {
+    enumerator.getNext();
+    nbCookies++;
+  }
+
+  return nbCookies;
+}
+
+function cookieSetHandler(metadata, response) {
+  var cookieName = metadata.getHeader("foo-set-cookie");
+
+  response.setStatusLine(metadata.httpVersion, 200, "Ok");
+  response.setHeader("Set-Cookie", cookieName + "=1; Path=/", false);
+  response.setHeader("Content-Type", "text/plain");
+  response.bodyOutputStream.write("Ok", "Ok".length);
+}
+
+function cookieCheckHandler(metadata, response) {
+  var cookies = metadata.getHeader("Cookie");
+
+  response.setStatusLine(metadata.httpVersion, 200, "Ok");
+  response.setHeader("foo-saw-cookies", cookies, false);
+  response.setHeader("Content-Type", "text/plain");
+  response.bodyOutputStream.write("Ok", "Ok".length);
+}
+
+function setupChannel(path) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+  var chan = ios.newChannel("http://localhost:4444" + path, "", null);
+  chan.notificationCallbacks = cookies[counter].loadContext;
+  chan.QueryInterface(Ci.nsIHttpChannel);
+  return chan;
+}
+
+function setCookie() {
+  var channel = setupChannel(cookieSetPath);
+  channel.setRequestHeader("foo-set-cookie", cookies[counter].cookieName, false);
+  channel.asyncOpen(new ChannelListener(setNextCookie, null), null);
+}
+
+function setNextCookie(request, data, context) {
+  if (++counter == cookies.length) {
+    // all cookies set: switch to checking them
+    counter = 0;
+    checkCookie();
+  } else {
+    setCookie();
+  }
+}
+
+var previousDryRunValue = null;
+try {
+  previousDryRunValue = SpecialPowers.getBoolPref('browser.mozApps.installer.dry_run');
+} catch(e) {
+}
+
+SpecialPowers.setBoolPref('browser.mozApps.installer.dry_run', true);
+permManager.addFromPrincipal(window.document.nodePrincipal, "webapps-manage",
+                             Ci.nsIPermissionManager.ALLOW_ACTION);
+
+var gManifestURL = "http://www.example.com:80/chrome/dom/tests/mochitest/webapps/apps/basic.webapp";
+
+confirmNextInstall();
+
+var gTestAppId = 0;
+var gCurrentCookiesCount = 0;
+
+navigator.mozApps.install(gManifestURL, null).onsuccess = function() {
+  gTestAppId = appsService.getAppLocalIdByManifestURL(gManifestURL);
+
+  cookies[0].loadContext = new LoadContextCallback(gTestAppId, false, false, 1);
+  cookies[1].loadContext = new LoadContextCallback(gTestAppId, true, false, 1);
+
+  is(getCookiesCountForApp(gTestAppId), 0, "App should have no cookies");
+
+  httpserver.registerPathHandler(cookieSetPath, cookieSetHandler);
+  httpserver.registerPathHandler(cookieCheckPath, cookieCheckHandler);
+  httpserver.start(4444);
+
+  setCookie();
+};
+
+function checkCookie() {
+  var appCookiesCount = getCookiesCountForApp(gTestAppId);
+  is(appCookiesCount, 2, "App should have two cookies");
+
+  gCurrentCookiesCount = getCookiesCount() - appCookiesCount;
+
+  // Not installed means not installed as native app.
+  navigator.mozApps.mgmt.getNotInstalled().onsuccess = function() {
+    for (i in this.result) {
+      var app = this.result[i];
+      if (app.manifestURL == gManifestURL) {
+        app.uninstall().onsuccess = function() {
+          is(getCookiesCountForApp(gTestAppId), 0, "App should have no cookies");
+
+          is(getCookiesCount(), gCurrentCookiesCount,
+             "Number of cookies should not have changed");
+
+          SpecialPowers.setBoolPref('browser.mozApps.installer.dry_run', previousDryRunValue);
+          permManager.removeFromPrincipal(window.document.nodePrincipal, "webapps-manage",
+                                       Ci.nsIPermissionManager.ALLOW_ACTION);
+
+          httpserver.stop(function() {
+            SimpleTest.finish();
+          });
+
+          return;
+        };
+      }
+    }
+  };
+}
+
+</script>
+</pre>
+</body>
+</html>
rename from extensions/cookie/test/test_app_uninstall.html
rename to extensions/cookie/test/test_app_uninstall_permissions.html
--- a/gfx/cairo/cairo/src/cairo-pdf-surface.c
+++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c
@@ -1759,16 +1759,101 @@ static cairo_int_status_t
 }
 
 static cairo_bool_t
 _cairo_pdf_surface_supports_fine_grained_fallbacks (void *abstract_surface)
 {
     return TRUE;
 }
 
+static cairo_status_t
+_cairo_pdf_surface_add_padded_image_surface (cairo_pdf_surface_t          *surface,
+					     cairo_surface_pattern_t      *source,
+					     const cairo_rectangle_int_t  *extents,
+					     cairo_pdf_resource_t         *surface_res,
+					     int                          *width,
+					     int                          *height,
+					     int                          *origin_x,
+					     int                          *origin_y)
+{
+    cairo_image_surface_t *image;
+    cairo_surface_t *pad_image;
+    void *image_extra;
+    cairo_int_status_t status;
+    int x = 0;
+    int y = 0;
+    int w, h;
+    cairo_rectangle_int_t extents2;
+    cairo_box_t box;
+    cairo_rectangle_int_t rect;
+    cairo_surface_pattern_t pad_pattern;
+
+    status = _cairo_surface_acquire_source_image (source->surface, &image, &image_extra);
+    if (unlikely (status))
+        return status;
+
+    pad_image = &image->base;
+
+    /* get the operation extents in pattern space */
+    _cairo_box_from_rectangle (&box, extents);
+    _cairo_matrix_transform_bounding_box_fixed (&source->base.matrix, &box, NULL);
+    _cairo_box_round_to_rectangle (&box, &rect);
+
+    /* Check if image needs padding to fill extents */
+    w = image->width;
+    h = image->height;
+    if (_cairo_fixed_integer_ceil(box.p1.x) < 0 ||
+	_cairo_fixed_integer_ceil(box.p1.y) < 0 ||
+	_cairo_fixed_integer_floor(box.p2.y) > w ||
+	_cairo_fixed_integer_floor(box.p2.y) > h)
+    {
+	x = -rect.x;
+	y = -rect.y;
+	pad_image = _cairo_image_surface_create_with_content (source->surface->content,
+							      rect.width,
+							      rect.height);
+	if (pad_image->status) {
+	    status = pad_image->status;
+	    goto BAIL;
+	}
+
+	_cairo_pattern_init_for_surface (&pad_pattern, &image->base);
+	cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y);
+	pad_pattern.base.extend = CAIRO_EXTEND_PAD;
+	status = _cairo_surface_paint (pad_image,
+				       CAIRO_OPERATOR_SOURCE, &pad_pattern.base,
+				       NULL);
+        _cairo_pattern_fini (&pad_pattern.base);
+        if (unlikely (status))
+            goto BAIL;
+    }
+
+    status = _cairo_pdf_surface_add_source_surface (surface,
+						    pad_image,
+						    source->base.filter,
+						    surface_res,
+						    &w,
+						    &h);
+    if (unlikely (status))
+        goto BAIL;
+
+    *width = ((cairo_image_surface_t *)pad_image)->width;
+    *height = ((cairo_image_surface_t *)pad_image)->height;
+    *origin_x = x;
+    *origin_y = y;
+
+BAIL:
+    if (pad_image != &image->base)
+        cairo_surface_destroy (pad_image);
+
+    _cairo_surface_release_source_image (source->surface, image, image_extra);
+
+    return status;
+}
+
 /* Emit alpha channel from the image into the given data, providing
  * an id that can be used to reference the resulting SMask object.
  *
  * In the case that the alpha channel happens to be all opaque, then
  * no SMask object will be emitted and *id_ret will be set to 0.
  */
 static cairo_status_t
 _cairo_pdf_surface_emit_smask (cairo_pdf_surface_t	*surface,
@@ -2114,116 +2199,16 @@ static cairo_status_t
 
 BAIL:
     _cairo_surface_release_source_image (source, image, image_extra);
 
     return status;
 }
 
 static cairo_status_t
-_cairo_pdf_surface_emit_padded_image_surface (cairo_pdf_surface_t     *surface,
-					      cairo_pdf_pattern_t     *pdf_pattern,
-					      cairo_pdf_resource_t    *resource,
-					      int                     *width,
-					      int                     *height,
-					      int                     *origin_x,
-					      int                     *origin_y)
-{
-    cairo_image_surface_t *image;
-    cairo_surface_t *pad_image;
-    void *image_extra;
-    cairo_status_t status;
-    cairo_surface_pattern_t *pattern = (cairo_surface_pattern_t *) pdf_pattern->pattern;
-    int x = 0;
-    int y = 0;
-    cairo_bool_t interpolate;
-
-    status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra);
-    if (unlikely (status))
-        return status;
-
-    pad_image = &image->base;
-    if (pattern->base.extend == CAIRO_EXTEND_PAD) {
-        cairo_box_t box;
-        cairo_rectangle_int_t rect;
-        cairo_surface_pattern_t pad_pattern;
-
-        /* get the operation extents in pattern space */
-        _cairo_box_from_rectangle (&box, &pdf_pattern->extents);
-        _cairo_matrix_transform_bounding_box_fixed (&pattern->base.matrix, &box, NULL);
-        _cairo_box_round_to_rectangle (&box, &rect);
-        x = -rect.x;
-        y = -rect.y;
-
-        pad_image = _cairo_image_surface_create_with_content (pattern->surface->content,
-                                                              rect.width,
-                                                              rect.height);
-        if (pad_image->status) {
-            status = pad_image->status;
-            goto BAIL;
-        }
-
-        _cairo_pattern_init_for_surface (&pad_pattern, &image->base);
-        cairo_matrix_init_translate (&pad_pattern.base.matrix, -x, -y);
-        pad_pattern.base.extend = CAIRO_EXTEND_PAD;
-        status = _cairo_surface_composite (CAIRO_OPERATOR_SOURCE,
-                                           &pad_pattern.base,
-                                           NULL,
-                                           pad_image,
-                                           0, 0,
-                                           0, 0,
-                                           0, 0,
-                                           rect.width,
-                                           rect.height,
-					   NULL);
-        _cairo_pattern_fini (&pad_pattern.base);
-        if (unlikely (status))
-            goto BAIL;
-    }
-
-    switch (pdf_pattern->pattern->filter) {
-    case CAIRO_FILTER_GOOD:
-    case CAIRO_FILTER_BEST:
-    case CAIRO_FILTER_BILINEAR:
-	interpolate = TRUE;
-	break;
-    case CAIRO_FILTER_FAST:
-    case CAIRO_FILTER_NEAREST:
-    case CAIRO_FILTER_GAUSSIAN:
-	interpolate = FALSE;
-	break;
-    }
-
-    *resource = _cairo_pdf_surface_new_object (surface);
-    if (resource->id == 0) {
-	status = _cairo_error (CAIRO_STATUS_NO_MEMORY);
-	goto BAIL;
-    }
-
-    status = _cairo_pdf_surface_emit_image (surface, (cairo_image_surface_t *)pad_image,
-                                            resource, interpolate);
-    if (unlikely (status))
-        goto BAIL;
-
-    *width = ((cairo_image_surface_t *)pad_image)->width;
-    *height = ((cairo_image_surface_t *)pad_image)->height;
-    *origin_x = x;
-    *origin_y = y;
-
-BAIL:
-    if (pad_image != &image->base)
-        cairo_surface_destroy (pad_image);
-
-    _cairo_surface_release_source_image (pattern->surface, image, image_extra);
-
-    return status;
-}
-
-
-static cairo_status_t
 _cairo_pdf_surface_emit_recording_surface (cairo_pdf_surface_t  *surface,
 					   cairo_surface_t      *recording_surface,
 					   cairo_pdf_resource_t  resource)
 {
     double old_width, old_height;
     cairo_paginated_mode_t old_paginated_mode;
     cairo_rectangle_int_t recording_extents;
     cairo_bool_t is_bounded;
@@ -2377,23 +2362,24 @@ static cairo_status_t
     int origin_x = 0; /* squelch bogus compiler warning */
     int origin_y = 0; /* squelch bogus compiler warning */
     int bbox_x, bbox_y;
     char draw_surface[200];
 
     if (pattern->base.extend == CAIRO_EXTEND_PAD &&
 	pattern->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
     {
-	status = _cairo_pdf_surface_emit_padded_image_surface (surface,
-							       pdf_pattern,
-							       &pattern_resource,
-							       &pattern_width,
-							       &pattern_height,
-							       &origin_x,
-							       &origin_y);
+	status = _cairo_pdf_surface_add_padded_image_surface (surface,
+							      pattern,
+							      &pdf_pattern->extents,
+							      &pattern_resource,
+							      &pattern_width,
+							      &pattern_height,
+							      &origin_x,
+							      &origin_y);
     }
     else
     {
 	status = _cairo_pdf_surface_add_source_surface (surface,
 							pattern->surface,
 							pdf_pattern->pattern->filter,
 							&pattern_resource,
 							&pattern_width,
@@ -3351,40 +3337,57 @@ static cairo_status_t
 					  old_width,
 					  old_height);
 
     return status;
 }
 
 static cairo_status_t
 _cairo_pdf_surface_paint_surface_pattern (cairo_pdf_surface_t     *surface,
-					  cairo_surface_pattern_t *source)
+					  cairo_surface_pattern_t *source,
+					  const cairo_rectangle_int_t  *extents)
 {
     cairo_pdf_resource_t surface_res;
     int width, height;
     cairo_matrix_t cairo_p2d, pdf_p2d;
     cairo_status_t status;
     int alpha;
-
-    status = _cairo_pdf_surface_add_source_surface (surface,
-						    source->surface,
-						    source->base.filter,
-						    &surface_res,
-						    &width,
-						    &height);
+    int origin_x = 0;
+    int origin_y = 0;
+
+    if (source->base.extend == CAIRO_EXTEND_PAD &&
+	source->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
+    {
+	status = _cairo_pdf_surface_add_padded_image_surface (surface,
+							      source,
+							      extents,
+							      &surface_res,
+							      &width,
+							      &height,
+							      &origin_x,
+							      &origin_y);
+    } else {
+	status = _cairo_pdf_surface_add_source_surface (surface,
+							source->surface,
+							source->base.filter,
+							&surface_res,
+							&width,
+						 	&height);
+    }
     if (unlikely (status))
 	return status;
 
     cairo_p2d = source->base.matrix;
     status = cairo_matrix_invert (&cairo_p2d);
     /* cairo_pattern_set_matrix ensures the matrix is invertible */
     assert (status == CAIRO_STATUS_SUCCESS);
 
     pdf_p2d = surface->cairo_to_pdf;
     cairo_matrix_multiply (&pdf_p2d, &cairo_p2d, &pdf_p2d);
+    cairo_matrix_translate (&pdf_p2d, -origin_x, -origin_y);
     cairo_matrix_translate (&pdf_p2d, 0.0, height);
     cairo_matrix_scale (&pdf_p2d, 1.0, -1.0);
     if (source->surface->type != CAIRO_SURFACE_TYPE_RECORDING)
 	cairo_matrix_scale (&pdf_p2d, width, height);
 
     status = _cairo_pdf_operators_flush (&surface->pdf_operators);
     if (unlikely (status))
 	return status;
@@ -5309,20 +5312,38 @@ static cairo_int_status_t
     if (! _pattern_supported (pattern))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     if (_pdf_operator_supported (op)) {
 	if (pattern->type == CAIRO_PATTERN_TYPE_SURFACE) {
 	    cairo_surface_pattern_t *surface_pattern = (cairo_surface_pattern_t *) pattern;
 
 	    if (surface_pattern->surface->type == CAIRO_SURFACE_TYPE_RECORDING) {
-		if (pattern->extend == CAIRO_EXTEND_PAD)
-		    return CAIRO_INT_STATUS_UNSUPPORTED;
-		else
-		    return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
+		if (pattern->extend == CAIRO_EXTEND_PAD) {
+		    cairo_box_t box;
+		    cairo_rectangle_int_t rect;
+		    cairo_rectangle_int_t rec_extents;
+
+		    /* get the operation extents in pattern space */
+		    _cairo_box_from_rectangle (&box, extents);
+		    _cairo_matrix_transform_bounding_box_fixed (&pattern->matrix, &box, NULL);
+		    _cairo_box_round_to_rectangle (&box, &rect);
+
+		    /* Check if surface needs padding to fill extents */
+		    if (_cairo_surface_get_extents (surface_pattern->surface, &rec_extents)) {
+			if (_cairo_fixed_integer_ceil(box.p1.x) < rec_extents.x ||
+			    _cairo_fixed_integer_ceil(box.p1.y) < rec_extents.y ||
+			    _cairo_fixed_integer_floor(box.p2.y) > rec_extents.x + rec_extents.width ||
+			    _cairo_fixed_integer_floor(box.p2.y) > rec_extents.y + rec_extents.height)
+			{
+			    return CAIRO_INT_STATUS_UNSUPPORTED;
+			}
+		    }
+		}
+		return CAIRO_INT_STATUS_ANALYZE_RECORDING_SURFACE_PATTERN;
 	    }
 	}
 
 	return CAIRO_STATUS_SUCCESS;
     }
 
 
     /* The SOURCE operator is supported if the pattern is opaque or if
@@ -5435,17 +5456,18 @@ static cairo_int_status_t
     if (unlikely (status))
 	return status;
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
 	source->extend == CAIRO_EXTEND_NONE)
     {
 	_cairo_output_stream_printf (surface->output, "q\n");
 	status = _cairo_pdf_surface_paint_surface_pattern (surface,
-							   (cairo_surface_pattern_t *) source);
+							   (cairo_surface_pattern_t *) source,
+							   &extents.bounded);
 	if (unlikely (status))
 	    return status;
 
 	_cairo_output_stream_printf (surface->output, "Q\n");
 	return _cairo_output_stream_get_status (surface->output);
     }
 
     pattern_res.id = 0;
@@ -5806,31 +5828,33 @@ static cairo_int_status_t
     if (unlikely (status))
 	return status;
 
     status = _cairo_pdf_surface_select_operator (surface, op);
     if (unlikely (status))
 	return status;
 
     if (source->type == CAIRO_PATTERN_TYPE_SURFACE &&
-	source->extend == CAIRO_EXTEND_NONE)
+	(source->extend == CAIRO_EXTEND_NONE ||
+	 source->extend == CAIRO_EXTEND_PAD))
     {
 	status = _cairo_pdf_operators_flush (&surface->pdf_operators);
 	if (unlikely (status))
 	    return status;
 
 	_cairo_output_stream_printf (surface->output, "q\n");
 	status =  _cairo_pdf_operators_clip (&surface->pdf_operators,
 					     path,
 					     fill_rule);
 	if (unlikely (status))
 	    return status;
 
 	status = _cairo_pdf_surface_paint_surface_pattern (surface,
-							   (cairo_surface_pattern_t *) source);
+							   (cairo_surface_pattern_t *) source,
+							   &extents.bounded);
 	if (unlikely (status))
 	    return status;
 
 	_cairo_output_stream_printf (surface->output, "Q\n");
 	return _cairo_output_stream_get_status (surface->output);
     }
 
     pattern_res.id = 0;
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -613,20 +613,29 @@ SampleValue(float aPortion, Animation& a
   if (aAnimation.property() == eCSSProperty_opacity) {
     *aValue = interpolatedValue.GetFloatValue();
     return;
   }
 
   nsCSSValueList* interpolatedList = interpolatedValue.GetCSSValueListValue();
 
   TransformData& data = aAnimation.data().get_TransformData();
+  nsPoint origin = data.origin();
+  int32_t auPerCSSPixel = nsDeviceContext::AppUnitsPerCSSPixel();
   gfx3DMatrix transform =
-    nsDisplayTransform::GetResultingTransformMatrix(nullptr, data.origin(), nsDeviceContext::AppUnitsPerCSSPixel(),
-                                                    &data.bounds(), interpolatedList, &data.mozOrigin(),
-                                                    &data.perspectiveOrigin(), &data.perspective());
+    nsDisplayTransform::GetResultingTransformMatrix(
+      nullptr, origin, auPerCSSPixel,
+      &data.bounds(), interpolatedList, &data.mozOrigin(),
+      &data.perspectiveOrigin(), &data.perspective());
+  // NB: See nsDisplayTransform::GetTransform().
+  gfxPoint3D newOrigin =
+    gfxPoint3D(NS_round(NSAppUnitsToFloatPixels(origin.x, auPerCSSPixel)),
+               NS_round(NSAppUnitsToFloatPixels(origin.y, auPerCSSPixel)),
+               0.0f);
+  transform.Translate(newOrigin);
 
   InfallibleTArray<TransformFunction>* functions = new InfallibleTArray<TransformFunction>();
   functions->AppendElement(TransformMatrix(transform));
   *aValue = *functions;
 }
 
 static bool
 SampleAnimations(Layer* aLayer, TimeStamp aPoint)
--- a/intl/hyphenation/src/hyphen.c
+++ b/intl/hyphenation/src/hyphen.c
@@ -417,32 +417,34 @@ for (k = 0; k < 2; k++) {
       for (i=0;i<MAX_NAME;i++)
         if ((dict[k]->cset[i] == '\r') || (dict[k]->cset[i] == '\n'))
           dict[k]->cset[i] = 0;
     } else {
       dict[k]->cset[0] = 0;
     }
     dict[k]->utf8 = (strcmp(dict[k]->cset, "UTF-8") == 0);
   } else {
-    strcpy(dict[k]->cset, dict[0]->cset);
+    strncpy(dict[k]->cset, dict[0]->cset, sizeof(dict[k]->cset)-1);
+    dict[k]->cset[sizeof(dict[k]->cset)-1] = '\0';
     dict[k]->utf8 = dict[0]->utf8;
   }
 
   if (k == 0 || nextlevel) {
     while (fgets (buf, sizeof(buf), f) != NULL) {
       if (strncmp(buf, "NEXTLEVEL", 9) == 0) {
 	nextlevel = 1;
 	break;
       } else if (buf[0] != '%') hnj_hyphen_load_line(buf, dict[k], hashtab);
     }
   } else if (k == 1) {
     /* default first level: hyphen and ASCII apostrophe */
-    if (!dict[0]->utf8) hnj_hyphen_load_line("NOHYPHEN '\n", dict[k], hashtab);
-    else hnj_hyphen_load_line("NOHYPHEN ',\xe2\x80\x93,\xe2\x80\x99\n", dict[k], hashtab);
-    strcpy(buf, "1-1/=,1,1\n"); // buf rewritten by hnj_hyphen_load here
+    if (!dict[0]->utf8) hnj_hyphen_load_line("NOHYPHEN ',-\n", dict[k], hashtab);
+    else hnj_hyphen_load_line("NOHYPHEN ',\xe2\x80\x93,\xe2\x80\x99,-\n", dict[k], hashtab);
+    strncpy(buf, "1-1\n", MAX_CHARS-1); // buf rewritten by hnj_hyphen_load here
+    buf[MAX_CHARS-1] = '\0';
     hnj_hyphen_load_line(buf, dict[k], hashtab); /* remove hyphen */
     hnj_hyphen_load_line("1'1\n", dict[k], hashtab); /* ASCII apostrophe */
     if (dict[0]->utf8) {
       hnj_hyphen_load_line("1\xe2\x80\x93" "1\n", dict[k], hashtab); /* endash */
       hnj_hyphen_load_line("1\xe2\x80\x99" "1\n", dict[k], hashtab); /* apostrophe */
     }
   }
 
@@ -538,29 +540,25 @@ void hnj_hyphen_free (HyphenDict *dict)
 }
 
 #define MAX_WORD 256
 
 int hnj_hyphen_hyphenate (HyphenDict *dict,
 			   const char *word, int word_size,
 			   char *hyphens)
 {
-  char prep_word_buf[MAX_WORD];
   char *prep_word;
   int i, j, k;
   int state;
   char ch;
   HyphenState *hstate;
   char *match;
   int offset;
 
-  if (word_size + 3 < MAX_WORD)
-    prep_word = prep_word_buf;
-  else
-    prep_word = hnj_malloc (word_size + 3);
+  prep_word = hnj_malloc (word_size + 3);
 
   j = 0;
   prep_word[j++] = '.';
 
   for (i = 0; i < word_size; i++) {
     if (word[i] <= '9' && word[i] >= '0') {
       prep_word[j++] = '.';
     } else {
@@ -657,18 +655,17 @@ int hnj_hyphen_hyphenate (HyphenDict *di
 #else
     hyphens[i] = hyphens[i + 1];
 #endif
   hyphens[0] = '0';
   for (; i < word_size; i++)
     hyphens[i] = '0';
   hyphens[word_size] = '\0';
 
-  if (prep_word != prep_word_buf)
-    hnj_free (prep_word);
+  hnj_free (prep_word);
     
   return 0;    
 }
 
 /* Unicode ligature length */
 int hnj_ligature(unsigned char c) {
     switch (c) {
         case 0x80:			/* ff */
@@ -732,76 +729,66 @@ int hnj_hyphen_lhmin(int utf8, const cha
        }
     } while (utf8 && (word[j] & 0xc0) == 0x80);
     return 0;
 }
 
 int hnj_hyphen_rhmin(int utf8, const char *word, int word_size, char * hyphens,
 	char *** rep, int ** pos, int ** cut, int rhmin)
 {
-    int i = 1;
+    int i = 0;
     int j;
 
     // ignore numbers
     for (j = word_size - 1; j > 0 && word[j] <= '9' && word[j] >= '0'; j--) i--;
 
-    for (j = word_size - 2; i < rhmin && j > 0; j--) {
+    for (j = word_size - 1; i < rhmin && j > 0; j--) {
       // check length of the non-standard part
       if (*rep && *pos && *cut && (*rep)[j]) {
         char * rh = strchr((*rep)[j], '=');
         if (rh && (hnj_hyphen_strnlen(word + j - (*pos)[j] + (*cut)[j] + 1, 100, utf8) +
           hnj_hyphen_strnlen(rh + 1, strlen(rh + 1), utf8)) < rhmin) {
             free((*rep)[j]);
             (*rep)[j] = NULL;
             hyphens[j] = '0';
           }
        } else {
          hyphens[j] = '0';
        }
-       if (!utf8 || (word[j] & 0xc0) != 0xc0) i++;
+       if (!utf8 || (word[j] & 0xc0) == 0xc0 || (word[j] & 0x80) != 0x80) i++;
     }
     return 0;
 }
 
 // recursive function for compound level hyphenation
 int hnj_hyphen_hyph_(HyphenDict *dict, const char *word, int word_size,
     char * hyphens, char *** rep, int ** pos, int ** cut,
     int clhmin, int crhmin, int lend, int rend)
 {
-  char prep_word_buf[MAX_WORD];
   char *prep_word;
   int i, j, k;
   int state;
   char ch;
   HyphenState *hstate;
   char *match;
   char *repl;
   signed char replindex;
   signed char replcut;
   int offset;
-  int matchlen_buf[MAX_CHARS];
-  int matchindex_buf[MAX_CHARS];
-  char * matchrepl_buf[MAX_CHARS];
   int * matchlen;
   int * matchindex;
   char ** matchrepl;  
   int isrepl = 0;
   int nHyphCount;
 
-  if (word_size + 3 < MAX_CHARS) {
-    prep_word = prep_word_buf;
-    matchlen = matchlen_buf;
-    matchindex = matchindex_buf;
-    matchrepl = matchrepl_buf;
-  } else {
-    prep_word = hnj_malloc (word_size + 3);
-    matchlen = hnj_malloc ((word_size + 3) * sizeof(int));
-    matchindex = hnj_malloc ((word_size + 3) * sizeof(int));
-    matchrepl = hnj_malloc ((word_size + 3) * sizeof(char *));
-  }
+  size_t prep_word_size = word_size + 3;
+  prep_word = hnj_malloc (prep_word_size);
+  matchlen = hnj_malloc ((word_size + 3) * sizeof(int));
+  matchindex = hnj_malloc ((word_size + 3) * sizeof(int));
+  matchrepl = hnj_malloc ((word_size + 3) * sizeof(char *));
 
   j = 0;
   prep_word[j++] = '.';
   
   for (i = 0; i < word_size; i++) {
     if (word[i] <= '9' && word[i] >= '0') {
       prep_word[j++] = '.';
     } else {
@@ -928,74 +915,60 @@ int hnj_hyphen_hyph_(HyphenDict *dict, c
        nHyphCount = 0;
        for (i = 0; i < word_size; i++)
           if (hyphens[i]&1)
              nHyphCount++;
        j = 0;
        for (i = 0; i < word_size; i++) {
            if (isrepl && (matchindex[i] >= 0) && matchrepl[matchindex[i]]) { 
                 if (rep && pos && cut) {
-                    if (!*rep && !*pos && !*cut) {
-                        int k;
-                        *rep = (char **) malloc(sizeof(char *) * word_size);
-                        *pos = (int *) malloc(sizeof(int) * word_size);
-                        *cut = (int *) malloc(sizeof(int) * word_size);
-                        for (k = 0; k < word_size; k++) {
-                            (*rep)[k] = NULL;
-                            (*pos)[k] = 0;
-                            (*cut)[k] = 0;
-                        }
+                    if (!*rep)
+                        *rep = (char **) calloc(word_size, sizeof(char *));
+                    if (!*pos)
+                        *pos = (int *) calloc(word_size, sizeof(int));
+                    if (!*cut) {
+                        *cut = (int *) calloc(word_size, sizeof(int));
                     }
                     (*rep)[matchindex[i] - 1] = hnj_strdup(matchrepl[matchindex[i]]);
                     (*pos)[matchindex[i] - 1] = matchindex[i] - i;
                     (*cut)[matchindex[i] - 1] = matchlen[i];
                 }
                 j += strlen(matchrepl[matchindex[i]]);
                 i += matchlen[i] - 1;
           }
        }
 
-  if (matchrepl != matchrepl_buf) {
-    hnj_free (matchrepl);
-    hnj_free (matchlen);
-    hnj_free (matchindex);
-  }
+  hnj_free (matchrepl);
+  hnj_free (matchlen);
+  hnj_free (matchindex);
 
   // recursive hyphenation of the first (compound) level segments
   if (dict->nextlevel) {
-     char * rep2_buf[MAX_WORD];
-     int pos2_buf[MAX_WORD];
-     int cut2_buf[MAX_WORD];
-     char hyphens2_buf[MAX_WORD];
      char ** rep2;
      int * pos2;
      int * cut2;
      char * hyphens2;
      int begin = 0;
-     if (word_size < MAX_CHARS) {
-        rep2 = rep2_buf;
-        pos2 = pos2_buf;
-        cut2 = cut2_buf;
-        hyphens2 = hyphens2_buf;
-     } else {
-        rep2 = hnj_malloc (word_size * sizeof(char *));
-        pos2 = hnj_malloc (word_size * sizeof(int));
-        cut2 = hnj_malloc (word_size * sizeof(int));
-        hyphens2 = hnj_malloc (word_size);
-     }
+
+     rep2 = hnj_malloc (word_size * sizeof(char *));
+     pos2 = hnj_malloc (word_size * sizeof(int));
+     cut2 = hnj_malloc (word_size * sizeof(int));
+     hyphens2 = hnj_malloc (word_size + 3);
      for (i = 0; i < word_size; i++) rep2[i] = NULL;
      for (i = 0; i < word_size; i++) if 
         (hyphens[i]&1 || (begin > 0 && i + 1 == word_size)) {
         if (i - begin > 1) {
             int hyph = 0;
             prep_word[i + 2] = '\0';
             /* non-standard hyphenation at compound boundary (Schiffahrt) */
-            if (*rep && *pos && *cut && (*rep)[i]) {
+            if (rep && *rep && *pos && *cut && (*rep)[i]) {
                 char * l = strchr((*rep)[i], '=');
-                strcpy(prep_word + 2 + i - (*pos)[i], (*rep)[i]);
+                size_t offset = 2 + i - (*pos)[i];
+                strncpy(prep_word + offset, (*rep)[i], prep_word_size - offset - 1);
+                prep_word[prep_word_size - 1] = '\0';
                 if (l) {
                     hyph = (l - (*rep)[i]) - (*pos)[i];
                     prep_word[2 + i + hyph] = '\0';
                 }
             }
             hnj_hyphen_hyph_(dict, prep_word + begin + 1, i - begin + 1 + hyph,
                 hyphens2, &rep2, &pos2, &cut2, clhmin,
                 crhmin, (begin > 0 ? 0 : lend), (hyphens[i]&1 ? 0 : rend));
@@ -1015,42 +988,42 @@ int hnj_hyphen_hyph_(HyphenDict *dict, c
                     }
                     (*rep)[begin + j] = rep2[j];
                     (*pos)[begin + j] = pos2[j];
                     (*cut)[begin + j] = cut2[j];
                 }
             }
             prep_word[i + 2] = word[i + 1];
             if (*rep && *pos && *cut && (*rep)[i]) {
-                strcpy(prep_word + 1, word);
+                size_t offset = 1;
+                strncpy(prep_word + offset, word, prep_word_size - offset - 1);
+                prep_word[prep_word_size - 1] = '\0';
             }
         }
         begin = i + 1;
         for (j = 0; j < word_size; j++) rep2[j] = NULL;
      }
      
      // non-compound
      if (begin == 0) {
         hnj_hyphen_hyph_(dict->nextlevel, word, word_size,
             hyphens, rep, pos, cut, clhmin, crhmin, lend, rend);
         if (!lend) hnj_hyphen_lhmin(dict->utf8, word, word_size, hyphens,
             rep, pos, cut, clhmin);
         if (!rend) hnj_hyphen_rhmin(dict->utf8, word, word_size, hyphens,
             rep, pos, cut, crhmin);
      }
      
-     if (rep2 != rep2_buf) {
-        free(rep2);
-        free(cut2);
-        free(pos2);
-        free(hyphens2);
-     }
+     free(rep2);
+     free(cut2);
+     free(pos2);
+     free(hyphens2);
   }
 
-  if (prep_word != prep_word_buf) hnj_free (prep_word);
+  hnj_free (prep_word);
   return 0;
 }
 
 /* UTF-8 normalization of hyphen and non-standard positions */
 int hnj_hyphen_norm(const char *word, int word_size, char * hyphens,
 	char *** rep, int ** pos, int ** cut)
 {
   int i, j, k;
@@ -1090,22 +1063,26 @@ int hnj_hyphen_norm(const char *word, in
 #endif
   return 0;
 }
 
 /* get the word with all possible hyphenations (output: hyphword) */
 void hnj_hyphen_hyphword(const char * word, int l, const char * hyphens, 
     char * hyphword, char *** rep, int ** pos, int ** cut)
 {
+  int hyphenslen = l + 5;
+
   int i, j;
   for (i = 0, j = 0; i < l; i++, j++) {
     if (hyphens[i]&1) {
       hyphword[j] = word[i];
       if (*rep && *pos && *cut && (*rep)[i]) {
-        strcpy(hyphword + j - (*pos)[i] + 1, (*rep)[i]);
+        size_t offset = j - (*pos)[i] + 1;
+        strncpy(hyphword + offset, (*rep)[i], hyphenslen - offset - 1);
+        hyphword[hyphenslen-1] = '\0';
         j += strlen((*rep)[i]) - (*pos)[i];
         i += (*cut)[i] - (*pos)[i];
       } else hyphword[++j] = '=';
     } else hyphword[j] = word[i];
   }
   hyphword[j] = '\0';
 }
 
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -25,16 +25,17 @@ IPDLDIRS =  \
   uriloader/exthandler \
   dom/devicestorage \
   dom/indexedDB/ipc \
   dom/bluetooth/ipc \
   dom/plugins/ipc  \
   dom/ipc \
   dom/sms/src/ipc \
   dom/src/storage \
+  dom/network/src \
   gfx/layers/ipc \
   hal/sandbox \
   ipc/glue  \
   ipc/testshell  \
   js/ipc  \
   layout/ipc \
   netwerk/ipc  \
   netwerk/protocol/ftp \
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -552,25 +552,20 @@ class CallCompiler : public BaseCompiler
         repatch.relink(oolCall, fptr);
     }
 
 #ifdef JS_ION
     bool generateIonStub()
     {
         RecompilationMonitor monitor(cx);
 
-        if (f.script()->function()) {
-            f.script()->uninlineable = true;
-            MarkTypeObjectFlags(cx, f.script()->function(), types::OBJECT_FLAG_UNINLINEABLE);
-        }
-
-        /* Don't touch the IC if the call triggered a recompilation. */
-        if (monitor.recompiled())
-            return true;
-
+        /*
+         * When IonMonkey is enabled we never inline in JM. So do not cause any
+         * recompilation by setting the UNINLINEABLE flag.
+         */
         JS_ASSERT(!f.regs.inlined());
 
         Assembler masm;
         Registers regs(Registers::AvailRegs);
 
         /* Reserve this, so we don't take something setupFallibleABICall will use. */
         regs.takeReg(Registers::ClobberInCall);
 
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -298,16 +298,17 @@ LOCAL_INCLUDES	+= -I$(srcdir)/../base \
 		   -I$(topsrcdir)/editor/txtsvc/src \
 		   -I$(topsrcdir)/editor/composer/src \
 		   -I$(topsrcdir)/js/xpconnect/src \
 		   -I$(topsrcdir)/js/xpconnect/loader \
 		   -I$(topsrcdir)/caps/include \
 		   -I$(topsrcdir)/netwerk/base/src \
 		   -I$(topsrcdir)/content/svg/content/src \
 		   -I$(topsrcdir)/extensions/cookie \
+		   -I$(topsrcdir)/netwerk/cookie \
 		   $(NULL)
 
 ifdef MOZ_B2G_RIL #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/system/gonk
 endif #}
 
 ifdef MOZ_B2G_FM #{
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/fm
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -88,16 +88,17 @@
 #include "nsIControllerContext.h"
 #include "nsDOMScriptObjectFactory.h"
 #include "nsDOMStorage.h"
 #include "nsJSON.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
 #include "mozilla/dom/DOMRequest.h"
 #include "mozilla/OSFileConstants.h"
 #include "mozilla/dom/Activity.h"
+#include "mozilla/dom/network/TCPSocketChild.h"
 
 #ifdef MOZ_B2G_RIL
 #include "SystemWorkerManager.h"
 using mozilla::dom::gonk::SystemWorkerManager;
 #define SYSTEMWORKERMANAGER_CID \
   {0xd53b6524, 0x6ac3, 0x42b0, {0xae, 0xca, 0x62, 0xb3, 0xc4, 0xe5, 0x2b, 0x04}}
 #define SYSTEMWORKERMANAGER_CONTRACTID \
   "@mozilla.org/telephony/system-worker-manager;1"
@@ -243,16 +244,17 @@ static void Shutdown();
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::file;
 using namespace mozilla::dom::sms;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::dom::power::PowerManagerService;
+using mozilla::dom::TCPSocketChild;
 
 
 // Transformiix
 /* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
 #define TRANSFORMIIX_NODESET_CID \
 { 0x5d5d92cd, 0x6bf8, 0x11d9, { 0xbf, 0x4a, 0x0, 0x0a, 0x95, 0xdc, 0x23, 0x4c } }
 
 #define TRANSFORMIIX_NODESET_CONTRACTID \
@@ -652,16 +654,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMixedCo
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsPrincipal)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSecurityNameSet)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsSystemPrincipal,
     nsScriptSecurityManager::SystemPrincipalSingletonConstructor)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsNullPrincipal, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsStructuredCloneContainer)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(OSFileConstantsService)
+NS_GENERIC_FACTORY_CONSTRUCTOR(TCPSocketChild)
 
 static nsresult
 Construct_nsIScriptSecurityManager(nsISupports *aOuter, REFNSIID aIID, 
                                    void **aResult)
 {
     if (!aResult)
         return NS_ERROR_NULL_POINTER;
     *aResult = nullptr;
@@ -819,16 +822,17 @@ NS_DEFINE_NAMED_CID(NS_HAPTICFEEDBACK_CI
 #endif
 #endif
 NS_DEFINE_NAMED_CID(SMS_SERVICE_CID);
 NS_DEFINE_NAMED_CID(SMS_DATABASE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(SMS_REQUEST_MANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_POWERMANAGERSERVICE_CID);
 NS_DEFINE_NAMED_CID(OSFILECONSTANTSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_ALARMHALSERVICE_CID);
+NS_DEFINE_NAMED_CID(TCPSOCKETCHILD_CID);
 
 static nsresult
 CreateWindowCommandTableConstructor(nsISupports *aOuter,
                                     REFNSIID aIID, void **aResult)
 {
   nsresult rv;
   nsCOMPtr<nsIControllerCommandTable> commandTable =
       do_CreateInstance(NS_CONTROLLERCOMMANDTABLE_CONTRACTID, &rv);
@@ -1097,16 +1101,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_STRUCTUREDCLONECONTAINER_CID, false, NULL, nsStructuredCloneContainerConstructor },
   { &kNS_DOMMUTATIONOBSERVER_CID, false, NULL, nsDOMMutationObserverConstructor },
   { &kSMS_SERVICE_CID, false, NULL, nsISmsServiceConstructor },
   { &kSMS_DATABASE_SERVICE_CID, false, NULL, nsISmsDatabaseServiceConstructor },
   { &kSMS_REQUEST_MANAGER_CID, false, NULL, SmsRequestManagerConstructor },
   { &kNS_POWERMANAGERSERVICE_CID, false, NULL, nsIPowerManagerServiceConstructor },
   { &kOSFILECONSTANTSSERVICE_CID, true, NULL, OSFileConstantsServiceConstructor },
   { &kNS_ALARMHALSERVICE_CID, false, NULL, nsIAlarmHalServiceConstructor },
+  { &kTCPSOCKETCHILD_CID, false, NULL, TCPSocketChildConstructor },
   { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kLayoutContracts[] = {
   XPCONNECT_CONTRACTS
   { "@mozilla.org/layout/xul-boxobject;1", &kNS_BOXOBJECT_CID },
 #ifdef MOZ_XUL
   { "@mozilla.org/layout/xul-boxobject-listbox;1", &kNS_LISTBOXOBJECT_CID },
@@ -1240,16 +1245,17 @@ static const mozilla::Module::ContractID
   { NS_STRUCTUREDCLONECONTAINER_CONTRACTID, &kNS_STRUCTUREDCLONECONTAINER_CID },
   { NS_DOMMUTATIONOBSERVER_CONTRACTID, &kNS_DOMMUTATIONOBSERVER_CID },
   { SMS_SERVICE_CONTRACTID, &kSMS_SERVICE_CID },
   { SMS_DATABASE_SERVICE_CONTRACTID, &kSMS_DATABASE_SERVICE_CID },
   { SMS_REQUEST_MANAGER_CONTRACTID, &kSMS_REQUEST_MANAGER_CID },
   { POWERMANAGERSERVICE_CONTRACTID, &kNS_POWERMANAGERSERVICE_CID },
   { OSFILECONSTANTSSERVICE_CONTRACTID, &kOSFILECONSTANTSSERVICE_CID },
   { ALARMHALSERVICE_CONTRACTID, &kNS_ALARMHALSERVICE_CID },
+  { "@mozilla.org/tcp-socket-child;1", &kTCPSOCKETCHILD_CID },
   { NULL }
 };
 
 static const mozilla::Module::CategoryEntry kLayoutCategories[] = {
   XPCONNECT_CATEGORIES
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY, "Image", NS_HTMLIMGELEMENT_CONTRACTID },
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_PROTO_ALIAS_CATEGORY, "Image", "HTMLImageElement" },
   { JAVASCRIPT_GLOBAL_CONSTRUCTOR_CATEGORY, "Option", NS_HTMLOPTIONELEMENT_CONTRACTID },
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -95,16 +95,17 @@
 #include "nsRefreshDriver.h"
 #include "nsDOMMutationObserver.h"
 #include "nsHyphenationManager.h"
 #include "nsEditorSpellCheck.h"
 #include "nsWindowMemoryReporter.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ipc/ProcessPriorityManager.h"
 #include "nsPermissionManager.h"
+#include "nsCookieService.h"
 
 extern void NS_ShutdownChainItemPool();
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
 nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
@@ -250,16 +251,17 @@ nsLayoutStatics::Initialize()
 
   nsWindowMemoryReporter::Init();
 
   nsSVGUtils::Init();
 
   InitProcessPriorityManager();
 
   nsPermissionManager::AppUninstallObserverInit();
+  nsCookieService::AppUninstallObserverInit();
 
   nsDOMStorageBaseDB::Init();
 
   return NS_OK;
 }
 
 void
 nsLayoutStatics::Shutdown()
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -345,16 +345,17 @@
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
 
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 #ifdef MOZ_SAFE_BROWSING
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
--- a/mobile/xul/installer/package-manifest.in
+++ b/mobile/xul/installer/package-manifest.in
@@ -432,16 +432,17 @@
 @BINPATH@/components/Weave.js
 @BINPATH@/components/WeaveCrypto.manifest
 @BINPATH@/components/WeaveCrypto.js
 #endif
 @BINPATH@/components/TelemetryPing.js
 @BINPATH@/components/TelemetryPing.manifest
 
 @BINPATH@/components/TCPSocket.js
+@BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 ; Modules
 @BINPATH@/modules/*
 
 ; Safe Browsing
 @BINPATH@/components/nsURLClassifier.manifest
 @BINPATH@/components/nsUrlClassifierHashCompleter.js
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -43,16 +43,18 @@
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsIPrivateBrowsingService.h"
 #include "nsNetCID.h"
 #include "mozilla/storage.h"
 #include "mozilla/Util.h" // for DebugOnly
 #include "mozilla/Attributes.h"
+#include "nsIAppsService.h"
+#include "mozIApplication.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 // Create key from baseDomain that will access the default cookie namespace.
 // TODO: When we figure out what the API will look like for nsICookieManager{2}
 // on content processes (see bug 777620), change to use the appropriate app
 // namespace.  For now those IDLs aren't supported on child processes.
@@ -536,16 +538,47 @@ public:
   {
     gCookieService->HandleDBClosed(mDBState);
     return NS_OK;
   }
 };
 
 NS_IMPL_ISUPPORTS1(CloseCookieDBListener, mozIStorageCompletionCallback)
 
+namespace {
+
+class AppUninstallObserver MOZ_FINAL : public nsIObserver {
+public:
+  NS_DECL_ISUPPORTS
+
+  // nsIObserver implementation.
+  NS_IMETHODIMP
+  Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *data)
+  {
+    MOZ_ASSERT(!nsCRT::strcmp(aTopic, "webapps-uninstall"));
+
+    nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
+    nsCOMPtr<mozIApplication> app;
+
+    appsService->GetAppFromObserverMessage(nsAutoString(data), getter_AddRefs(app));
+    NS_ENSURE_TRUE(app, NS_ERROR_UNEXPECTED);
+
+    uint32_t appId;
+    app->GetLocalId(&appId);
+    MOZ_ASSERT(appId != NECKO_NO_APP_ID);
+
+    nsCOMPtr<nsICookieManager2> cookieManager = do_GetService(NS_COOKIEMANAGER_CONTRACTID);
+    return cookieManager->RemoveCookiesForApp(appId, false);
+  }
+};
+
+NS_IMPL_ISUPPORTS1(AppUninstallObserver, nsIObserver)
+
+} // anonymous namespace
+
 /******************************************************************************
  * nsCookieService impl:
  * singleton instance ctor/dtor methods
  ******************************************************************************/
 
 nsICookieService*
 nsCookieService::GetXPCOMSingleton()
 {
@@ -577,16 +610,23 @@ nsCookieService::GetSingleton()
     if (NS_FAILED(gCookieService->Init())) {
       NS_RELEASE(gCookieService);
     }
   }
 
   return gCookieService;
 }
 
+/* static */ void
+nsCookieService::AppUninstallObserverInit()
+{
+  nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1");
+  observerService->AddObserver(new AppUninstallObserver(), "webapps-uninstall", /* holdsWeak= */ false);
+}
+
 /******************************************************************************
  * nsCookieService impl:
  * public methods
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS5(nsCookieService,
                    nsICookieService,
                    nsICookieManager,
@@ -1795,21 +1835,21 @@ nsCookieService::Add(const nsACString &a
   if (!cookie) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   AddInternal(DEFAULT_APP_KEY(baseDomain), cookie, currentTimeInUsec, nullptr, nullptr, true);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsCookieService::Remove(const nsACString &aHost,
-                        const nsACString &aName,
-                        const nsACString &aPath,
-                        bool             aBlocked)
+
+nsresult
+nsCookieService::Remove(const nsACString& aHost, uint32_t aAppId,
+                        bool aInBrowserElement, const nsACString& aName,
+                        const nsACString& aPath, bool aBlocked)
 {
   if (!mDBState) {
     NS_WARNING("No DBState! Profile already closed?");
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   // first, normalize the hostname, and fail if it contains illegal characters.
   nsAutoCString host(aHost);
@@ -1817,17 +1857,17 @@ nsCookieService::Remove(const nsACString
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString baseDomain;
   rv = GetBaseDomainFromHost(host, baseDomain);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsListIter matchIter;
   nsRefPtr<nsCookie> cookie;
-  if (FindCookie(DEFAULT_APP_KEY(baseDomain),
+  if (FindCookie(nsCookieKey(baseDomain, aAppId, aInBrowserElement),
                  host,
                  PromiseFlatCString(aName),
                  PromiseFlatCString(aPath),
                  matchIter)) {
     cookie = matchIter.Cookie();
     RemoveCookieFromList(matchIter);
   }
 
@@ -1849,16 +1889,25 @@ nsCookieService::Remove(const nsACString
   if (cookie) {
     // Everything's done. Notify observers.
     NotifyChanged(cookie, NS_LITERAL_STRING("deleted").get());
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCookieService::Remove(const nsACString &aHost,
+                        const nsACString &aName,
+                        const nsACString &aPath,
+                        bool             aBlocked)
+{
+  return Remove(aHost, NECKO_NO_APP_ID, false, aName, aPath, aBlocked);
+}
+
 /******************************************************************************
  * nsCookieService impl:
  * private file I/O functions
  ******************************************************************************/
 
 // Begin an asynchronous read from the database.
 OpenDBResult
 nsCookieService::Read()
@@ -3712,16 +3761,121 @@ nsCookieService::GetCookiesFromHost(cons
   const nsCookieEntry::ArrayType &cookies = entry->GetCookies();
   for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
     cookieList.AppendObject(cookies[i]);
   }
 
   return NS_NewArrayEnumerator(aEnumerator, cookieList);
 }
 
+namespace {
+
+/**
+ * This structure is used as a in/out parameter when enumerating the cookies
+ * for an app.
+ * It will contain the app id and onlyBrowserElement flag information as input
+ * and will contain the array of matching cookies as output.
+ */
+struct GetCookiesForAppStruct {
+  uint32_t              appId;
+  bool                  onlyBrowserElement;
+  nsCOMArray<nsICookie> cookies;
+
+  GetCookiesForAppStruct() MOZ_DELETE;
+  GetCookiesForAppStruct(uint32_t aAppId, bool aOnlyBrowserElement)
+    : appId(aAppId)
+    , onlyBrowserElement(aOnlyBrowserElement)
+  {}
+};
+
+} // anonymous namespace
+
+/* static */ PLDHashOperator
+nsCookieService::GetCookiesForApp(nsCookieEntry* entry, void* arg)
+{
+  GetCookiesForAppStruct* data = static_cast<GetCookiesForAppStruct*>(arg);
+
+  if (entry->mAppId != data->appId ||
+      (data->onlyBrowserElement && !entry->mInBrowserElement)) {
+    return PL_DHASH_NEXT;
+  }
+
+  const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
+
+  for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
+    data->cookies.AppendObject(cookies[i]);
+  }
+
+  return PL_DHASH_NEXT;
+}
+
+NS_IMETHODIMP
+nsCookieService::GetCookiesForApp(uint32_t aAppId, bool aOnlyBrowserElement,
+                                  nsISimpleEnumerator** aEnumerator)
+{
+  if (!mDBState) {
+    NS_WARNING("No DBState! Profile already closed?");
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // TODO: For the moment NECKO_UNKNOWN_APP_ID isn't working well with Necko.
+  // This should be uncommented with bug 794023.
+  NS_ENSURE_TRUE(aAppId != NECKO_NO_APP_ID /*&& aAppId != NECKO_UNKNOWN_APP_ID*/,
+                 NS_ERROR_INVALID_ARG);
+
+  GetCookiesForAppStruct data(aAppId, aOnlyBrowserElement);
+  mDBState->hostTable.EnumerateEntries(GetCookiesForApp, &data);
+
+  return NS_NewArrayEnumerator(aEnumerator, data.cookies);
+}
+
+NS_IMETHODIMP
+nsCookieService::RemoveCookiesForApp(uint32_t aAppId, bool aOnlyBrowserElement)
+{
+  nsCOMPtr<nsISimpleEnumerator> enumerator;
+  nsresult rv = GetCookiesForApp(aAppId, aOnlyBrowserElement,
+                                 getter_AddRefs(enumerator));
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool hasMore;
+  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore) {
+    nsCOMPtr<nsICookie> cookie;
+    rv = enumerator->GetNext(getter_AddRefs(cookie));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsAutoCString host;
+    cookie->GetHost(host);
+
+    nsAutoCString name;
+    cookie->GetName(name);
+
+    nsAutoCString path;
+    cookie->GetPath(path);
+
+    // nsICookie do not carry the appId/inBrowserElement information.
+    // That means we have to guess. This is easy for appId but not for
+    // inBrowserElement flag.
+    // A simple solution is to always ask to remove the cookie with
+    // inBrowserElement = true and only ask for the other one to be removed if
+    // we happen to be in the case of !aOnlyBrowserElement.
+    // Anyway, with this solution, we will likely be looking for unexistant
+    // cookies.
+    //
+    // NOTE: we could make this better by getting nsCookieEntry objects instead
+    // of plain nsICookie.
+    Remove(host, aAppId, true, name, path, false);
+    if (!aOnlyBrowserElement) {
+      Remove(host, aAppId, false, name, path, false);
+    }
+  }
+
+  return NS_OK;
+}
+
 // find an exact cookie specified by host, name, and path that hasn't expired.
 bool
 nsCookieService::FindCookie(const nsCookieKey    &aKey,
                             const nsAFlatCString &aHost,
                             const nsAFlatCString &aName,
                             const nsAFlatCString &aPath,
                             nsListIter           &aIter)
 {
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -234,16 +234,24 @@ class nsCookieService : public nsICookie
     NS_DECL_NSICOOKIEMANAGER
     NS_DECL_NSICOOKIEMANAGER2
 
     nsCookieService();
     virtual ~nsCookieService();
     static nsICookieService*      GetXPCOMSingleton();
     nsresult                      Init();
 
+  /**
+   * Start watching the observer service for messages indicating that an app has
+   * been uninstalled.  When an app is uninstalled, we get the cookie service
+   * (thus instantiating it, if necessary) and clear all the cookies for that
+   * app.
+   */
+  static void AppUninstallObserverInit();
+
   protected:
     void                          PrefChanged(nsIPrefBranch *aPrefBranch);
     void                          InitDBStates();
     OpenDBResult                  TryInitDB(bool aDeleteExistingDB);
     nsresult                      CreateTable();
     void                          CloseDBStates();
     void                          CloseDefaultDBConnection();
     void                          HandleDBClosed(DBState* aDBState);
@@ -278,16 +286,31 @@ class nsCookieService : public nsICookie
     already_AddRefed<nsIArray>    PurgeCookies(int64_t aCurrentTimeInUsec);
     bool                          FindCookie(const nsCookieKey& aKey, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
     static void                   FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsListIter &aIter);
     void                          NotifyRejected(nsIURI *aHostURI);
     void                          NotifyChanged(nsISupports *aSubject, const PRUnichar *aData);
     void                          NotifyPurged(nsICookie2* aCookie);
     already_AddRefed<nsIArray>    CreatePurgeList(nsICookie2* aCookie);
 
+    /**
+     * This method is used to iterate the cookie hash table and select the ones
+     * that are part of a specific app.
+     */
+    static PLDHashOperator GetCookiesForApp(nsCookieEntry* entry, void* arg);
+
+    /**
+     * This method is a helper that allows calling nsICookieManager::Remove()
+     * with appId/inBrowserElement parameters.
+     * NOTE: this could be added to a public interface if we happen to need it.
+     */
+    nsresult Remove(const nsACString& aHost, uint32_t aAppId,
+                    bool aInBrowserElement, const nsACString& aName,
+                    const nsACString& aPath, bool aBlocked);
+
   protected:
     // cached members.
     nsCOMPtr<nsIObserverService>     mObserverService;
     nsCOMPtr<nsICookiePermission>    mPermissionService;
     nsCOMPtr<mozIThirdPartyUtil>     mThirdPartyUtil;
     nsCOMPtr<nsIEffectiveTLDService> mTLDService;
     nsCOMPtr<nsIIDNService>          mIDNService;
     nsCOMPtr<mozIStorageService>     mStorageService;
--- a/netwerk/cookie/nsICookieManager2.idl
+++ b/netwerk/cookie/nsICookieManager2.idl
@@ -7,17 +7,17 @@
 
 interface nsICookie2;
 interface nsIFile;
 
 /** 
  * Additions to the frozen nsICookieManager
  */
 
-[scriptable, uuid(94628d1d-8b31-4baa-b474-9c872c440f90)]
+[scriptable, uuid(daf0caa7-b431-4b4d-ba51-08c179bb9dfe)]
 interface nsICookieManager2 : nsICookieManager
 {
   /**
    * Add a cookie. nsICookieService is the normal way to do this. This
    * method is something of a backdoor.
    *
    * @param aHost
    *        the host or domain for which the cookie is set. presence of a
@@ -102,9 +102,31 @@ interface nsICookieManager2 : nsICookieM
   /**
    * Import an old-style cookie file. Imported cookies will be added to the
    * existing database. If the database contains any cookies the same as those
    * being imported (i.e. domain, name, and path match), they will be replaced.
    *
    * @param aCookieFile the file to import, usually cookies.txt
    */
   void importCookies(in nsIFile aCookieFile);
+
+  /**
+   * Returns an enumerator of all cookies that are related to a specific app.
+   *
+   * If the onlyBrowserELement parameter is set to true, only cookies part of
+   * a browser element inside the app will be returned. If set to false, all
+   * cookies will be returned, regardless of their browserElement flag.
+   *
+   * This method assumes that appId is a valid app id. It should not be a
+   * special value like UNKNOWN_APP_ID or NO_APP_ID.
+   */
+  nsISimpleEnumerator getCookiesForApp(in unsigned long appId, in boolean onlyBrowserElement);
+
+  /**
+   * Remove all the cookies associated with the app with the id aAppId.
+   *
+   * If onlyBrowserElement is set to true, the method will only remove the
+   * cookies marked as part of a browser element inside the app.
+   *
+   * Special app id values are not allowed (NO_APP_ID or UNKNOWN_APP_ID for example).
+   */
+  void removeCookiesForApp(in unsigned long appId, in boolean onlyBrowserElement);
 };
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -8,16 +8,19 @@
 #include "nsHttp.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/WyciwygChannelChild.h"
 #include "mozilla/net/FTPChannelChild.h"
 #include "mozilla/net/WebSocketChannelChild.h"
+#include "mozilla/dom/network/TCPSocketChild.h"
+
+using mozilla::dom::TCPSocketChild;
 
 namespace mozilla {
 namespace net {
 
 PNeckoChild *gNeckoChild = nullptr;
 
 // C++ file contents
 NeckoChild::NeckoChild()
@@ -144,10 +147,29 @@ NeckoChild::AllocPWebSocket(PBrowserChil
 bool
 NeckoChild::DeallocPWebSocket(PWebSocketChild* child)
 {
   WebSocketChannelChild* p = static_cast<WebSocketChannelChild*>(child);
   p->ReleaseIPDLReference();
   return true;
 }
 
+PTCPSocketChild*
+NeckoChild::AllocPTCPSocket(const nsString& aHost,
+                            const uint16_t& aPort,
+                            const bool& useSSL,
+                            const nsString& aBinaryType,
+                            PBrowserChild* aBrowser)
+{
+  NS_NOTREACHED("AllocPTCPSocket should not be called");
+  return nullptr;
+}
+
+bool
+NeckoChild::DeallocPTCPSocket(PTCPSocketChild* child)
+{
+  TCPSocketChild* p = static_cast<TCPSocketChild*>(child);
+  p->ReleaseIPDLReference();
+  return true;
+}
+
 }} // mozilla::net
 
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -31,16 +31,22 @@ protected:
   virtual PCookieServiceChild* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceChild*);
   virtual PWyciwygChannelChild* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelChild*);
   virtual PFTPChannelChild* AllocPFTPChannel();
   virtual bool DeallocPFTPChannel(PFTPChannelChild*);
   virtual PWebSocketChild* AllocPWebSocket(PBrowserChild*);
   virtual bool DeallocPWebSocket(PWebSocketChild*);
+  virtual PTCPSocketChild* AllocPTCPSocket(const nsString& aHost,
+                                           const uint16_t& aPort,
+                                           const bool& useSSL,
+                                           const nsString& aBinaryType,
+                                           PBrowserChild* aBrowser);
+  virtual bool DeallocPTCPSocket(PTCPSocketChild*);
 };
 
 /**
  * Reference to the PNecko Child protocol.
  * Null if this is not a content process.
  */
 extern PNeckoChild *gNeckoChild;
 
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -8,20 +8,23 @@
 #include "nsHttp.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
 #include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/network/TCPSocketParent.h"
 
 #include "nsHTMLDNSPrefetch.h"
 
 using mozilla::dom::TabParent;
+using mozilla::net::PTCPSocketParent;
+using mozilla::dom::TCPSocketParent;
 
 namespace mozilla {
 namespace net {
 
 // C++ file contents
 NeckoParent::NeckoParent()
 {
 }
@@ -103,16 +106,48 @@ NeckoParent::AllocPWebSocket(PBrowserPar
 bool
 NeckoParent::DeallocPWebSocket(PWebSocketParent* actor)
 {
   WebSocketChannelParent* p = static_cast<WebSocketChannelParent*>(actor);
   p->Release();
   return true;
 }
 
+PTCPSocketParent*
+NeckoParent::AllocPTCPSocket(const nsString& aHost,
+                             const uint16_t& aPort,
+                             const bool& useSSL,
+                             const nsString& aBinaryType,
+                             PBrowserParent* aBrowser)
+{
+  TCPSocketParent* p = new TCPSocketParent();
+  p->AddRef();
+  return p;
+}
+
+bool
+NeckoParent::RecvPTCPSocketConstructor(PTCPSocketParent* aActor,
+                                       const nsString& aHost,
+                                       const uint16_t& aPort,
+                                       const bool& useSSL,
+                                       const nsString& aBinaryType,
+                                       PBrowserParent* aBrowser)
+{
+  return static_cast<TCPSocketParent*>(aActor)->
+      Init(aHost, aPort, useSSL, aBinaryType, aBrowser);
+}
+
+bool
+NeckoParent::DeallocPTCPSocket(PTCPSocketParent* actor)
+{
+  TCPSocketParent* p = static_cast<TCPSocketParent*>(actor);
+  p->Release();
+  return true;
+}
+
 bool
 NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname,
                                  const uint16_t& flags)
 {
   nsHTMLDNSPrefetch::Prefetch(hostname, flags);
   return true;
 }
 
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -28,16 +28,28 @@ protected:
   virtual PCookieServiceParent* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceParent*);
   virtual PWyciwygChannelParent* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelParent*);
   virtual PFTPChannelParent* AllocPFTPChannel();
   virtual bool DeallocPFTPChannel(PFTPChannelParent*);
   virtual PWebSocketParent* AllocPWebSocket(PBrowserParent* browser);
   virtual bool DeallocPWebSocket(PWebSocketParent*);
+  virtual PTCPSocketParent* AllocPTCPSocket(const nsString& aHost,
+                                            const uint16_t& aPort,
+                                            const bool& useSSL,
+                                            const nsString& aBinaryType,
+                                            PBrowserParent* aBrowser);
+  virtual bool RecvPTCPSocketConstructor(PTCPSocketParent*,
+                                         const nsString& aHost,
+                                         const uint16_t& aPort,
+                                         const bool& useSSL,
+                                         const nsString& aBinaryType,
+                                         PBrowserParent* aBrowser);
+  virtual bool DeallocPTCPSocket(PTCPSocketParent*);
   virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
                                    const uint16_t& flags);
   virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
                                          const uint16_t& flags,
                                          const nsresult& reason);
 
 };
 
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -7,38 +7,42 @@
 
 include protocol PContent;
 include protocol PHttpChannel;
 include protocol PCookieService;
 include protocol PBrowser;
 include protocol PWyciwygChannel;
 include protocol PFTPChannel;
 include protocol PWebSocket;
+include protocol PTCPSocket;
 
 namespace mozilla {
 namespace net {
 
 
 //-------------------------------------------------------------------
 sync protocol PNecko
 {
   manager PContent;
   manages PHttpChannel;
   manages PCookieService;
   manages PWyciwygChannel;
   manages PFTPChannel;
   manages PWebSocket;
+  manages PTCPSocket;
 
 parent:
   __delete__();
 
   PCookieService();
   PWyciwygChannel();
   PFTPChannel();
   PWebSocket(PBrowser browser);
+  PTCPSocket(nsString host, uint16_t port, bool useSSL, nsString binaryType,
+             nullable PBrowser browser);
 
   HTMLDNSPrefetch(nsString hostname, uint16_t flags);
   CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
 
 both:
   PHttpChannel(nullable PBrowser browser);
 };
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -1591,74 +1591,78 @@ nsHttpChannel::AsyncRedirectChannelToHtt
     }
 
     return rv;
 }
 
 nsresult
 nsHttpChannel::ContinueAsyncRedirectChannelToHttps(nsresult rv)
 {
-    AutoRedirectVetoNotifier notifier(this);
+    if (NS_SUCCEEDED(rv))
+        rv = OpenRedirectChannel(rv);
 
     if (NS_FAILED(rv)) {
         // Fill the failure status here, the update to https had been vetoed
         // but from the security reasons we have to discard the whole channel
         // load.
         mStatus = rv;
     }
 
     if (mLoadGroup)
         mLoadGroup->RemoveRequest(this, nullptr, mStatus);
 
     if (NS_FAILED(rv)) {
         // We have to manually notify the listener because there is not any pump
         // that would call our OnStart/StopRequest after resume from waiting for
         // the redirect callback.
         DoNotifyListener();
-        return rv;
-    }
+    }
+
+    return rv;
+}
+
+nsresult
+nsHttpChannel::OpenRedirectChannel(nsresult rv)
+{
+    AutoRedirectVetoNotifier notifier(this);
 
     // Make sure to do this _after_ calling OnChannelRedirect
     mRedirectChannel->SetOriginalURI(mOriginalURI);
 
     // And now, notify observers the deprecated way
     nsCOMPtr<nsIHttpEventSink> httpEventSink;
     GetCallback(httpEventSink);
     if (httpEventSink) {
         // NOTE: nsIHttpEventSink is only used for compatibility with pre-1.8
         // versions.
         rv = httpEventSink->OnRedirect(this, mRedirectChannel);
         if (NS_FAILED(rv)) {
-            mStatus = rv;
-            DoNotifyListener();
             return rv;
         }
     }
 
     // open new channel
     rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext);
     if (NS_FAILED(rv)) {
-        mStatus = rv;
-        DoNotifyListener();
         return rv;
     }
 
     mStatus = NS_BINDING_REDIRECTED;
 
     notifier.RedirectSucceeded();
 
     // disconnect from the old listeners...
     mListener = nullptr;
     mListenerContext = nullptr;
 
     // ...and the old callbacks
     mCallbacks = nullptr;
     mProgressSink = nullptr;
 
-    return rv;
+    return NS_OK;
 }
 
 nsresult
 nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi)
 {
     LOG(("nsHttpChannel::AsyncDoReplaceWithProxy [this=%p pi=%p]", this, pi));
     nsresult rv;
 
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -235,16 +235,17 @@ private:
     nsresult ProcessPartialContent();
     nsresult OnDoneReadingPartialCacheEntry(bool *streamDone);
 
     nsresult DoAuthRetry(nsAHttpConnection *);
 
     void     HandleAsyncRedirectChannelToHttps();
     nsresult AsyncRedirectChannelToHttps();
     nsresult ContinueAsyncRedirectChannelToHttps(nsresult rv);
+    nsresult OpenRedirectChannel(nsresult rv);
 
     /**
      * A function that takes care of reading STS headers and enforcing STS 
      * load rules.  After a secure channel is erected, STS requires the channel
      * to be trusted or any STS header data on the channel is ignored.
      * This is called from ProcessResponse.
      */
     nsresult ProcessSTSHeader();
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -8,16 +8,17 @@
 [include:modules/libjar/zipwriter/test/unit/xpcshell.ini]
 [include:uriloader/exthandler/tests/unit/xpcshell.ini]
 [include:parser/xml/test/unit/xpcshell.ini]
 [include:image/test/unit/xpcshell.ini]
 [include:dom/plugins/test/unit/xpcshell.ini]
 [include:dom/sms/tests/xpcshell.ini]
 [include:dom/mms/tests/xpcshell.ini]
 [include:dom/network/tests/unit/xpcshell.ini]
+[include:dom/network/tests/unit_ipc/xpcshell.ini]
 [include:dom/src/json/test/unit/xpcshell.ini]
 [include:dom/system/gonk/tests/xpcshell.ini]
 [include:dom/tests/unit/xpcshell.ini]
 [include:dom/indexedDB/test/unit/xpcshell.ini]
 [include:content/xtf/test/unit/xpcshell.ini]
 [include:docshell/test/unit/xpcshell.ini]
 [include:docshell/test/unit_ipc/xpcshell.ini]
 [include:embedding/tests/unit/xpcshell.ini]
--- a/toolkit/components/osfile/osfile_async_front.jsm
+++ b/toolkit/components/osfile/osfile_async_front.jsm
@@ -43,16 +43,30 @@ if (OS.Constants.Win) {
 } else {
   throw new Error("I am neither under Windows nor under a Posix system");
 }
 let Type = OS.Shared.Type;
 
 // The library of promises.
 Components.utils.import("resource://gre/modules/commonjs/promise/core.js");
 
+/**
+ * Return a shallow clone of the enumerable properties of an object
+ */
+let clone = function clone(object) {
+  let result = {};
+  for (let k in object) {
+    result[k] = object[k];
+  }
+  return result;
+};
+
+/**
+ * A shared constant used to normalize a set of options to nothing.
+ */
 const noOptions = {};
 
 /**
  * An implementation of queues (FIFO).
  *
  * The current implementation uses two arrays and runs in O(n * log(n)).
  * It is optimized for the case in which many items are enqueued sequentially.
  */
@@ -301,18 +315,18 @@ File.prototype = {
    * @resolves {number} The number of bytes effectively read.
    * @rejects {OS.File.Error}
    */
   readTo: function readTo(buffer, options) {
     // If |buffer| is an ArrayBuffer and there is no |bytes| options,
     // we need to extract the |byteLength| now, as it will be lost
     // by communication
     if ("byteLength" in buffer && (!options || !"bytes" in options)) {
-      options = Object.create(options || noOptions,
-        {bytes: {value: buffer.byteLength, enumerable: true}});
+      options = clone(options || noOptions);
+      options.bytes = buffer.byteLength;
     }
     // Note: Classic semantics for ArrayBuffer communication would imply
     // that posting the ArrayBuffer removes ownership from the sender
     // thread. Here, we use Type.voidptr_t.toMsg to ensure that these
     // semantics do not apply.
     return Scheduler.post("File_prototype_readTo",
       [this._fdmsg,
       Type.void_t.out_ptr.toMsg(buffer),
@@ -330,18 +344,18 @@ File.prototype = {
    * @resolves {number} The number of bytes effectively written.
    * @rejects {OS.File.Error}
    */
   write: function write(buffer, options) {
     // If |buffer| is an ArrayBuffer and there is no |bytes| options,
     // we need to extract the |byteLength| now, as it will be lost
     // by communication
     if ("byteLength" in buffer && (!options || !"bytes" in options)) {
-      options = Object.create(options || noOptions,
-        {bytes: {value: buffer.byteLength, enumerable: true}});
+      options = clone(options || noOptions);
+      options.bytes = buffer.byteLength;
     }
     // Note: Classic semantics for ArrayBuffer communication would imply
     // that posting the ArrayBuffer removes ownership from the sender
     // thread. Here, we use Type.voidptr_t.toMsg to ensure that these
     // semantics do not apply.
     return Scheduler.post("File_prototype_write",
       [this._fdmsg,
       Type.void_t.in_ptr.toMsg(buffer),
@@ -546,16 +560,27 @@ File.move = function move(sourcePath, de
  *   - {bool} ignoreAbsent If |true|, do not fail if the
  *     directory does not exist yet.
  */
 File.removeEmptyDir = function removeEmptyDir(path, options) {
   return Scheduler.post("removeEmptyDir",
     [Type.path.toMsg(path), options], path);
 };
 
+/**
+ * Remove an existing file.
+ *
+ * @param {string} path The name of the file.
+ */
+File.remove = function remove(path) {
+  return Scheduler.post("remove",
+    [Type.path.toMsg(path)]);
+};
+
+
 
 /**
  * Create a directory.
  *
  * @param {string} path The name of the directory.
  * @param {*=} options Additional options.
  * Implementations may interpret the following fields:
  *
@@ -565,16 +590,68 @@ File.removeEmptyDir = function removeEmp
  * parent directory.
  */
 File.makeDir = function makeDir(path, options) {
   return Scheduler.post("makeDir",
     [Type.path.toMsg(path), options], path);
 };
 
 /**
+ * Return the contents of a file
+ *
+ * @param {string} path The path to the file.
+ * @param {number=} bytes Optionally, an upper bound to the number of bytes
+ * to read.
+ *
+ * @resolves {{buffer: ArrayBuffer, bytes: number}} A buffer holding the bytes
+ * and the number of bytes read from the file.
+ */
+File.read = function read(path, bytes) {
+  return Scheduler.post("read",
+    [Type.path.toMsg(path), bytes], path);
+};
+
+/**
+ * Write a file, atomically.
+ *
+ * By opposition to a regular |write|, this operation ensures that,
+ * until the contents are fully written, the destination file is
+ * not modified.
+ *
+ * Important note: In the current implementation, option |tmpPath|
+ * is required. This requirement should disappear as part of bug 793660.
+ *
+ * @param {string} path The path of the file to modify.
+ * @param {ArrayByffer} buffer A buffer containing the bytes to write.
+ * @param {number} bytes The number of bytes to write.
+ * @param {*=} options Optionally, an object determining the behavior
+ * of this function. This object may contain the following fields:
+ * - {number} offset The offset in |buffer| at which to start extracting
+ * data
+ * - {string} tmpPath The path at which to write the temporary file.
+ *
+ * @return {number} The number of bytes actually written.
+ */
+File.writeAtomic = function writeAtomic(path, buffer, options) {
+  // Copy |options| to avoid modifying the original object
+  options = clone(options || noOptions);
+  // As options.tmpPath is a path, we need to encode it as |Type.path| message
+  if ("tmpPath" in options) {
+    options.tmpPath = Type.path.toMsg(options.tmpPath);
+  };
+  if ("byteLength" in buffer && (!("bytes" in options))) {
+    options.bytes = buffer.byteLength;
+  };
+  return Scheduler.post("writeAtomic",
+    [Type.path.toMsg(path),
+    Type.void_t.in_ptr.toMsg(buffer),
+    options], [options, buffer]);
+};
+
+/**
  * Information on a file, as returned by OS.File.stat or
  * OS.File.prototype.stat
  *
  * @constructor
  */
 File.Info = function Info(value) {
   return value;
 };
--- a/toolkit/components/osfile/osfile_async_worker.js
+++ b/toolkit/components/osfile/osfile_async_worker.js
@@ -195,20 +195,35 @@ if (this.Components) {
            Type.path.fromMsg(destPath), options);
        },
        makeDir: function makeDir(path, options) {
          return File.makeDir(Type.path.fromMsg(path), options);
        },
        removeEmptyDir: function removeEmptyDir(path, options) {
          return File.removeEmptyDir(Type.path.fromMsg(path), options);
        },
+       remove: function remove(path) {
+         return File.remove(Type.path.fromMsg(path));
+       },
        open: function open(path, mode, options) {
          let file = File.open(Type.path.fromMsg(path), mode, options);
          return OpenedFiles.add(file);
        },
+       read: function read(path, bytes) {
+         return File.read(Type.path.fromMsg(path), bytes);
+       },
+       writeAtomic: function writeAtomic(path, buffer, options) {
+         if (options.tmpPath) {
+           options.tmpPath = Type.path.fromMsg(options.tmpPath);
+         }
+         return File.writeAtomic(Type.path.fromMsg(path),
+                                 Type.voidptr_t.fromMsg(buffer),
+                                 options
+                                );
+       },
        new_DirectoryIterator: function new_DirectoryIterator(path, options) {
          let iterator = new File.DirectoryIterator(Type.path.fromMsg(path), options);
          return OpenedDirectoryIterators.add(iterator);
        },
        // Methods of OS.File
        File_prototype_close: function close(fd) {
          return withFile(fd,
            function do_close() {
--- a/toolkit/components/osfile/osfile_shared_front.jsm
+++ b/toolkit/components/osfile/osfile_shared_front.jsm
@@ -300,10 +300,76 @@ AbstractFile.normalizeOpenMode = functio
   }
   // Handle read/write
   if (!result.write) {
     result.read = true;
   }
   return result;
 };
 
+/**
+ * Return the contents of a file.
+ *
+ * @param {string} path The path to the file.
+ * @param {number=} bytes Optionally, an upper bound to the number of bytes
+ * to read.
+ *
+ * @return {{buffer: ArrayBuffer, bytes: number}} A buffer holding the bytes
+ * and the number of bytes read from the file.
+ */
+AbstractFile.read = function read(path, bytes) {
+  let file = exports.OS.File.open(path);
+  try {
+    return file.read(bytes);
+  } finally {
+    file.close();
+  }
+};
+
+/**
+ * Write a file, atomically.
+ *
+ * By opposition to a regular |write|, this operation ensures that,
+ * until the contents are fully written, the destination file is
+ * not modified.
+ *
+ * Important note: In the current implementation, option |tmpPath|
+ * is required. This requirement should disappear as part of bug 793660.
+ *
+ * @param {string} path The path of the file to modify.
+ * @param {ArrayByffer} buffer A buffer containing the bytes to write.
+ * @param {*=} options Optionally, an object determining the behavior
+ * of this function. This object may contain the following fields:
+ * - {number} offset The offset in |buffer| at which to start extracting
+ * data. If unspecified, 0.
+ * - {number} bytes The number of bytes to write. If unspecified, all
+ * the bytes in |buffer|.
+ * - {string} tmpPath The path at which to write the temporary file.
+ *
+ * @return {number} The number of bytes actually written.
+ */
+AbstractFile.writeAtomic =
+     function writeAtomic(path, buffer, options) {
+  options = options || noOptions;
+
+  let tmpPath = options.tmpPath;
+
+  if (!tmpPath) {
+    throw new TypeError("Expected option tmpPath");
+  }
+  let tmpFile = OS.File.open(tmpPath, {write: true, truncate: true});
+  let bytesWritten;
+  try {
+    bytesWritten = tmpFile.write(buffer, options);
+    tmpFile.flush();
+  } catch (x) {
+    OS.File.remove(tmpPath);
+    throw x;
+  } finally {
+    tmpFile.close();
+  }
+
+  OS.File.move(tmpPath, path, {noCopy: true});
+  return bytesWritten;
+};
+
    exports.OS.Shared.AbstractFile = AbstractFile;
-})(this);
\ No newline at end of file
+})(this);
--- a/toolkit/components/osfile/osfile_unix_front.jsm
+++ b/toolkit/components/osfile/osfile_unix_front.jsm
@@ -343,16 +343,19 @@
       * the file may currently be found.
       * @param {string} destPath The platform-specific path at which the
       * file should be moved.
       * @param {*=} options An object which may contain the following fields:
       *
       * @option {bool} noOverwrite - If set, this function will fail if
       * a file already exists at |destPath|. Otherwise, if this file exists,
       * it will be erased silently.
+      * @option {bool} noCopy - If set, this function will fail if the
+      * operation is more sophisticated than a simple renaming, i.e. if
+      * |sourcePath| and |destPath| are not situated on the same device.
       *
       * @throws {OS.File.Error} In case of any error.
       *
       * General note: The behavior of this function is defined only when
       * it is called on a single file. If it is called on a directory, the
       * behavior is undefined and may not be the same across all platforms.
       *
       * General note: The behavior of this function with respect to metadata
@@ -554,19 +557,21 @@
        }
 
        // If we can, rename the file
        let result = UnixFile.rename(sourcePath, destPath);
        if (result != -1)
          return;
 
        // If the error is not EXDEV ("not on the same device"),
-       // throw it.
-       if (ctypes.errno != Const.EXDEV) {
-         throw new File.Error();
+       // or if the error is EXDEV and we have passed an option
+       // that prevents us from crossing devices, throw the
+       // error.
+       if (ctypes.errno != Const.EXDEV || options.noCopy) {
+         throw new File.Error("move");
        }
 
        // Otherwise, copy and remove.
        File.copy(sourcePath, destPath, options);
        // FIXME: Clean-up in case of copy error?
        File.remove(sourcePath);
      };
 
@@ -817,16 +822,19 @@
        if (options.unixNoFollowingLinks) {
          throw_on_negative("stat", UnixFile.lstat(path, gStatDataPtr));
        } else {
          throw_on_negative("stat", UnixFile.stat(path, gStatDataPtr));
        }
        return new File.Info(gStatData);
      };
 
+     File.read = exports.OS.Shared.AbstractFile.read;
+     File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
+
      /**
       * Get/set the current directory.
       */
      Object.defineProperty(File, "curDir", {
          set: function(path) {
            throw_on_negative("curDir",
              UnixFile.chdir(path)
            );
--- a/toolkit/components/osfile/osfile_win_front.jsm
+++ b/toolkit/components/osfile/osfile_win_front.jsm
@@ -404,34 +404,38 @@
       * the file may currently be found.
       * @param {string} destPath The platform-specific path at which the
       * file should be moved.
       * @param {*=} options An object which may contain the following fields:
       *
       * @option {bool} noOverwrite - If set, this function will fail if
       * a file already exists at |destPath|. Otherwise, if this file exists,
       * it will be erased silently.
+      * @option {bool} noCopy - If set, this function will fail if the
+      * operation is more sophisticated than a simple renaming, i.e. if
+      * |sourcePath| and |destPath| are not situated on the same drive.
       *
       * @throws {OS.File.Error} In case of any error.
       *
       * General note: The behavior of this function is defined only when
       * it is called on a single file. If it is called on a directory, the
       * behavior is undefined and may not be the same across all platforms.
       *
       * General note: The behavior of this function with respect to metadata
       * is unspecified. Metadata may or may not be moved with the file. The
       * behavior may not be the same across all platforms.
       */
      File.move = function move(sourcePath, destPath, options) {
        options = options || noOptions;
-       let flags;
-       if (options.noOverwrite) {
+       let flags = 0;
+       if (!options.noCopy) {
          flags = Const.MOVEFILE_COPY_ALLOWED;
-       } else {
-         flags = Const.MOVEFILE_COPY_ALLOWED | Const.MOVEFILE_REPLACE_EXISTING;
+       }
+       if (!options.noOverwrite) {
+         flags = flags | Const.MOVEFILE_REPLACE_EXISTING;
        }
        throw_on_zero("move",
          WinFile.MoveFileEx(sourcePath, destPath, flags)
        );
      };
 
      /**
       * A global value used to receive data during a
@@ -812,16 +816,19 @@
      const FILE_STAT_OPTIONS = {
        // Directories can be opened neither for reading(!) nor for writing
        winAccess: 0,
        // Directories can only be opened with backup semantics(!)
        winFlags: OS.Constants.Win.FILE_FLAG_BACKUP_SEMANTICS,
        winDisposition: OS.Constants.Win.OPEN_EXISTING
      };
 
+     File.read = exports.OS.Shared.AbstractFile.read;
+     File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
+
      /**
       * Get/set the current directory.
       */
      Object.defineProperty(File, "curDir", {
          set: function(path) {
            throw_on_zero("set curDir",
              WinFile.SetCurrentDirectory(path));
          },
--- a/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
+++ b/toolkit/components/osfile/tests/mochi/main_test_osfile_async.js
@@ -154,17 +154,18 @@ let reference_compare_files = function r
   });
   return promise;
 };
 
 let test = maketest("Main",
   function main(test) {
     SimpleTest.waitForExplicitFinish();
     let tests = [test_constants, test_path, test_open, test_stat,
-                 test_read_write, test_position, test_copy,
+                 test_read_write, test_read_write_all,
+                 test_position, test_copy,
                  test_iter];
     let current = 0;
     let aux = function aux() {
       if (current >= tests.length) {
         info("Test is over");
         SimpleTest.finish();
         return null;
       }
@@ -446,16 +447,96 @@ let test_read_write = maketest("read_wri
     promise = promise.then(
       function compare_contents() {
         return reference_compare_files(pathSource, pathDest, test);
       }
     );
     return promise;
 });
 
+let test_read_write_all = maketest(
+  "read_write_all",
+  function read_write_all(test) {
+    let pathSource;
+    let pathDest = OS.Path.join(OS.Constants.Path.tmpDir,
+       "osfile async test read writeAtomic.tmp");
+    let tmpPath = pathDest + ".tmp";
+
+    let options, optionsBackup;
+
+// Check that read + writeAtomic performs a correct copy
+
+    let promise = OS.File.getCurrentDirectory();
+    promise = promise.then(
+      function obtained_current_directory(path) {
+        test.ok(path, "Obtained current directory");
+        pathSource = OS.Path.join(path, EXISTING_FILE);
+        return OS.File.read(pathSource);
+      }
+    );
+
+    let buffer;
+    let bytes;
+    promise = promise.then(
+      function read_complete(contents) {
+        test.ok(contents, "Obtained contents");
+        buffer = contents.buffer;
+        bytes = contents.bytes;
+        options = {tmpPath: tmpPath};
+        optionsBackup = {tmpPath: tmpPath};
+        return OS.File.writeAtomic(pathDest, buffer, options);
+      }
+    );
+
+// Check that options are not altered
+
+    promise = promise.then(
+      function atomicWrite_complete(bytesWritten) {
+        test.is(bytes, bytesWritten, "Wrote the correct number of bytes");
+        test.is(Object.keys(options).length, Object.keys(optionsBackup).length,
+                "The number of options was not changed");
+        for (let k in options) {
+          test.is(options[k], optionsBackup[k], "Option was not changed");
+        }
+        return reference_compare_files(pathSource, pathDest, test);
+      }
+    );
+
+// Check that temporary file was removed
+
+    promise = promise.then(
+      function compare_complete() {
+        test.info("Compare complete");
+        test.ok(!(new FileUtils.File(tmpPath).exists()), "Temporary file was removed");
+      }
+    );
+
+// Check that writeAtomic fails if there is no tmpPath
+// FIXME: Remove this as part of bug 793660
+
+    promise = promise.then(
+      function check_without_tmpPath() {
+        return OS.File.writeAtomic(pathDest, buffer, {});
+      },
+      function onFailure() {
+        test.info("Resetting failure");
+      }
+    );
+
+    promise = promise.then(
+      function onSuccess() {
+        test.fail("Without a tmpPath, writeAtomic should have failed");
+      },
+      function onFailure() {
+        test.ok("Without a tmpPath, writeAtomic has failed as expected");
+      });
+    return promise;
+  }
+);
+
 let test_position = maketest(
   "position",
   function position(test){
 
     let promise = OS.File.open(EXISTING_FILE);
 
     let file;
 
--- a/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
+++ b/toolkit/components/osfile/tests/mochi/worker_test_osfile_front.js
@@ -331,16 +331,69 @@ function test_readall_writeall_file()
   dest.write(readResult.buffer, {bytes: readResult.bytes});
 
   ok(true, "test_readall_writeall_file: copy complete (auto allocation)");
   source.close();
   dest.close();
 
   compare_files("test_readall_writeall_file (auto allocation)", src_file_name, tmp_file_name);
   OS.File.remove(tmp_file_name);
+
+  // File.readAll
+  readResult = OS.File.read(src_file_name);
+  is(readResult.bytes, size, "test_readall_writeall_file: read the right number of bytes (OS.File.readAll)");
+ 
+  // File.writeAtomic on top of nothing
+  OS.File.writeAtomic(tmp_file_name, readResult.buffer,
+    {bytes: readResult.bytes,
+     tmpPath: tmp_file_name + ".tmp"});
+  try {
+    let stat = OS.File.stat(tmp_file_name);
+    ok(true, "readAll + writeAtomic created a file");
+    is(stat.size, size, "readAll + writeAtomic created a file of the right size");
+  } catch (x) {
+    ok(false, "readAll + writeAtomic somehow failed");
+    if(x.becauseNoSuchFile) {
+      ok(false, "readAll + writeAtomic did not create file");
+    }
+  }
+  compare_files("test_readall_writeall_file (OS.File.readAll + writeAtomic)",
+                src_file_name, tmp_file_name);
+  exn = null;
+  try {
+    let stat = OS.File.stat(tmp_file_name + ".tmp");
+  } catch (x) {
+    exn = x;
+  }
+  ok(!!exn, "readAll + writeAtomic cleaned up after itself");
+
+
+  // File.writeAtomic on top of existing file
+  // Remove content and set arbitrary size, to avoid potential false negatives
+  dest = OS.File.open(tmp_file_name, {write: true, trunc:true});
+  dest.setPosition(1234);
+  dest.close();
+
+  OS.File.writeAtomic(tmp_file_name, readResult.buffer,
+    {bytes: readResult.bytes,
+     tmpPath: tmp_file_name + ".tmp"});
+  compare_files("test_readall_writeall_file (OS.File.readAll + writeAtomic 2)",
+                src_file_name, tmp_file_name);
+
+  // Ensure that File.writeAtomic fails if no temporary file name is provided
+  // (FIXME: Remove this test as part of bug 793660)
+
+  exn = null;
+  try {
+    OS.File.writeAtomic(tmp_file_name, readResult.buffer,
+      {bytes: readResult.bytes});
+  } catch (x) {
+    exn = x;
+  }
+  ok(!!exn && exn instanceof TypeError, "wrietAtomic fails if tmpPath is not provided");
 }
 
 /**
  * Test that copying a file using |copy| works.
  */
 function test_copy_existing_file()
 {
   let src_file_name = "chrome/toolkit/components/osfile/tests/mochi/worker_test_osfile_unix.js";
--- a/toolkit/components/social/test/browser/browser_frameworker.js
+++ b/toolkit/components/social/test/browser/browser_frameworker.js
@@ -1,14 +1,14 @@
 /* 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/. */
 
 function makeWorkerUrl(runner) {
-  return "data:application/javascript," + encodeURI("let run=" + runner.toSource()) + ";run();"
+  return "data:application/javascript;charset=utf-8," + encodeURI("let run=" + runner.toSource()) + ";run();"
 }
 
 var getFrameWorkerHandle;
 function test() {
   waitForExplicitFinish();
 
   let scope = {};
   Cu.import("resource://gre/modules/FrameWorker.jsm", scope);
--- a/toolkit/components/social/test/browser/browser_notifications.js
+++ b/toolkit/components/social/test/browser/browser_notifications.js
@@ -69,17 +69,17 @@ function restorePromptService() {
 }
 // end of alerts service mock.
 
 
 function ensureProvider(workerFunction, cb) {
   let manifest = {
     origin: TEST_PROVIDER_ORIGIN,
     name: "Example Provider",
-    workerURL: "data:application/javascript," + encodeURI("let run=" + workerFunction.toSource()) + ";run();"
+    workerURL: "data:application/javascript;charset=utf-8," + encodeURI("let run=" + workerFunction.toSource()) + ";run();"
   };
 
   ensureSocialEnabled();
   SocialService.addProvider(manifest, function (p) {
     cb(p);
   });
 }
 
--- a/toolkit/components/telemetry/TelemetryStopwatch.jsm
+++ b/toolkit/components/telemetry/TelemetryStopwatch.jsm
@@ -54,16 +54,48 @@ let TelemetryStopwatch = {
       return false;
     }
 
     timers[aHistogram] = Date.now();
     return true;
   },
 
   /**
+   * Deletes the timer associated with a telemetry histogram. The timer can be
+   * directly associated with a histogram, or with a pair of a histogram and
+   * an object. Important: Only use this method when a legitimate cancellation
+   * should be done.
+   *
+   * @param aHistogram a string which must be a valid histogram name
+   *                   from TelemetryHistograms.json
+   *
+   * @param aObj Optional parameter. If specified, the timer is associated
+   *             with this object, meaning that multiple timers for a same
+   *             histogram may be run concurrently, as long as they are
+   *             associated with different objects.
+   *
+   * @return true if the timer exist and it was cleared, false otherwise.
+   */
+  cancel: function ts_cancel(aHistogram, aObj) {
+    if (!validTypes(aHistogram, aObj))
+      return false;
+
+    let timers = aObj
+                 ? objectTimers.get(aObj, {})
+                 : simpleTimers;
+
+    if (timers.hasOwnProperty(aHistogram)) {
+      delete timers[aHistogram];
+      return true;
+    }
+
+    return false;
+  },
+
+  /**
    * Stops the timer associated with the given histogram (and object),
    * calculates the time delta between start and finish, and adds the value
    * to the histogram.
    *
    * @param aHistogram a string which must be a valid histogram name
    *                   from TelemetryHistograms.h. If an invalid name
    *                   is given, the function will throw.
    *
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryStopwatch.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryStopwatch.js
@@ -81,21 +81,34 @@ function run_test() {
   do_check_false(TelemetryStopwatch.finish(HIST_NAME, refObj));
 
   // Verify that they can be used again
   do_check_true(TelemetryStopwatch.start(HIST_NAME));
   do_check_true(TelemetryStopwatch.start(HIST_NAME, refObj));
   do_check_true(TelemetryStopwatch.finish(HIST_NAME));
   do_check_true(TelemetryStopwatch.finish(HIST_NAME, refObj));
 
-
   do_check_false(TelemetryStopwatch.finish("unknown-mark")); // Unknown marker
   do_check_false(TelemetryStopwatch.finish("unknown-mark", {})); // Unknown object
   do_check_false(TelemetryStopwatch.finish(HIST_NAME, {})); // Known mark on unknown object
 
+  // Test cancel
+  do_check_true(TelemetryStopwatch.start(HIST_NAME));
+  do_check_true(TelemetryStopwatch.start(HIST_NAME, refObj));
+  do_check_true(TelemetryStopwatch.cancel(HIST_NAME));
+  do_check_true(TelemetryStopwatch.cancel(HIST_NAME, refObj));
+
+  // Verify that can not cancel twice
+  do_check_false(TelemetryStopwatch.cancel(HIST_NAME));
+  do_check_false(TelemetryStopwatch.cancel(HIST_NAME, refObj));
+
+  // Verify that cancel removes the timers
+  do_check_false(TelemetryStopwatch.finish(HIST_NAME));
+  do_check_false(TelemetryStopwatch.finish(HIST_NAME, refObj));
+
   finishTest();
 }
 
 function finishTest() {
   let histogram = Telemetry.getHistogramById(HIST_NAME);
   let snapshot = histogram.snapshot();
   let newCount = snapshot.counts.reduce(function (a,b) a += b);
 
--- a/toolkit/content/tests/chrome/Makefile.in
+++ b/toolkit/content/tests/chrome/Makefile.in
@@ -84,16 +84,17 @@ MOCHITEST_CHROME_FILES = 	findbar_window
 		test_bug382990.xul \
 		test_bug457632.xul \
 		test_bug460942.xul \
 		test_bug509732.xul \
 		test_bug554279.xul \
 		test_bug557987.xul\
 		test_bug562554.xul \
 		test_bug585946.xul \
+		test_bug792324.xul \
 		test_button.xul \
 		test_closemenu_attribute.xul \
 		test_colorpicker_popup.xul \
 		test_menulist.xul \
 		test_menuitem_blink.xul \
 		test_menulist_keynav.xul \
 		test_popup_coords.xul \
 		test_popup_recreate.xul \
new file mode 100644
--- /dev/null
+++ b/toolkit/content/tests/chrome/test_bug792324.xul
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=792324
+-->
+<window title="Mozilla Bug 792324"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <title>Test for Bug 792324</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+<body  xmlns="http://www.w3.org/1999/xhtml">
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=792324">Mozilla Bug 792324</a>
+
+  <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+<panel id="panel-1">
+  <button label="just a normal button"/>
+  <button id="button-1"
+          accesskey="X"
+          oncommand="clicked(event)"
+          label="Button in panel 1"
+  />
+</panel>
+
+<panel id="panel-2">
+  <button label="just a normal button"/>
+  <button id="button-2"
+          accesskey="X"
+          oncommand="clicked(event)"
+          label="Button in panel 2"
+  />
+</panel>
+
+<script class="testbody" type="application/javascript;version=1.7"><![CDATA[
+
+/** Test for Bug 792324 **/
+let after_click;
+
+function clicked(event) {
+  after_click(event);
+}
+
+function checkAccessKeyOnPanel(panelid, buttonid, cb) {
+  let panel = document.getElementById(panelid);
+  panel.addEventListener("popupshown", function onpopupshown() {
+    panel.removeEventListener("popupshown", onpopupshown);
+    panel.firstChild.focus();
+    after_click = function(event) {
+      is(event.target.id, buttonid, "Accesskey was directed to the button '" + buttonid + "'");
+      panel.hidePopup();
+      cb();
+    }
+    synthesizeKey("X", {});
+  });
+ panel.openPopup(null, "", 100, 100, false, false);
+}
+
+function test() {
+  checkAccessKeyOnPanel("panel-1", "button-1", function() {
+    checkAccessKeyOnPanel("panel-2", "button-2", function() {
+      SimpleTest.finish();
+    });
+  });
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(test, window);
+
+]]></script>
+
+</window>
--- a/toolkit/content/widgets/button.xml
+++ b/toolkit/content/widgets/button.xml
@@ -84,18 +84,27 @@
       <property name="autoCheck"
                 onget="return this.getAttribute('autoCheck') == 'true';"
                 onset="this.setAttribute('autoCheck', val); return val;"/>
 
       <method name ="filterButtons">
         <parameter name="node"/>
         <body>
         <![CDATA[
-          if (node.localName == "button" && node.accessKey &&
-            !node.disabled && !node.collapsed && !node.hidden)
+          // if the node isn't visible, don't descend into it.
+          var cs = node.ownerDocument.defaultView.getComputedStyle(node, null);
+          if (cs.visibility != "visible" || cs.display == "none") {
+            return NodeFilter.FILTER_REJECT;
+          }
+          // but it may be a popup element, in which case we look at "state"...
+          if (cs.display == "-moz-popup" && node.state != "open") {
+            return NodeFilter.FILTER_REJECT;
+          }
+          // OK - the node seems visible, so it is a candidate.
+          if (node.localName == "button" && node.accessKey && !node.disabled)
             return NodeFilter.FILTER_ACCEPT;
           return NodeFilter.FILTER_SKIP;
         ]]>
         </body>
       </method>
 
       <method name="fireAccessKeyButton">
         <parameter name="aSubtree"/>
--- a/uriloader/prefetch/OfflineCacheUpdateChild.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateChild.cpp
@@ -50,16 +50,17 @@ extern PRLogModuleInfo *gOfflineCacheUpd
 namespace mozilla {
 namespace docshell {
 
 //-----------------------------------------------------------------------------
 // OfflineCacheUpdateChild::nsISupports
 //-----------------------------------------------------------------------------
 
 NS_INTERFACE_MAP_BEGIN(OfflineCacheUpdateChild)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsIOfflineCacheUpdate)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(OfflineCacheUpdateChild)
 NS_IMPL_RELEASE_WITH_DESTROY(OfflineCacheUpdateChild, RefcountHitZero())
 
 void
 OfflineCacheUpdateChild::RefcountHitZero()
--- a/uriloader/prefetch/OfflineCacheUpdateParent.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp
@@ -3,16 +3,17 @@
  * 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 "OfflineCacheUpdateParent.h"
 
 #include "mozilla/ipc/URIUtils.h"
 #include "nsOfflineCacheUpdate.h"
 #include "nsIApplicationCache.h"
+#include "nsNetUtil.h"
 
 using namespace mozilla::ipc;
 
 #if defined(PR_LOGGING)
 //
 // To enable logging (see prlog.h for full details):
 //
 //    set NSPR_LOG_MODULES=nsOfflineCacheUpdate:5
@@ -78,30 +79,40 @@ OfflineCacheUpdateParent::Schedule(const
     mIsInBrowserElement = isInBrowserElement;
     mAppId = appId;
 
     nsRefPtr<nsOfflineCacheUpdate> update;
     nsCOMPtr<nsIURI> manifestURI = DeserializeURI(aManifestURI);
     if (!manifestURI)
         return NS_ERROR_FAILURE;
 
-    nsCOMPtr<nsIURI> documentURI = DeserializeURI(aDocumentURI);
-    if (!documentURI)
-        return NS_ERROR_FAILURE;
-
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
         return NS_ERROR_FAILURE;
 
+    bool offlinePermissionAllowed = false;
+    nsresult rv = service->OfflineAppAllowedForURI(
+        manifestURI, nullptr, &offlinePermissionAllowed);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (!offlinePermissionAllowed)
+        return NS_ERROR_DOM_SECURITY_ERR;
+
+    nsCOMPtr<nsIURI> documentURI = DeserializeURI(aDocumentURI);
+    if (!documentURI)
+        return NS_ERROR_FAILURE;
+
+    if (!NS_SecurityCompareURIs(manifestURI, documentURI, false))
+        return NS_ERROR_DOM_SECURITY_ERR;
+
     service->FindUpdate(manifestURI, this, getter_AddRefs(update));
     if (!update) {
         update = new nsOfflineCacheUpdate();
 
-        nsresult rv;
         // Leave aDocument argument null. Only glues and children keep 
         // document instances.
         rv = update->Init(manifestURI, documentURI, nullptr, nullptr, this);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = update->Schedule();
         NS_ENSURE_SUCCESS(rv, rv);
     }
--- a/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
+++ b/uriloader/prefetch/nsOfflineCacheUpdateService.cpp
@@ -571,17 +571,18 @@ OfflineAppPermForURI(nsIURI *aURI,
             aPrefBranch->GetBoolPref(kPrefName, aAllowed);
         } else {
             *aAllowed = Preferences::GetBool(kPrefName, false);
         }
 
         return NS_OK;
     }
 
-    if (perm == nsIPermissionManager::ALLOW_ACTION) {
+    if (perm == nsIPermissionManager::ALLOW_ACTION ||
+        perm == nsIOfflineCacheUpdateService::ALLOW_NO_WARN) {
         *aAllowed = true;
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsOfflineCacheUpdateService::OfflineAppAllowedForURI(nsIURI *aURI,