Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 18 Jul 2014 17:51:39 -0700
changeset 216895 9fdb8047f07d2d13cc0325b9ee43e999e465b8d0
parent 216855 50a8424cf1b8a579ba486eecc109459377a59ab3 (current diff)
parent 216894 69615044a960e09d24d46aa113b0410651b63b20 (diff)
child 216917 961c13085948f6baf16fd56749e199d8c3a931fd
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
js/src/jit-test/tests/basic/bug923390.js
modules/libpref/src/init/all.js
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -27,21 +27,28 @@ const gXPInstallObserver = {
     }
     return null;
   },
 
   observe: function (aSubject, aTopic, aData)
   {
     var brandBundle = document.getElementById("bundle_brand");
     var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
-    var win = installInfo.originatingWindow;
-    var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-                   .getInterface(Components.interfaces.nsIWebNavigation)
-                   .QueryInterface(Components.interfaces.nsIDocShell);
-    var browser = this._getBrowser(shell);
+    var winOrBrowser = installInfo.originator;
+
+    var browser;
+    try {
+      var shell = winOrBrowser.QueryInterface(Components.interfaces.nsIDOMWindow)
+                              .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                              .getInterface(Components.interfaces.nsIWebNavigation)
+                              .QueryInterface(Components.interfaces.nsIDocShell);
+      browser = this._getBrowser(shell);
+    } catch (e) {
+      browser = winOrBrowser;
+    }
     if (!browser)
       return;
     const anchorID = "addons-notification-icon";
     var messageString, action;
     var brandShortName = brandBundle.getString("brandShortName");
 
     var notificationID = aTopic;
     // Make notifications persist a minimum of 30 seconds
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -1,16 +1,20 @@
 /* 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 { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
+// Invalid auth token as per
+// https://github.com/mozilla-services/loop-server/blob/45787d34108e2f0d87d74d4ddf4ff0dbab23501c/loop/errno.json#L6
+const INVALID_AUTH_TOKEN = 110;
+
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 Cu.import("resource://gre/modules/osfile.jsm", this);
 
 this.EXPORTED_SYMBOLS = ["MozLoopService"];
 
 XPCOMUtils.defineLazyModuleGetter(this, "console",
@@ -240,17 +244,19 @@ let MozLoopServiceInternal = {
         if (!this.storeSessionToken(response.headers))
           return;
 
         this.registeredLoopServer = true;
         this._registeredDeferred.resolve();
         // No need to clear the promise here, everything was good, so we don't need
         // to re-register.
       }, (error) => {
-        if (error.errno == 401) {
+        // There's other errors than invalid auth token, but we should only do the reset
+        // as a last resort.
+        if (error.code === 401 && error.errno === INVALID_AUTH_TOKEN) {
           if (this.urlExpiryTimeIsInFuture()) {
             // XXX Should this be reported to the user is a visible manner?
             Cu.reportError("Loop session token is invalid, all previously "
                            + "generated urls will no longer work.");
           }
 
           // Authorization failed, invalid token, we need to try again with a new token.
           Services.prefs.clearUserPref("loop.hawk-session-token");
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/xpcshell/test_loopservice_token_invalid.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const LOOP_HAWK_PREF = "loop.hawk-session-token";
+const fakeSessionToken1 = "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1751";
+const fakeSessionToken2 = "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1750";
+
+add_test(function test_registration_invalid_token() {
+  Services.prefs.setCharPref(LOOP_HAWK_PREF, fakeSessionToken1);
+  var authorizationAttempts = 0;
+
+  loopServer.registerPathHandler("/registration", (request, response) => {
+    // Due to the way the time stamp checking code works in hawkclient, we expect a couple
+    // of authorization requests before we reset the token.
+    if (request.hasHeader("Authorization")) {
+      authorizationAttempts++;
+      response.setStatusLine(null, 401, "Unauthorized");
+      response.write(JSON.stringify({
+        code: 401,
+        errno: 110,
+        error: {
+          error: "Unauthorized",
+          message: "Unknown credentials",
+          statusCode: 401
+        }
+      }));
+    } else {
+      // We didn't have an authorization header, so check the pref has been cleared.
+      Assert.equal(Services.prefs.prefHasUserValue(LOOP_HAWK_PREF), false);
+      response.setStatusLine(null, 200, "OK");
+      response.setHeader("Hawk-Session-Token", fakeSessionToken2, false);
+    }
+    response.processAsync();
+    response.finish();
+  });
+
+  MozLoopService.register(mockPushHandler).then(() => {
+    // Due to the way the time stamp checking code works in hawkclient, we expect a couple
+    // of authorization requests before we reset the token.
+    Assert.equal(authorizationAttempts, 2);
+    Assert.equal(Services.prefs.getCharPref(LOOP_HAWK_PREF), fakeSessionToken2);
+    run_next_test();
+  }, err => {
+    do_throw("shouldn't be a failure result: " + err);
+  });
+});
+
+
+function run_test()
+{
+  setupFakeLoopServer();
+
+  do_register_cleanup(function() {
+    Services.prefs.clearUserPref("loop.hawk-session-token");
+  });
+
+  run_next_test();
+}
--- a/browser/components/loop/test/xpcshell/xpcshell.ini
+++ b/browser/components/loop/test/xpcshell/xpcshell.ini
@@ -6,11 +6,12 @@ firefox-appdir = browser
 [test_looppush_initialize.js]
 [test_loopservice_dnd.js]
 [test_loopservice_expiry.js]
 [test_loopservice_get_loop_char_pref.js]
 [test_loopservice_set_loop_char_pref.js]
 [test_loopservice_initialize.js]
 [test_loopservice_locales.js]
 [test_loopservice_registration.js]
+[test_loopservice_token_invalid.js]
 [test_loopservice_token_save.js]
 [test_loopservice_token_send.js]
 [test_loopservice_token_validation.js]
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -867,18 +867,16 @@ nsScriptSecurityManager::CheckLoadURIStr
     return rv;
 }
 
 bool
 nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
 {
     MOZ_ASSERT(aGlobal);
     MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
-    AutoJSContext cx;
-    JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
 
     // Check the bits on the compartment private.
     return xpc::Scriptability::Get(aGlobal).Allowed();
 }
 
 ///////////////// Principals ///////////////////////
 
 NS_IMETHODIMP
--- a/content/base/public/nsDeprecatedOperationList.h
+++ b/content/base/public/nsDeprecatedOperationList.h
@@ -60,8 +60,9 @@ DEPRECATED_OPERATION(KeyNameRed)
 DEPRECATED_OPERATION(KeyNameGreen)
 DEPRECATED_OPERATION(KeyNameYellow)
 DEPRECATED_OPERATION(KeyNameBlue)
 DEPRECATED_OPERATION(KeyNameLive)
 DEPRECATED_OPERATION(KeyNameApps)
 DEPRECATED_OPERATION(KeyNameFastFwd)
 DEPRECATED_OPERATION(KeyNameZoom)
 DEPRECATED_OPERATION(KeyNameDeadKeys)
+DEPRECATED_OPERATION(ImportXULIntoContent)
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -1988,16 +1988,17 @@ public:
   void FlushPendingLinkUpdates();
 
 #define DEPRECATED_OPERATION(_op) e##_op,
   enum DeprecatedOperations {
 #include "nsDeprecatedOperationList.h"
     eDeprecatedOperationCount
   };
 #undef DEPRECATED_OPERATION
+  bool HasWarnedAbout(DeprecatedOperations aOperation);
   void WarnOnceAbout(DeprecatedOperations aOperation, bool asError = false);
 
   virtual void PostVisibilityUpdateEvent() = 0;
   
   bool IsSyntheticDocument() const { return mIsSyntheticDocument; }
 
   void SetNeedLayoutFlush() {
     mNeedLayoutFlush = true;
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -9816,23 +9816,29 @@ nsDocument::FindImageMap(const nsAString
 
 #define DEPRECATED_OPERATION(_op) #_op "Warning",
 static const char* kWarnings[] = {
 #include "nsDeprecatedOperationList.h"
   nullptr
 };
 #undef DEPRECATED_OPERATION
 
+bool
+nsIDocument::HasWarnedAbout(DeprecatedOperations aOperation)
+{
+  static_assert(eDeprecatedOperationCount <= 64,
+                "Too many deprecated operations");
+  return mWarnedAbout & (1ull << aOperation);
+}
+
 void
 nsIDocument::WarnOnceAbout(DeprecatedOperations aOperation,
                            bool asError /* = false */)
 {
-  static_assert(eDeprecatedOperationCount <= 64,
-                "Too many deprecated operations");
-  if (mWarnedAbout & (1ull << aOperation)) {
+  if (HasWarnedAbout(aOperation)) {
     return;
   }
   mWarnedAbout |= (1ull << aOperation);
   uint32_t flags = asError ? nsIScriptError::errorFlag
                            : nsIScriptError::warningFlag;
   nsContentUtils::ReportToConsole(flags,
                                   NS_LITERAL_CSTRING("DOM Core"), this,
                                   nsContentUtils::eDOM_PROPERTIES,
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -84,17 +84,26 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGenericDOMDataNode)
   return Element::CanSkipInCC(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsGenericDOMDataNode)
   return Element::CanSkipThis(tmp);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGenericDOMDataNode)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGenericDOMDataNode)
+  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
+    char name[40];
+    PR_snprintf(name, sizeof(name), "nsGenericDOMDataNode (len=%d)",
+                tmp->mText.GetLength());
+    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
+  } else {
+    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGenericDOMDataNode, tmp->mRefCnt.get())
+  }
+
   // Always need to traverse script objects, so do that before we check
   // if we're uncollectable.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 
   if (!nsINode::Traverse(tmp, cb)) {
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
 
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -1474,18 +1474,20 @@ AdoptNodeIntoOwnerDoc(nsINode *aParent, 
 
   return NS_OK;
 }
 
 static nsresult
 CheckForOutdatedParent(nsINode* aParent, nsINode* aNode)
 {
   if (JSObject* existingObjUnrooted = aNode->GetWrapper()) {
+    JSRuntime* runtime = JS_GetObjectRuntime(existingObjUnrooted);
+    JS::Rooted<JSObject*> existingObj(runtime, existingObjUnrooted);
+
     AutoJSContext cx;
-    JS::Rooted<JSObject*> existingObj(cx, existingObjUnrooted);
     nsIGlobalObject* global = aParent->OwnerDoc()->GetScopeObject();
     MOZ_ASSERT(global);
 
     if (js::GetGlobalForObjectCrossCompartment(existingObj) !=
         global->GetGlobalJSObject()) {
       JSAutoCompartment ac(cx, existingObj);
       nsresult rv = ReparentWrapper(cx, existingObj);
       NS_ENSURE_SUCCESS(rv, rv);
--- a/content/canvas/compiledtest/TestWebGLElementArrayCache.cpp
+++ b/content/canvas/compiledtest/TestWebGLElementArrayCache.cpp
@@ -54,79 +54,96 @@ GLenum GLType()
     case 2:  return LOCAL_GL_UNSIGNED_SHORT;
     case 1:  return LOCAL_GL_UNSIGNED_BYTE;
     default:
       VERIFY(false);
       return 0;
   }
 }
 
+void CheckValidate(bool expectSuccess,
+                   WebGLElementArrayCache& c,
+                   GLenum type,
+                   uint32_t maxAllowed,
+                   size_t first,
+                   size_t count)
+{
+  uint32_t out_upperBound = 0;
+  const bool success = c.Validate(type, maxAllowed, first, count, &out_upperBound);
+  VERIFY(success == expectSuccess);
+  if (success) {
+    VERIFY(out_upperBound <= maxAllowed);
+  } else {
+    VERIFY(out_upperBound > maxAllowed);
+  }
+}
+
 template<typename T>
-void CheckValidateOneType(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
+void CheckValidateOneTypeVariousBounds(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
 {
   size_t first = firstByte / sizeof(T);
   size_t count = countBytes / sizeof(T);
 
   GLenum type = GLType<T>();
 
   T max = 0;
   for (size_t i = 0; i < count; i++)
     if (c.Element<T>(first + i) > max)
       max = c.Element<T>(first + i);
 
-  VERIFY(c.Validate(type, max, first, count));
-  VERIFY(c.Validate(type, T(-1), first, count));
-  if (T(max + 1)) VERIFY(c.Validate(type, T(max + 1), first, count));
+  CheckValidate(true, c, type, max, first, count);
+  CheckValidate(true, c, type, T(-1), first, count);
+  if (T(max + 1)) CheckValidate(true, c, type, T(max + 1), first, count);
   if (max > 0) {
-    VERIFY(!c.Validate(type, max - 1, first, count));
-    VERIFY(!c.Validate(type, 0, first, count));
+    CheckValidate(false, c, type, max - 1, first, count);
+    CheckValidate(false, c, type, 0, first, count);
   }
 }
 
-void CheckValidate(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
+void CheckValidateAllTypes(WebGLElementArrayCache& c, size_t firstByte, size_t countBytes)
 {
-  CheckValidateOneType<uint8_t>(c, firstByte, countBytes);
-  CheckValidateOneType<uint16_t>(c, firstByte, countBytes);
-  CheckValidateOneType<uint32_t>(c, firstByte, countBytes);
+  CheckValidateOneTypeVariousBounds<uint8_t>(c, firstByte, countBytes);
+  CheckValidateOneTypeVariousBounds<uint16_t>(c, firstByte, countBytes);
+  CheckValidateOneTypeVariousBounds<uint32_t>(c, firstByte, countBytes);
 }
 
 template<typename T>
 void CheckSanity()
 {
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                         // ensure we exercise some nontrivial tree-walking
   T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now
   size_t numBytes = numElems * sizeof(T);
   MOZ_ASSERT(numBytes == sizeof(data));
 
   GLenum type = GLType<T>();
 
   WebGLElementArrayCache c;
   c.BufferData(data, numBytes);
-  VERIFY( c.Validate(type, 6, 0, 8));
-  VERIFY(!c.Validate(type, 5, 0, 8));
-  VERIFY( c.Validate(type, 3, 0, 3));
-  VERIFY(!c.Validate(type, 2, 0, 3));
-  VERIFY( c.Validate(type, 6, 2, 4));
-  VERIFY(!c.Validate(type, 5, 2, 4));
+  CheckValidate(true,  c, type, 6, 0, 8);
+  CheckValidate(false, c, type, 5, 0, 8);
+  CheckValidate(true,  c, type, 3, 0, 3);
+  CheckValidate(false, c, type, 2, 0, 3);
+  CheckValidate(true,  c, type, 6, 2, 4);
+  CheckValidate(false, c, type, 5, 2, 4);
 
   c.BufferSubData(5*sizeof(T), data, sizeof(T));
-  VERIFY( c.Validate(type, 5, 0, 8));
-  VERIFY(!c.Validate(type, 4, 0, 8));
+  CheckValidate(true,  c, type, 5, 0, 8);
+  CheckValidate(false, c, type, 4, 0, 8);
 
   // now test a somewhat larger size to ensure we exceed the size of a tree leaf
   for(size_t i = 0; i < numElems; i++)
     data[i] = numElems - i;
   c.BufferData(data, numBytes);
-  VERIFY( c.Validate(type, numElems,     0, numElems));
-  VERIFY(!c.Validate(type, numElems - 1, 0, numElems));
+  CheckValidate(true,  c, type, numElems,     0, numElems);
+  CheckValidate(false, c, type, numElems - 1, 0, numElems);
 
   MOZ_ASSERT(numElems > 10);
-  VERIFY( c.Validate(type, numElems - 10, 10, numElems - 10));
-  VERIFY(!c.Validate(type, numElems - 11, 10, numElems - 10));
+  CheckValidate(true,  c, type, numElems - 10, 10, numElems - 10);
+  CheckValidate(false, c, type, numElems - 11, 10, numElems - 10);
 }
 
 template<typename T>
 void CheckUintOverflow()
 {
   // This test is only for integer types smaller than uint32_t
   static_assert(sizeof(T) < sizeof(uint32_t), "This test is only for integer types \
                 smaller than uint32_t");
@@ -142,19 +159,19 @@ void CheckUintOverflow()
   WebGLElementArrayCache c;
 
   for(size_t i = 0; i < numElems; i++)
     data[i] = numElems - i;
   c.BufferData(data, numBytes);
 
   // bug 825205
   uint32_t bigValWrappingToZero = uint32_t(T(-1)) + 1;
-  VERIFY( c.Validate(type, bigValWrappingToZero,     0, numElems));
-  VERIFY( c.Validate(type, bigValWrappingToZero - 1, 0, numElems));
-  VERIFY(!c.Validate(type,                        0, 0, numElems));
+  CheckValidate(true,  c, type, bigValWrappingToZero,     0, numElems);
+  CheckValidate(true,  c, type, bigValWrappingToZero - 1, 0, numElems);
+  CheckValidate(false, c, type,                        0, 0, numElems);
 }
 
 int main(int argc, char *argv[])
 {
   srand(0); // do not want a random seed here.
 
   CheckSanity<uint8_t>();
   CheckSanity<uint16_t>();
@@ -172,17 +189,17 @@ int main(int argc, char *argv[])
     // producing different values. In that case, the minimum value by which to replace
     // this 20 to reproduce the bug on Linux, was 25. Replacing it with 64 should give
     // us some comfort margin.
     int repeat = std::min(maxBufferSize, 64);
     for (int i = 0; i < repeat; i++) {
       size_t size = RandomInteger<size_t>(0, maxBufferSize);
       MakeRandomVector(v, size);
       b.BufferData(v.Elements(), size);
-      CheckValidate(b, 0, size);
+      CheckValidateAllTypes(b, 0, size);
 
       for (int j = 0; j < 16; j++) {
         for (int bufferSubDataCalls = 1; bufferSubDataCalls <= 8; bufferSubDataCalls *= 2) {
           for (int validateCalls = 1; validateCalls <= 8; validateCalls *= 2) {
 
             size_t offset = 0, subsize = 0;
 
             for (int k = 0; k < bufferSubDataCalls; k++) {
@@ -190,17 +207,17 @@ int main(int argc, char *argv[])
               subsize = RandomInteger<size_t>(0, size - offset);
               MakeRandomVector(vsub, subsize);
               b.BufferSubData(offset, vsub.Elements(), subsize);
             }
 
             for (int k = 0; k < validateCalls; k++) {
               offset = RandomInteger<size_t>(0, size);
               subsize = RandomInteger<size_t>(0, size - offset);
-              CheckValidate(b, offset, subsize);
+              CheckValidateAllTypes(b, offset, subsize);
             }
           } // validateCalls
         } // bufferSubDataCalls
       } // j
     } // i
   } // maxBufferSize
 
   std::cerr << argv[0] << ": all " << gTestsPassed << " tests passed" << std::endl;
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -299,17 +299,17 @@ WebGLContext::DrawElements(GLenum mode, 
                            WebGLintptr byteOffset)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateDrawModeEnum(mode, "drawElements: mode"))
         return;
 
-    GLuint upperBound = UINT_MAX;
+    GLuint upperBound = 0;
     if (!DrawElements_check(count, type, byteOffset, 1, "drawElements",
                             &upperBound))
     {
         return;
     }
 
     RunContextLossTimer();
 
--- a/content/canvas/src/WebGLElementArrayCache.cpp
+++ b/content/canvas/src/WebGLElementArrayCache.cpp
@@ -12,30 +12,19 @@
 #include <cstdlib>
 #include <cstring>
 #include <limits>
 #include <algorithm>
 
 namespace mozilla {
 
 static void
-SetUpperBound(uint32_t* out_upperBound, uint32_t newBound)
-{
-  if (!out_upperBound)
-      return;
-
-  *out_upperBound = newBound;
-}
-
-static void
 UpdateUpperBound(uint32_t* out_upperBound, uint32_t newBound)
 {
-  if (!out_upperBound)
-      return;
-
+  MOZ_ASSERT(out_upperBound);
   *out_upperBound = std::max(*out_upperBound, newBound);
 }
 
 /*
  * WebGLElementArrayCacheTree contains most of the implementation of WebGLElementArrayCache,
  * which performs WebGL element array buffer validation for drawElements.
  *
  * Attention: Here lie nontrivial data structures, bug-prone algorithms, and non-canonical tweaks!
@@ -484,22 +473,22 @@ bool WebGLElementArrayCache::UpdateTrees
   return result;
 }
 
 template<typename T>
 bool
 WebGLElementArrayCache::Validate(uint32_t maxAllowed, size_t firstElement,
                                  size_t countElements, uint32_t* out_upperBound)
 {
-  SetUpperBound(out_upperBound, 0);
+  *out_upperBound = 0;
 
   // if maxAllowed is >= the max T value, then there is no way that a T index could be invalid
   uint32_t maxTSize = std::numeric_limits<T>::max();
   if (maxAllowed >= maxTSize) {
-    SetUpperBound(out_upperBound, maxTSize);
+    UpdateUpperBound(out_upperBound, maxTSize);
     return true;
   }
 
   T maxAllowedT(maxAllowed);
 
   // integer overflow must have been handled earlier, so we assert that maxAllowedT
   // is exactly the max allowed value.
   MOZ_ASSERT(uint32_t(maxAllowedT) == maxAllowed);
@@ -523,17 +512,17 @@ WebGLElementArrayCache::Validate(uint32_
 
   size_t lastElement = firstElement + countElements - 1;
 
   // fast exit path when the global maximum for the whole element array buffer
   // falls in the allowed range
   T globalMax = tree->GlobalMaximum();
   if (globalMax <= maxAllowedT)
   {
-    SetUpperBound(out_upperBound, globalMax);
+    UpdateUpperBound(out_upperBound, globalMax);
     return true;
   }
 
   const T* elements = Elements<T>();
 
   // before calling tree->Validate, we have to validate ourselves the boundaries of the elements span,
   // to round them to the nearest multiple of sElementsPerLeaf.
   size_t firstElementAdjustmentEnd = std::min(lastElement,
--- a/content/canvas/src/WebGLElementArrayCache.h
+++ b/content/canvas/src/WebGLElementArrayCache.h
@@ -31,31 +31,52 @@ struct WebGLElementArrayCacheTree;
  */
 class WebGLElementArrayCache {
 
 public:
   bool BufferData(const void* ptr, size_t byteLength);
   bool BufferSubData(size_t pos, const void* ptr, size_t updateByteSize);
 
   bool Validate(GLenum type, uint32_t maxAllowed, size_t first, size_t count,
-                uint32_t* out_upperBound = nullptr);
+                uint32_t* out_upperBound);
 
   template<typename T>
   T Element(size_t i) const { return Elements<T>()[i]; }
 
   WebGLElementArrayCache();
 
   ~WebGLElementArrayCache();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   bool BeenUsedWithMultipleTypes() const;
 
 private:
 
+  /*
+   * Returns true if a drawElements call with the given parameters should succeed,
+   * false otherwise.
+   *
+   * In other words, this returns true if all entries in the element array at positions
+   *
+   *    first .. first+count-1
+   *
+   * are less than or equal to maxAllowed.
+   *
+   * Input parameters:
+   *   maxAllowed: maximum value to be allowed in the specificied portion of the element array.
+   *   first: start of the portion of the element array to consume.
+   *   count: number of entries from the element array to consume.
+   *
+   * Output parameter:
+   *   out_upperBound: upon success, is set to the actual maximum value in the specified range,
+   *                   which is then guaranteed to be less than or equal to maxAllowed.
+   *                   upon failure, is set to the first value in the specified range, that
+   *                   is greater than maxAllowed.
+   */
   template<typename T>
   bool Validate(uint32_t maxAllowed, size_t first, size_t count,
                 uint32_t* out_upperBound);
 
   template<typename T>
   const T* Elements() const { return reinterpret_cast<const T*>(mBytes.Elements()); }
   template<typename T>
   T* Elements() { return reinterpret_cast<T*>(mBytes.Elements()); }
--- a/content/media/gmp/GMPChild.cpp
+++ b/content/media/gmp/GMPChild.cpp
@@ -7,16 +7,19 @@
 #include "GMPVideoDecoderChild.h"
 #include "GMPVideoEncoderChild.h"
 #include "GMPVideoHost.h"
 #include "nsIFile.h"
 #include "nsXULAppAPI.h"
 #include "gmp-video-decode.h"
 #include "gmp-video-encode.h"
 #include "GMPPlatform.h"
+#include "mozilla/dom/CrashReporterChild.h"
+
+using mozilla::dom::CrashReporterChild;
 
 #ifdef XP_WIN
 #include <stdlib.h> // for _exit()
 #else
 #include <unistd.h> // for _exit()
 #endif
 
 #if defined(XP_WIN)
@@ -39,16 +42,19 @@ GMPChild::~GMPChild()
 }
 
 bool
 GMPChild::Init(const std::string& aPluginPath,
                base::ProcessHandle aParentProcessHandle,
                MessageLoop* aIOLoop,
                IPC::Channel* aChannel)
 {
+#ifdef MOZ_CRASHREPORTER
+  SendPCrashReporterConstructor(CrashReporter::CurrentThreadId());
+#endif
 #if defined(XP_WIN)
   mozilla::SandboxTarget::Instance()->StartSandbox();
 #endif
   return LoadPluginLibrary(aPluginPath) &&
          Open(aChannel, aParentProcessHandle, aIOLoop);
 }
 
 bool
@@ -148,16 +154,29 @@ GMPChild::ProcessingError(Result aWhat)
       MOZ_CRASH("aborting because of MsgRouteError");
     case MsgValueError:
       MOZ_CRASH("aborting because of MsgValueError");
     default:
       MOZ_CRASH("not reached");
   }
 }
 
+mozilla::dom::PCrashReporterChild*
+GMPChild::AllocPCrashReporterChild(const NativeThreadId& aThread)
+{
+  return new CrashReporterChild();
+}
+
+bool
+GMPChild::DeallocPCrashReporterChild(PCrashReporterChild* aCrashReporter)
+{
+  delete aCrashReporter;
+  return true;
+}
+
 PGMPVideoDecoderChild*
 GMPChild::AllocPGMPVideoDecoderChild()
 {
   return new GMPVideoDecoderChild(this);
 }
 
 bool
 GMPChild::DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor)
--- a/content/media/gmp/GMPChild.h
+++ b/content/media/gmp/GMPChild.h
@@ -22,16 +22,18 @@ public:
   bool Init(const std::string& aPluginPath,
             base::ProcessHandle aParentProcessHandle,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
   bool LoadPluginLibrary(const std::string& aPluginPath);
   MessageLoop* GMPMessageLoop();
 
 private:
+  virtual PCrashReporterChild* AllocPCrashReporterChild(const NativeThreadId& aThread) MOZ_OVERRIDE;
+  virtual bool DeallocPCrashReporterChild(PCrashReporterChild*) MOZ_OVERRIDE;
   virtual PGMPVideoDecoderChild* AllocPGMPVideoDecoderChild() MOZ_OVERRIDE;
   virtual bool DeallocPGMPVideoDecoderChild(PGMPVideoDecoderChild* aActor) MOZ_OVERRIDE;
   virtual PGMPVideoEncoderChild* AllocPGMPVideoEncoderChild() MOZ_OVERRIDE;
   virtual bool DeallocPGMPVideoEncoderChild(PGMPVideoEncoderChild* aActor) MOZ_OVERRIDE;
   virtual bool RecvPGMPVideoDecoderConstructor(PGMPVideoDecoderChild* aActor) MOZ_OVERRIDE;
   virtual bool RecvPGMPVideoEncoderConstructor(PGMPVideoEncoderChild* aActor) MOZ_OVERRIDE;
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
   virtual void ProcessingError(Result aWhat) MOZ_OVERRIDE;
--- a/content/media/gmp/GMPParent.cpp
+++ b/content/media/gmp/GMPParent.cpp
@@ -10,16 +10,24 @@
 #include "nsILineInputStream.h"
 #include "nsNetUtil.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "mozilla/unused.h"
 
+#include "mozilla/dom/CrashReporterParent.h"
+using mozilla::dom::CrashReporterParent;
+
+#ifdef MOZ_CRASHREPORTER
+using CrashReporter::AnnotationTable;
+using CrashReporter::GetIDFromMinidump;
+#endif
+
 namespace mozilla {
 namespace gmp {
 
 GMPParent::GMPParent()
   : mState(GMPStateNotLoaded)
   , mProcess(nullptr)
 {
 }
@@ -48,20 +56,17 @@ GMPParent::Init(nsIFile* aPluginDir)
   return ReadGMPMetaData();
 }
 
 nsresult
 GMPParent::LoadProcess()
 {
   MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  if (mState == GMPStateLoaded) {
-    return NS_OK;
-  }
+  MOZ_ASSERT(mState == GMPStateNotLoaded);
 
   nsAutoCString path;
   if (NS_FAILED(mDirectory->GetNativePath(path))) {
     return NS_ERROR_FAILURE;
   }
 
   mProcess = new GMPProcessParent(path.get());
   if (!mProcess->Launch(30 * 1000)) {
@@ -78,80 +83,91 @@ GMPParent::LoadProcess()
   }
 
   mState = GMPStateLoaded;
 
   return NS_OK;
 }
 
 void
-GMPParent::MaybeUnloadProcess()
+GMPParent::CloseIfUnused()
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
-  if (mVideoDecoders.Length() == 0 &&
+  if (mState == GMPStateLoaded &&
+      mVideoDecoders.Length() == 0 &&
       mVideoEncoders.Length() == 0) {
-    UnloadProcess();
+    Shutdown();
   }
 }
 
 void
-GMPParent::UnloadProcess()
+GMPParent::CloseActive()
 {
-  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
-
-  if (mState == GMPStateNotLoaded) {
-    MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty());
-    return;
-  }
-
-  mState = GMPStateNotLoaded;
-
   // Invalidate and remove any remaining API objects.
   for (uint32_t i = mVideoDecoders.Length(); i > 0; i--) {
     mVideoDecoders[i - 1]->DecodingComplete();
   }
 
   // Invalidate and remove any remaining API objects.
   for (uint32_t i = mVideoEncoders.Length(); i > 0; i--) {
     mVideoEncoders[i - 1]->EncodingComplete();
   }
+}
 
+void
+GMPParent::Shutdown()
+{
+  MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
+
+  if (mState == GMPStateNotLoaded || mState == GMPStateClosing) {
+    MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty());
+    return;
+  }
+
+  CloseActive();
   Close();
-  if (mProcess) {
-    mProcess->Delete();
-    mProcess = nullptr;
-  }
+  DeleteProcess();
+  MOZ_ASSERT(mState == GMPStateNotLoaded);
+}
+
+void
+GMPParent::DeleteProcess()
+{
+  MOZ_ASSERT(mState == GMPStateClosing);
+  mProcess->Delete();
+  mProcess = nullptr;
+  mState = GMPStateNotLoaded;
 }
 
 void
 GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   // If the constructor fails, we'll get called before it's added
   unused << NS_WARN_IF(!mVideoDecoders.RemoveElement(aDecoder));
 
   // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
   // until after this has completed.
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
+  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::CloseIfUnused);
   NS_DispatchToCurrentThread(event);
 }
 
 void
 GMPParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   // If the constructor fails, we'll get called before it's added
   unused << NS_WARN_IF(!mVideoEncoders.RemoveElement(aEncoder));
 
   // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
   // until after this has completed.
-  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
+  nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::CloseIfUnused);
   NS_DispatchToCurrentThread(event);
 }
 
 GMPState
 GMPParent::State() const
 {
   return mState;
 }
@@ -192,16 +208,19 @@ GMPParent::SupportsAPI(const nsCString& 
 }
 
 bool
 GMPParent::EnsureProcessLoaded()
 {
   if (mState == GMPStateLoaded) {
     return true;
   }
+  if (mState == GMPStateClosing) {
+    return false;
+  }
 
   nsresult rv = LoadProcess();
 
   return NS_SUCCEEDED(rv);
 }
 
 nsresult
 GMPParent::GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD)
@@ -238,20 +257,87 @@ GMPParent::GetGMPVideoEncoder(GMPVideoEn
   }
   nsRefPtr<GMPVideoEncoderParent> vep = static_cast<GMPVideoEncoderParent*>(pvep);
   mVideoEncoders.AppendElement(vep);
   vep.forget(aGMPVE);
 
   return NS_OK;
 }
 
+#ifdef MOZ_CRASHREPORTER
+void
+GMPParent::WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes)
+{
+  notes.Put(NS_LITERAL_CSTRING("GMPPlugin"), NS_LITERAL_CSTRING("1"));
+  notes.Put(NS_LITERAL_CSTRING("PluginFilename"),
+                               NS_ConvertUTF16toUTF8(mName));
+  notes.Put(NS_LITERAL_CSTRING("PluginName"), mDisplayName);
+  notes.Put(NS_LITERAL_CSTRING("PluginVersion"), mVersion);
+}
+
+void
+GMPParent::GetCrashID(nsString& aResult)
+{
+  CrashReporterParent* cr = nullptr;
+  if (ManagedPCrashReporterParent().Length() > 0) {
+    cr = static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
+  }
+  if (NS_WARN_IF(!cr)) {
+    return;
+  }
+
+  AnnotationTable notes(4);
+  WriteExtraDataForMinidump(notes);
+  nsCOMPtr<nsIFile> dumpFile;
+  TakeMinidump(getter_AddRefs(dumpFile), nullptr);
+  if (!dumpFile) {
+    NS_WARNING("GMP crash without crash report");
+    return;
+  }
+  GetIDFromMinidump(dumpFile, aResult);
+  cr->GenerateCrashReportForMinidump(dumpFile, &notes);
+}
+#endif
+
 void
 GMPParent::ActorDestroy(ActorDestroyReason aWhy)
 {
-  UnloadProcess();
+  mState = GMPStateClosing;
+  if (AbnormalShutdown == aWhy) {
+    nsString dumpID;
+#ifdef MOZ_CRASHREPORTER
+    GetCrashID(dumpID);
+#endif
+    // now do something with the crash ID, bug 1038961
+  }
+
+  CloseActive();
+
+  // Normal Shutdown() will delete the process on unwind.
+  if (AbnormalShutdown == aWhy) {
+    NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &GMPParent::DeleteProcess));
+  }
+}
+
+mozilla::dom::PCrashReporterParent*
+GMPParent::AllocPCrashReporterParent(const NativeThreadId& aThread)
+{
+#ifndef MOZ_CRASHREPORTER
+  MOZ_ASSERT(false, "Should only be sent if crash reporting is enabled.");
+#endif
+  CrashReporterParent* cr = new CrashReporterParent();
+  cr->SetChildData(aThread, GeckoProcessType_GMPlugin);
+  return cr;
+}
+
+bool
+GMPParent::DeallocPCrashReporterParent(PCrashReporterParent* aCrashReporter)
+{
+  delete aCrashReporter;
+  return true;
 }
 
 PGMPVideoDecoderParent*
 GMPParent::AllocPGMPVideoDecoderParent()
 {
   GMPVideoDecoderParent* vdp = new GMPVideoDecoderParent(this);
   NS_ADDREF(vdp);
   return vdp;
--- a/content/media/gmp/GMPParent.h
+++ b/content/media/gmp/GMPParent.h
@@ -16,42 +16,66 @@
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIFile.h"
 #include "ThreadSafeRefcountingWithMainThreadDestruction.h"
 
 class nsILineInputStream;
 class nsIThread;
 
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+
+namespace mozilla {
+namespace dom {
+class PCrashReporterParent;
+class CrashReporterParent;
+}
+}
+#endif
+
 namespace mozilla {
 namespace gmp {
 
 class GMPCapability
 {
 public:
   nsCString mAPIName;
   nsTArray<nsCString> mAPITags;
 };
 
 enum GMPState {
   GMPStateNotLoaded,
-  GMPStateLoaded
+  GMPStateLoaded,
+  GMPStateClosing
 };
 
 class GMPParent MOZ_FINAL : public PGMPParent
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GMPParent)
 
   GMPParent();
 
   nsresult Init(nsIFile* aPluginDir);
   nsresult LoadProcess();
-  void MaybeUnloadProcess();
-  void UnloadProcess();
+
+  // Called internally to close this if we don't need it
+  void CloseIfUnused();
+
+  // Notify all active de/encoders that we are closing, either because of
+  // normal shutdown or unexpected shutdown/crash.
+  void CloseActive();
+
+  // Called by the GMPService to forcibly close active de/encoders at shutdown
+  void Shutdown();
+
+  // This must not be called while we're in the middle of abnormal ActorDestroy
+  void DeleteProcess();
+
   bool SupportsAPI(const nsCString& aAPI, const nsCString& aTag);
   nsresult GetGMPVideoDecoder(GMPVideoDecoderParent** aGMPVD);
   void VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder);
   nsresult GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE);
   void VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder);
   GMPState State() const;
 #ifdef DEBUG
   nsIThread* GMPThread();
@@ -81,17 +105,24 @@ public:
   already_AddRefed<nsIFile> GetDirectory() {
     return nsCOMPtr<nsIFile>(mDirectory).forget();
   }
 
 private:
   ~GMPParent();
   bool EnsureProcessLoaded();
   nsresult ReadGMPMetaData();
+#ifdef MOZ_CRASHREPORTER
+  void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
+  void GetCrashID(nsString& aResult);
+#endif
   virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
+
+  virtual PCrashReporterParent* AllocPCrashReporterParent(const NativeThreadId& aThread) MOZ_OVERRIDE;
+  virtual bool DeallocPCrashReporterParent(PCrashReporterParent* aCrashReporter) MOZ_OVERRIDE;
   virtual PGMPVideoDecoderParent* AllocPGMPVideoDecoderParent() MOZ_OVERRIDE;
   virtual bool DeallocPGMPVideoDecoderParent(PGMPVideoDecoderParent* aActor) MOZ_OVERRIDE;
   virtual PGMPVideoEncoderParent* AllocPGMPVideoEncoderParent() MOZ_OVERRIDE;
   virtual bool DeallocPGMPVideoEncoderParent(PGMPVideoEncoderParent* aActor) MOZ_OVERRIDE;
 
   GMPState mState;
   nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
   nsString mName; // base name of plugin on disk, UTF-16 because used for paths
--- a/content/media/gmp/GMPService.cpp
+++ b/content/media/gmp/GMPService.cpp
@@ -257,17 +257,17 @@ GeckoMediaPluginService::UnloadPlugins()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
 
   MOZ_ASSERT(!mShuttingDownOnGMPThread);
   mShuttingDownOnGMPThread = true;
 
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mPlugins.Length(); i++) {
-    mPlugins[i]->UnloadProcess();
+    mPlugins[i]->Shutdown();
   }
   mPlugins.Clear();
 }
 
 void
 GeckoMediaPluginService::LoadFromEnvironment()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
@@ -438,17 +438,17 @@ GeckoMediaPluginService::RemoveOnGMPThre
     return;
   }
 
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
     nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
     bool equals;
     if (NS_SUCCEEDED(directory->Equals(pluginpath, &equals)) && equals) {
-      mPlugins[i]->UnloadProcess();
+      mPlugins[i]->Shutdown();
       mPlugins.RemoveElementAt(i);
       return;
     }
   }
   NS_WARNING("Removing GMP which was never added.");
   nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   cs->LogStringMessage(MOZ_UTF16("Removing GMP which was never added."));
 }
--- a/content/media/gmp/PGMP.ipdl
+++ b/content/media/gmp/PGMP.ipdl
@@ -1,22 +1,30 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 PGMPVideoDecoder;
 include protocol PGMPVideoEncoder;
+include protocol PCrashReporter;
+
+using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 
 namespace mozilla {
 namespace gmp {
 
 intr protocol PGMP
 {
   manages PGMPVideoDecoder;
   manages PGMPVideoEncoder;
+  manages PCrashReporter;
+
+parent:
+  async PCrashReporter(NativeThreadId tid);
+
 child:
   async PGMPVideoDecoder();
   async PGMPVideoEncoder();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/.
  *
  * This Original Code has been modified by IBM Corporation.
  * Modifications made by IBM described herein are
  * Copyright (c) International Business Machines
  * Corporation, 2000
@@ -44,16 +44,17 @@
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIPresShell.h"
 #include "nsIPrincipal.h"
 #include "nsIRDFCompositeDataSource.h"
 #include "nsIRDFNode.h"
 #include "nsIRDFService.h"
 #include "nsIScriptContext.h"
+#include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIServiceManager.h"
 #include "mozilla/css/StyleRule.h"
 #include "nsIStyleSheet.h"
 #include "nsIURL.h"
 #include "nsViewManager.h"
 #include "nsIWidget.h"
 #include "nsIXULDocument.h"
@@ -799,22 +800,44 @@ IsInFeedSubscribeLine(nsXULElement* aEle
     if (idAtom && idAtom->Equals(NS_LITERAL_STRING("feedSubscribeLine"))) {
       return true;
     }
   }
   return false;
 }
 #endif
 
+class XULInContentErrorReporter : public nsRunnable
+{
+public:
+  XULInContentErrorReporter(nsIDocument* aDocument) : mDocument(aDocument) {}
+
+  NS_IMETHOD Run()
+  {
+    mDocument->WarnOnceAbout(nsIDocument::eImportXULIntoContent, false);
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIDocument> mDocument;
+};
+
 nsresult
 nsXULElement::BindToTree(nsIDocument* aDocument,
                          nsIContent* aParent,
                          nsIContent* aBindingParent,
                          bool aCompileEventHandlers)
 {
+  if (!aBindingParent &&
+      aDocument &&
+      !aDocument->AllowXULXBL() &&
+      !aDocument->HasWarnedAbout(nsIDocument::eImportXULIntoContent)) {
+    nsContentUtils::AddScriptRunner(new XULInContentErrorReporter(aDocument));
+  }
+
   nsresult rv = nsStyledElement::BindToTree(aDocument, aParent,
                                             aBindingParent,
                                             aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument &&
       !aDocument->LoadsFullXULStyleSheetUpFront() &&
       !aDocument->IsUnstyledDocument()) {
--- a/content/xul/content/test/chrome.ini
+++ b/content/xul/content/test/chrome.ini
@@ -2,8 +2,9 @@
 support-files =
   398289-resource.xul
   file_bug236853.rdf
 
 [test_bug233643.xul]
 [test_bug236853.xul]
 [test_bug398289.html]
 [test_bug775972.xul]
+[test_import_xul_to_content.xul]
new file mode 100644
--- /dev/null
+++ b/content/xul/content/test/test_import_xul_to_content.xul
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<window title="Mozilla Importing XUL into Content"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027299"
+     target="_blank">Mozilla Bug 1027299</a>
+  </body>
+
+  <browser id="browserelt" src="about:blank" type="content"/>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  Components.utils.import("resource://gre/modules/Services.jsm");
+
+  SimpleTest.waitForExplicitFinish();
+
+  function expectWarning(expected, when, f) {
+    Services.console.reset();
+
+    f();
+
+    var sawWarning = false;
+    var msgs = Services.console.getMessageArray();
+    for (var i = 0; i < msgs.length; i++) {
+      var msg = msgs[i];
+      if (!msg || !(msg instanceof Components.interfaces.nsIScriptError)) {
+        continue;
+      }
+
+      if (msg.category.contains("DOM") && msg.errorMessage.contains("Importing XUL")) {
+        sawWarning = true;
+      }
+    }
+
+    ok(sawWarning == expected, "correct warning condition when " + when);
+  }
+
+  var browser = document.getElementById("browserelt");
+  browser.addEventListener("load", function() {
+    var doc = browser.contentDocument;
+
+    // We add a <video> element, which contains anonymous XUL content. This should not warn.
+    var video = doc.createElement("video");
+    expectWarning(false, "appending video", function() {
+      doc.documentElement.appendChild(video);
+      // Force a layout flush to make sure the anonymous content is added.
+      let dummy = doc.documentElement.offsetLeft;
+    });
+
+    // We add some XUL to a content document. This should generate a warning.
+    var elt = document.createElement("label");
+    var newElt = doc.importNode(elt, false);
+    expectWarning(true, "appending XUL", function() {
+      doc.documentElement.appendChild(newElt);
+    });
+
+    SimpleTest.finish();
+  });
+
+  ]]>
+  </script>
+</window>
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -4,17 +4,16 @@
 
 "use strict";
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
-Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
   "resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "WebappOSUtils",
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2224,23 +2224,24 @@ Navigator::HasWifiManagerSupport(JSConte
   return nsIPermissionManager::ALLOW_ACTION == permission;
 }
 
 #ifdef MOZ_NFC
 /* static */
 bool
 Navigator::HasNFCSupport(JSContext* /* unused */, JSObject* aGlobal)
 {
+  nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
+
   // Do not support NFC if NFC content helper does not exist.
   nsCOMPtr<nsISupports> contentHelper = do_GetService("@mozilla.org/nfc/content-helper;1");
   if (!contentHelper) {
     return false;
   }
 
-  nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
   return win && (CheckPermission(win, "nfc-read") ||
                  CheckPermission(win, "nfc-write"));
 }
 #endif // MOZ_NFC
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 /* static */
 bool
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -1674,22 +1674,21 @@ InterfaceToJsval(nsPIDOMWindow* aWindow,
   if (!sgo) {
     return JS::NullValue();
   }
 
   JSObject *unrootedScopeObj = sgo->GetGlobalJSObject();
   NS_ENSURE_TRUE(unrootedScopeObj, JS::NullValue());
   JSRuntime *runtime = JS_GetObjectRuntime(unrootedScopeObj);
   JS::Rooted<JS::Value> someJsVal(runtime);
+  JS::Rooted<JSObject*> scopeObj(runtime, unrootedScopeObj);
   nsresult rv;
 
   { // Protect someJsVal from moving GC in ~JSAutoCompartment
     AutoJSContext cx;
-
-    JS::Rooted<JSObject*> scopeObj(cx, unrootedScopeObj);
     JSAutoCompartment ac(cx, scopeObj);
 
     rv = nsContentUtils::WrapNative(cx, aObject, aIID, &someJsVal);
   }
   if (NS_FAILED(rv)) {
     return JS::NullValue();
   }
 
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -762,20 +762,26 @@ IMEStateManager::SetIMEState(const IMESt
     if (Preferences::GetBool("dom.forms.inputmode", false)) {
       aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::inputmode,
                         context.mHTMLInputInputmode);
     }
 
     aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::moz_action_hint,
                       context.mActionHint);
 
-    // if we don't have an action hint and  return won't submit the form use "next"
-    if (context.mActionHint.IsEmpty() && aContent->Tag() == nsGkAtoms::input) {
+    // Get the input content corresponding to the focused node,
+    // which may be an anonymous child of the input content.
+    nsIContent* inputContent = aContent->FindFirstNonChromeOnlyAccessContent();
+
+    // If we don't have an action hint and
+    // return won't submit the form, use "next".
+    if (context.mActionHint.IsEmpty() &&
+        inputContent->Tag() == nsGkAtoms::input) {
       bool willSubmit = false;
-      nsCOMPtr<nsIFormControl> control(do_QueryInterface(aContent));
+      nsCOMPtr<nsIFormControl> control(do_QueryInterface(inputContent));
       mozilla::dom::Element* formElement = control->GetFormElement();
       nsCOMPtr<nsIForm> form;
       if (control) {
         // is this a form and does it have a default submit element?
         if ((form = do_QueryInterface(formElement)) &&
             form->GetDefaultSubmitElement()) {
           willSubmit = true;
         // is this an html form and does it only have a single text input element?
--- a/dom/ipc/CrashReporterParent.cpp
+++ b/dom/ipc/CrashReporterParent.cpp
@@ -6,16 +6,18 @@
 #include "CrashReporterParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "nsXULAppAPI.h"
 #include <time.h>
 
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #include "nsICrashService.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsThreadUtils.h"
 #endif
 
 using namespace base;
 
 namespace mozilla {
 namespace dom {
 
 void
@@ -109,16 +111,17 @@ CrashReporterParent::GenerateChildData(c
     MOZ_ASSERT(mInitialized);
 
     nsAutoCString type;
     switch (mProcessType) {
         case GeckoProcessType_Content:
             type = NS_LITERAL_CSTRING("content");
             break;
         case GeckoProcessType_Plugin:
+        case GeckoProcessType_GMPlugin:
             type = NS_LITERAL_CSTRING("plugin");
             break;
         default:
             NS_ERROR("unknown process type");
             break;
     }
     mNotes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
 
@@ -130,24 +133,40 @@ CrashReporterParent::GenerateChildData(c
         mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
 
     bool ret = CrashReporter::AppendExtraData(mChildDumpID, mNotes);
     if (ret && processNotes)
         ret = CrashReporter::AppendExtraData(mChildDumpID, *processNotes);
     if (!ret)
         NS_WARNING("problem appending child data to .extra");
 
-    NotifyCrashService();
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    class NotifyOnMainThread : public nsRunnable
+    {
+    public:
+        NotifyOnMainThread(CrashReporterParent* aCR)
+            : mCR(aCR)
+        { }
 
+        NS_IMETHOD Run() {
+            mCR->NotifyCrashService();
+            return NS_OK;
+        }
+    private:
+        CrashReporterParent* mCR;
+    };
+    SyncRunnable::DispatchToThread(mainThread, new NotifyOnMainThread(this));
     return ret;
 }
 
 void
 CrashReporterParent::NotifyCrashService()
 {
+    MOZ_ASSERT(NS_IsMainThread());
+
     nsCOMPtr<nsICrashService> crashService =
         do_GetService("@mozilla.org/crashservice;1");
     if (!crashService) {
         return;
     }
 
     if (mProcessType == GeckoProcessType_Content) {
         crashService->AddCrash(nsICrashService::PROCESS_TYPE_CONTENT,
--- a/dom/ipc/PCrashReporter.ipdl
+++ b/dom/ipc/PCrashReporter.ipdl
@@ -1,30 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set sw=4 ts=8 et tw=80 : 
  * 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 PContent;
 include protocol PPluginModule;
+include protocol PGMP;
 
 namespace mozilla {
 namespace dom {
 
 struct Mapping {
   nsCString library_name;
   nsCString file_id;
   uintptr_t start_address;
   size_t mapping_length;
   size_t file_offset;
 };
 
 intr protocol PCrashReporter {
-  manager PContent or PPluginModule;
+  manager PContent or PPluginModule or PGMP;
 parent:
   AnnotateCrashReport(nsCString key, nsCString data);
   AppendAppNotes(nsCString data);
   __delete__();
 };
 
 }
 }
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -202,8 +202,9 @@ KeyNameLiveWarning=KeyboardEvent.key val
 # LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Apps".
 KeyNameAppsWarning=KeyboardEvent.key value "Apps" is obsolete and will be removed. For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
 # LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "FastFwd" and "MediaFastForward".
 KeyNameFastFwdWarning=KeyboardEvent.key value "FastFwd" is obsolete and will be renamed to "MediaFastForward". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
 # LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Zoom" and "ZoomToggle".
 KeyNameZoomWarning=KeyboardEvent.key value "Zoom" is obsolete and will be renamed to "ZoomToggle". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
 # LOCALIZATION NOTE: Do not translate "KeyboardEvent.key" and "Dead".
 KeyNameDeadKeysWarning=KeyboardEvent.key values starting with "Dead" are obsolete and will be merged into just "Dead". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
+ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+run-if = os == "win" # Bug 1040924 - Failure prone on !Windows
 support-files =
   worker.js
   worker2.js
   worker3.js
   parse_error_worker.js
   install_event_worker.js
 
 [test_installation_simple.html]
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/bug923390.js
+++ /dev/null
@@ -1,3 +0,0 @@
-if (getpda) {
-  getpda();
-}
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -218,21 +218,23 @@ class BaselineFrame
     }
 
     bool copyRawFrameSlots(AutoValueVector *vec) const;
 
     bool hasReturnValue() const {
         return flags_ & HAS_RVAL;
     }
     MutableHandleValue returnValue() {
-        return MutableHandleValue::fromMarkedLocation(reinterpret_cast<Value *>(&loReturnValue_));
+        if (!hasReturnValue())
+            addressOfReturnValue()->setUndefined();
+        return MutableHandleValue::fromMarkedLocation(addressOfReturnValue());
     }
     void setReturnValue(const Value &v) {
+        returnValue().set(v);
         flags_ |= HAS_RVAL;
-        returnValue().set(v);
     }
     inline Value *addressOfReturnValue() {
         return reinterpret_cast<Value *>(&loReturnValue_);
     }
 
     bool hasCallObj() const {
         return flags_ & HAS_CALL_OBJ;
     }
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -393,30 +393,25 @@ Range::intersect(TempAllocator &alloc, c
     if (!lhs)
         return new(alloc) Range(*rhs);
     if (!rhs)
         return new(alloc) Range(*lhs);
 
     int32_t newLower = Max(lhs->lower_, rhs->lower_);
     int32_t newUpper = Min(lhs->upper_, rhs->upper_);
 
-    // :TODO: This information could be used better. If upper < lower, then we
-    // have conflicting constraints. Consider:
+    // If upper < lower, then we have conflicting constraints. Consider:
     //
     // if (x < 0) {
     //   if (x > 0) {
     //     [Some code.]
     //   }
     // }
     //
-    // In this case, the block is dead. Right now, we just disregard this fact
-    // and make the range unbounded, rather than empty.
-    //
-    // Instead, we should use it to eliminate the dead block.
-    // (Bug 765127)
+    // In this case, the block is unreachable.
     if (newUpper < newLower) {
         // If both ranges can be NaN, the result can still be NaN.
         if (!lhs->canBeNaN() || !rhs->canBeNaN())
             *emptyRange = true;
         return nullptr;
     }
 
     bool newHasInt32LowerBound = lhs->hasInt32LowerBound_ || rhs->hasInt32LowerBound_;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2587,75 +2587,16 @@ Clone(JSContext *cx, unsigned argc, jsva
     JSObject *clone = JS_CloneFunctionObject(cx, funobj, parent);
     if (!clone)
         return false;
     args.rval().setObject(*clone);
     return true;
 }
 
 static bool
-GetPDA(JSContext *cx, unsigned argc, jsval *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject vobj(cx);
-    bool ok;
-    JSPropertyDescArray pda;
-    JSPropertyDesc *pd;
-
-    if (!JS_ValueToObject(cx, args.get(0), &vobj))
-        return false;
-    if (!vobj) {
-        args.rval().setUndefined();
-        return true;
-    }
-
-    RootedObject aobj(cx, JS_NewArrayObject(cx, 0));
-    if (!aobj)
-        return false;
-    args.rval().setObject(*aobj);
-
-    ok = !!JS_GetPropertyDescArray(cx, vobj, &pda);
-    if (!ok)
-        return false;
-    pd = pda.array;
-
-    RootedObject pdobj(cx);
-    RootedValue id(cx);
-    RootedValue value(cx);
-    RootedValue flags(cx);
-    RootedValue alias(cx);
-
-    for (uint32_t i = 0; i < pda.length; i++, pd++) {
-        pdobj = JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr());
-        if (!pdobj) {
-            ok = false;
-            break;
-        }
-
-        /* Protect pdobj from GC by setting it as an element of aobj now */
-        ok = !!JS_SetElement(cx, aobj, i, pdobj);
-        if (!ok)
-            break;
-
-        id = pd->id;
-        value = pd->value;
-        flags.setInt32(pd->flags);
-        alias = pd->alias;
-        ok = JS_SetProperty(cx, pdobj, "id", id) &&
-             JS_SetProperty(cx, pdobj, "value", value) &&
-             JS_SetProperty(cx, pdobj, "flags", flags) &&
-             JS_SetProperty(cx, pdobj, "alias", alias);
-        if (!ok)
-            break;
-    }
-    JS_PutPropertyDescArray(cx, &pda);
-    return ok;
-}
-
-static bool
 GetSLX(JSContext *cx, unsigned argc, jsval *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedScript script(cx);
 
     script = ValueToScript(cx, args.get(0));
     if (!script)
         return false;
@@ -4699,20 +4640,16 @@ static const JSFunctionSpecWithHelp shel
     JS_FN_HELP("build", BuildDate, 0, 0,
 "build()",
 "  Show build date and time."),
 
     JS_FN_HELP("intern", Intern, 1, 0,
 "intern(str)",
 "  Internalize str in the atom table."),
 
-    JS_FN_HELP("getpda", GetPDA, 1, 0,
-"getpda(obj)",
-"  Get the property descriptors for obj."),
-
     JS_FN_HELP("getslx", GetSLX, 1, 0,
 "getslx(obj)",
 "  Get script line extent."),
 
     JS_FN_HELP("evalcx", EvalInContext, 1, 0,
 "evalcx(s[, o])",
 "  Evaluate s in optional sandbox object o.\n"
 "  if (s == '' && !o) return new o with eager standard classes\n"
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -772,21 +772,21 @@ class InterpreterFrame
 
     void unsetPushedSPSFrame() {
         flags_ &= ~HAS_PUSHED_SPS_FRAME;
     }
 
     /* Return value */
 
     bool hasReturnValue() const {
-        return !!(flags_ & HAS_RVAL);
+        return flags_ & HAS_RVAL;
     }
 
     MutableHandleValue returnValue() {
-        if (!(flags_ & HAS_RVAL))
+        if (!hasReturnValue())
             rval_.setUndefined();
         return MutableHandleValue::fromMarkedLocation(&rval_);
     }
 
     void markReturnValue() {
         flags_ |= HAS_RVAL;
     }
 
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/812665.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body onload="document.getElementById('x').style.transformStyle = '';">
+<div><span id="x" style="transform-style: preserve-3d;"><div><div style="position: fixed;"></div></div></span></div>
+</body>
+</html>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -406,16 +406,17 @@ load 767593-2.html
 load 770381-1.html
 load 772306.html
 load 788360.html
 load 793848.html
 load 795646.html
 skip-if(1) load 802902.html # bug 901752
 load 806056-1.html
 load 806056-2.html
+load 812665.html
 load 813372-1.html
 asserts-if(gtk2Widget,0-1) load 822865.html # bug 540078
 load 824862.html
 load 826163.html
 load 833604-1.html
 load 835056.html
 load 836990-1.html
 load 840480.html
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/783228.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+</head>
+<body>
+<div style="-moz-columns: auto 28em; padding: 10px;">
+<p>...</p>
+<div style="width: 400px;">
+<div style="float:left;">
+<img src="image.jpg"><br>.
+</div>
+</div>
+
+<div style="clear: both"></div><div style="width: 400px;"><div style="float:left;"><img src="image.jpg"><br>
+</div>
+</div>
+
+<div style="clear: both"></div><p>... ... ... ... ... ... ...</p><br>.
+
+<p>... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 
+... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... 
+... ... ... ... ... ... ...</p>
+<div style="width: 400px;">
+<div style="float:left;">
+<img src="image.jpg"><br>.
+</div>
+</div>
+
+<div style="clear: both"></div>
+<img src="image.jpg"><br>.
+
+<p>...</p><img src="image.jpg"><br>.
+<img src="image.jpg"><br>
+
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/785555.html
@@ -0,0 +1,12 @@
+<dd><output><dfn><blockquote></blockquote><body dir=rtl>
+ H.*XX  mhF ~0Gdv`a
+<table>
+  <figcaption id=test></table>
+
+
+<script>
+setTimeout("CFcrash()", 10);
+function CFcrash() {
+test.style.display = "inline-block";
+}
+</script>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/812879-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  var table = document.createElement("table");
+  var tbody = document.createElement("tbody");
+  var td = document.createElement("td");
+  tbody.appendChild(td);
+  table.appendChild(tbody);
+  document.body.appendChild(table);
+  td.style.marginTop = "126102421%";
+  td.style.marginLeft = "126102421%";
+  td.style.cssFloat = "right";
+  td.style.pageBreakInside = "avoid";
+
+  document.documentElement.offsetHeight;
+
+  tbody.style.overflowX = "hidden";
+
+  document.documentElement.offsetHeight;
+
+  document.body.style.MozColumns = "auto";
+  tbody.style.color = "red";
+
+  document.documentElement.offsetHeight;
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/812879.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body onload="document.getElementById('x').style.overflowX = 'hidden';">
+<table><tbody id="x"><tr><td style="margin-top: 126102421%; margin-right: 126102421%; float: right; page-break-inside: avoid;"></td></tr></tbody></table>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/812893.html
@@ -0,0 +1,15 @@
+><select size='18"' style='border-style: ' tabindex="    +"></select><style>div {
+    -moz-box-sizing: border-box;
+    }
+div:not([autohide="true"]) {
+    width: 96px;
+    height: 96px;
+    margin: 10px;
+    -moz-padding-end: 176em;
+    }
+#one:not([type=image]) {
+    font-size: 0.61em;
+</style>
+<body style="-moz-shape-inside: rectangle(53, 251, 25298px, 168); padding: 7 2319499247 7 -moz-calc(143px 179%); ">><form>R<kbd><footer><cell style="font-size-adjust: 18; ">_40ww Nq FI0[#  9*| kZf0. 8[7 0v]N=E4-T :es></footer></kbd><p hidden=true>>><div id=one>  H jk*Fk(s8{8q	F	bMIf T [  Kr~xP si%; z 	*jprB</div>
+>><length><hr style='wrap-padding: 238px; padding-bottom: 248px; '>>><description style="box-pack: start; border-style: inset; "><div><style>
+* { ruby-span: 2647821777; -moz-columns: 70 2px;>>
\ No newline at end of file
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -474,34 +474,39 @@ load 762902.html
 load 762764-1.html
 load 765409.html
 asserts(0-200) load 765621.html # bug 703550
 asserts(0-200) load 767765.html # bug 407550, bug 871758, and various nscoord_MAX related asserts
 load 769303-1.html
 load 769303-2.html
 load 769120.html
 load 777838.html
+load 783228.html
 skip-if(Android) load 784600.html
+load 785555.html
 load 786740-1.html
 asserts(0-4) test-pref(font.size.inflation.emPerLine,15) load 791601.xhtml # 3 counts of bug 871327, 1 bug 367185
 test-pref(font.size.inflation.minTwips,120) load 794693.html
 asserts-if(!Android,8) load 798020-1.html
 load 798235-1.html
 load 799207-1.html
 load 799207-2.html
 load 801268-1.html
 load 804089-1.xhtml
 load 807565-1.html
 load 807565-2.html
 load 810726.html
+load 812879.html
+load 812879-2.html
 load 825810-1.html
 load 825810-2.html
 load 827076.html
 load 840818.html
 load 812822-1.html
+load 812893.html
 asserts(1) load 824297-1.html # bug 399262
 load 826483-1.html
 asserts(1) load 826532-1.html # bug 399262
 load 827168-1.html
 load 836895.html
 load 840787.html
 load 842132-1.html
 load 842166.html
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8433518bc9180d0f6d2020c3b3630e81bf024d6e
GIT binary patch
literal 2646
zc%0Q#2~bl<82(>gE&_^C5Yg6DjuvI8RY24#4^a@04tRhTMJiq#9`(eNhoDud4&zZ&
zstt%0#i1Ed@IZ(tsI)o(5fP{+L<EGv1V|(ad2hS1mv)@#G1E@{-|RnccmMtO`@Y@%
z(L<zyK2rmy1Oh=25QINK^)L}QgvBhLV=*pi@jNb<<nnoZ{>vt?65=M{^M%$zE0M+U
zpN+N1#?rAQ`FSaaN0K~|fG-gJZ9~^UEQIxtMiN7SBPK{Ofl5IFK=59|Bj6V!I3$<H
z7ho9HI3T|dhEI|hG!G-jv)k}IaK*g7Lq<&C_nWa$;I!Iq<o5lULTCSTCH6BbwJx4f
zYm%%){T&844s?D01Gk~Wyu3&Gj2<&~;-rAU$w5=5&YB$>HfQdq;foe8S-LDbCU)(*
z^&8?hZrYr@W9JvUcJE0!aPZJqX@`#-O+S%!^4n9{r_bc%oxhM@aPiXRE7xz7mfgH{
zyZrlmRrjkO)I6-MYfwLFeA?99@~pL^vrDIc(QPoAEW8LHiC^uFyu_Fn$8rV&3on8b
zZwU}{c|%6<`%ai4Sh%{M)5z^YJOBNe=Sr-cJ!fj|qt;Z4`nz~FxOP~my=3;+h$a0a
zX1@{pomU;$kOV$FQVbLrZB;(kBI2L;7Nq;`BR@XB=&8M~NNVmoNL^N)cjwCVOADp?
zrHK**y=lW`1b%yHO=TAQ4uX!g?DoD0lv#cP^B4r`W`^U6KpvQCSc{-cVd73hkgyLV
z+GYe_N_E1+EHjX5P#|!iTExQ{lNZo8r0j=ug;QjYE*ilu$~+%I#w2-nO9k;SZx`oT
zhCn-2lCr>|@>KQ8bw@d+JNI<ng{@f~*4Mfpi)x-$Xci6Y7$fis5!BM!$?uL*gcxMY
z4}avII%1=fe2aOgN@`7CA0MsA^1l=17*!NaPG<YaL<lN++x9f28vL)iWALLXO*&8w
zBCU)86@p^Sa)<kMxsFU`jTz+sVnLjOj?r)pQPhsH$4du}b5oD>;kD-%B_t(MUn}dw
zgt^6@turQN5Nm$4J#!+_Hw8h`NV+_{0Ks=>Bjs#=1p7Bvv6oUn9sqVB#H-F8pTvc3
z^Zuea>}4s#J53sMn2U5{L9A5Ysk~F^Cz*(##F1*uok!P%%9IGK>Dxsy2<8Vs<cUO)
z8@%Sa5Y%m9gC3v70SY$hK4Gp-sF!!n^A0u{S2aav*k&S7?Ui<h<;vS~YBb$D&mj=%
zPf^XgGkX0ommA1@nbTC`zE<gT$xCEs{{$HV4DB|8nPWqErZ;8FMu5{Dqw&qH2v4Na
z<@NFGw54YdM0%L-+7UX+EI!Cit-i>t4`*0y0o^e8Lb>Ak71oQ$e1mYxd_L9iNoyj4
z&k?kE_(|?YwihCBJwF?PER9{70wxJ7EXO0U_3V}01O$e8Y(QJEN$tjA>|ll;?^QQP
z%2p{m1ND)nCR*1&!?x+{i$@3!)aKANKhT~5u+0_$cQGExUx6SZOir6U5IE_xnKgDq
zLJGKLa9(xp_)W!VjLw^7%v#E19U|xo?OBPSe0eq1_EdfpUy@yK%OyduQ{K4X0*2|7
zIK1+8(XiWupnW_7msO!SVi|%7vB1X4S164h>DkQIi(R-lRNyia7;JP8Q|hbZx)EGg
zb`B4xYs7vIEw8JF-BkoHzQHQ{<RGZ=#HB7DXz$1ZjXCY^EVg)_G2M-<p;T!S`>W%%
z_3F^#%-qK=L3uW5qh)neFS=w>La(~$v<G={1t)I};2s_GOd1YF2y7h<BQUhW;2tAR
zo5c?H)GlI^H%Pm&7BXBb$1PLcV)*Tp1`E9))v!@fl4|NzM=KR<5iXV;(qxHl5LQ4g
zWj0d{Pbfas;)CU-DU%Kt%$o@|bj8XlmZkMAxI*tW55TY=Wiw_3ZI&YaUv1v@1(Qju
zGAN;Wf>sf7&%?VgLgDDV{MZ4zo}LPUO3}Vd9YK{z>@|81-_UiN9wylq*%8fS1$)+S
z<1CR*FN{7CKQZOm!6&lPx%Zvgx>R)4EI<6Ft(9tuv2l=e>ndOESeU&{Q}=z8gNol<
i;QyI){_#V5`z-My%3IU-RhygC&;6eVyWo3;YJUQK%F<E*
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1587,17 +1587,17 @@ skip-if(B2G) fuzzy-if(d2d,52,1051) fuzzy
 != 589682-1.html 589682-1-notref.html
 skip-if(!browserIsRemote) == 593243-1.html 593243-1-ref.html # bug 593168
 skip-if(!browserIsRemote) == 593243-2.html 593243-2-ref.html # bug 593168
 == 593544-1.html 593544-1-ref.html
 random-if(Android) == 594333-1.html 594333-1-ref.html
 == 594624-1.html 594624-1-ref.html
 == 594737-1.html 594737-1-ref.html
 == 597721-1.html 597721-1-ref.html
-random-if(winWidget) fuzzy-if(B2G,1,40) fuzzy-if(Android,38,539) needs-focus == 598726-1.html 598726-1-ref.html # Fails on Windows, bug 782196
+random-if(winWidget) fuzzy-if(B2G,3,40) fuzzy-if(Android,38,539) needs-focus == 598726-1.html 598726-1-ref.html # Fails on Windows, bug 782196
 == 599113-1.html 599113-1-ref.html
 fails-if(!haveTestPlugin) == 599476.html 599476-ref.html
 == 599882-1a.html 599882-1-ref.html
 == 599882-1b.html 599882-1-ref.html
 == 599882-2.html 599882-2-ref.html
 == 600045-1.html 600045-1-ref.html
 skip-if(B2G) == 600803-1.html 600803-1-ref.html
 == 600974-1.html 600974-1-ref.html
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -1392,25 +1392,23 @@ nsXULPopupManager::FirePopupHidingEvent(
         !popupFrame->IsInContentShell()) {
       // XXXndeakin
       // If an attempt was made to hide this popup before the popupshown event
       // fired, then ePopupShown is set here even though it should be
       // ePopupVisible. This probably isn't worth the hassle of handling.
       popupFrame->SetPopupState(ePopupShown);
     }
     else {
-      // If the popup has an animate attribute and it is not set to false, assume
-      // that it has a closing transition and wait for it to finish. The transition
+      // If the popup has an animate attribute and it is not set to false, check
+      // if it has a closing transition and wait for it to finish. The transition
       // may still occur either way, but the view will be hidden and you won't be
       // able to see it. If there is a next popup, indicating that mutliple popups
       // are rolling up, don't wait and hide the popup right away since the effect
-      // would likely be undesirable. This also does a quick check to see if the
-      // popup has a transition defined, and skips the wait if not. Transitions
-      // are currently disabled on Linux due to rendering issues on certain
-      // configurations.
+      // would likely be undesirable. Transitions are currently disabled on Linux
+      // due to rendering issues on certain configurations.
 #ifndef MOZ_WIDGET_GTK
       if (!aNextPopup && aPopup->HasAttr(kNameSpaceID_None, nsGkAtoms::animate)) {
         // If animate="false" then don't transition at all. If animate="cancel",
         // only show the transition if cancelling the popup or rolling up.
         // Otherwise, always show the transition.
         nsAutoString animate;
         aPopup->GetAttr(kNameSpaceID_None, nsGkAtoms::animate, animate);
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1104,16 +1104,19 @@ pref("network.http.connection-retry-time
 // The number of seconds after sending initial SYN for an HTTP connection
 // to give up if the OS does not give up first
 pref("network.http.connection-timeout", 90);
 
 // The maximum number of current global half open sockets allowable
 // when starting a new speculative connection.
 pref("network.http.speculative-parallel-limit", 6);
 
+// Allow/Forbid speculative connections on loopback.
+pref("network.http.speculative.allowLoopback", false);
+
 // Whether or not to block requests for non head js/css items (e.g. media)
 // while those elements load.
 pref("network.http.rendering-critical-requests-prioritization", true);
 
 // Disable IPv6 for backup connections to workaround problems about broken
 // IPv6 connectivity.
 pref("network.http.fast-fallback-to-IPv4", true);
 
@@ -1523,16 +1526,20 @@ pref("network.proxy.autoconfig_url", "")
 // If we cannot load the PAC file, then try again (doubling from interval_min
 // until we reach interval_max or the PAC file is successfully loaded).
 pref("network.proxy.autoconfig_retry_interval_min", 5);    // 5 seconds
 pref("network.proxy.autoconfig_retry_interval_max", 300);  // 5 minutes
 
 // Use the HSTS preload list by default
 pref("network.stricttransportsecurity.preloadlist", true);
 
+// Prohibit resource loads from private networks (e.g. RFC1918 like IP
+// addresses) by documents which were loaded from public networks.
+pref("network.zonepolicy.enabled", true);
+
 pref("converter.html2txt.structs",          true); // Output structured phrases (strong, em, code, sub, sup, b, i, u)
 pref("converter.html2txt.header_strategy",  1); // 0 = no indention; 1 = indention, increased with header level; 2 = numbering and slight indention
 
 pref("intl.accept_languages",               "chrome://global/locale/intl.properties");
 pref("intl.menuitems.alwaysappendaccesskeys","chrome://global/locale/intl.properties");
 pref("intl.menuitems.insertseparatorbeforeaccesskeys","chrome://global/locale/intl.properties");
 pref("intl.charset.detector",               "chrome://global/locale/intl.properties");
 pref("intl.charset.fallback.override",      "");
--- a/netwerk/base/public/moz.build
+++ b/netwerk/base/public/moz.build
@@ -56,16 +56,17 @@ XPIDL_SOURCES += [
     'nsIMultiPartChannel.idl',
     'nsINestedURI.idl',
     'nsINetAddr.idl',
     'nsINetUtil.idl',
     'nsINetworkLinkService.idl',
     'nsINetworkPredictor.idl',
     'nsINetworkPredictorVerifier.idl',
     'nsINetworkProperties.idl',
+    'nsINetworkZonePolicy.idl',
     'nsINSSErrorsService.idl',
     'nsIParentChannel.idl',
     'nsIParentRedirectingChannel.idl',
     'nsIPermission.idl',
     'nsIPermissionManager.idl',
     'nsIPrivateBrowsingChannel.idl',
     'nsIProgressEventSink.idl',
     'nsIPrompt.idl',
--- a/netwerk/base/public/nsIIOService2.idl
+++ b/netwerk/base/public/nsIIOService2.idl
@@ -5,17 +5,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 "nsIIOService.idl"
 
 /**
  * nsIIOService2 extends nsIIOService
  */
-[scriptable, uuid(9a7dc724-0b5c-4b78-9722-1037074c02de)]
+[scriptable, uuid(b2344926-d194-41d5-b749-e6591a257428)]
 interface nsIIOService2 : nsIIOService
 {
   /**
    * While this is set, IOService will monitor an nsINetworkLinkService
    * (if available) and set its offline status to "true" whenever
    * isLinkUp is false.
    *
    * Applications that want to control changes to the IOService's offline
@@ -35,9 +35,15 @@ interface nsIIOService2 : nsIIOService
    *        case aURI is used
    * @param aProxyFlags flags from nsIProtocolProxyService to use
    *        when resolving proxies for this new channel
    * @return reference to the new nsIChannel object
    */
   nsIChannel newChannelFromURIWithProxyFlags(in nsIURI aURI,
                                              in nsIURI aProxyURI,
                                              in unsigned long aProxyFlags);
+
+  /**
+   * Returns a string ID for the current network. This ID should be
+   * refreshed when the network link changes.
+   */
+  readonly attribute ACString networkLinkID;
 };
--- a/netwerk/base/public/nsILoadGroup.idl
+++ b/netwerk/base/public/nsILoadGroup.idl
@@ -10,17 +10,17 @@ interface nsIRequestObserver;
 interface nsIInterfaceRequestor;
 interface nsILoadGroupConnectionInfo;
 
 typedef unsigned long nsLoadFlags;
 
 /**
  * A load group maintains a collection of nsIRequest objects. 
  */
-[scriptable, uuid(afb57ac2-bce5-4ee3-bb34-385089a9ba5c)]
+[scriptable, uuid(8531ccd9-f12f-4674-ae07-05b625cebf8b)]
 interface nsILoadGroup : nsIRequest
 {
     /**
      * The group observer is notified when requests are added to and removed
      * from this load group.  The groupObserver is weak referenced.
      */
     attribute nsIRequestObserver groupObserver;
 
@@ -88,16 +88,22 @@ interface nsILoadGroup : nsIRequest
      * to the group - typically via nsIDocShell::defaultLoadFlags on a new
      * docShell.
      * Note that these flags are *not* added to the default request for the
      * load group; it is expected the default request will already have these
      * flags (again, courtesy of setting nsIDocShell::defaultLoadFlags before
      * the docShell has created the default request.)
      */
     attribute nsLoadFlags defaultLoadFlags;
+
+    /**
+     * Allow/Deny this loadgroup to load resources from private, RFC1918-like
+     * addresses. See nsINetworkZonePolicy for more information.
+     */
+    attribute bool allowLoadsFromPrivateNetworks;
 };
 
 %{C++
 // Forward-declare mozilla::net::SpdyPushCache
 namespace mozilla {
 namespace net {
 class SpdyPushCache;
 }
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsINetworkZonePolicy.idl
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsISupports.idl"
+
+interface nsIRequest;
+
+/**
+ * NetworkZonePolicy is used to permit or deny nsIRequests loading from private
+ * IP addresses. Private in this case means RFC1918-like addresses and
+ * localhost. Primarily, this is to protect resources held on private
+ * networks from being loaded by documents which were loaded from the public
+ * internet.
+ */
+
+[scriptable, uuid(639a0797-7870-4a37-9110-57ea9f14783f)]
+interface nsINetworkZonePolicy : nsISupports
+{
+  /**
+   * Returns true if aRequest can connect to private, RFC1918-like addresses.
+   * Implementing classes should query the loadgroup of the request, along with
+   * the loadgroup's owning/parent hierarchy, to determine if permission is
+   * granted. This should result in requests generated by a public document
+   * being restricted to public loads only.
+   *
+   * @param aRequest  The nsIRequest which is trying to load from a private IP
+   *                  address.
+   * @returns         True if aRequest has permission to load from a private IP
+   *                  address; false if it does not.
+   */
+  bool checkPrivateNetworkPermission(in nsIRequest aRequest);
+
+  /**
+   * Tries to set permission in the loadgroup of aRequest with respect to
+   * loading from private IP addresses. Permission will only be set if the
+   * loadgroup's ancestors (parent-, owning- and docshell parent loadgroups) all
+   * allow private network loads. If permission cannot be set, the function
+   * returns silently.
+   * Note: only documents should be allowed to set permission for other
+   * resource loads in the loadgroup, so aRequest should represent a document
+   * load. Non document loads will be ignored and the function will return
+   * silently.
+   *
+   * @param aRequest  The request which is trying to set private load
+   *                  permission for its loadgroup.
+   * @param aAllowed  True if permission is to be granted; false if not.
+   */
+  void setPrivateNetworkPermission(in nsIRequest aRequest,
+                                   in bool aAllowed);
+};
--- a/netwerk/base/public/nsISocketTransport.idl
+++ b/netwerk/base/public/nsISocketTransport.idl
@@ -178,16 +178,22 @@ interface nsISocketTransport : nsITransp
 
     /**
      * If set, indicates that the socket should not connect if the hostname
      * resolves to an RFC1918 address or IPv6 equivalent.
      */
     const unsigned long DISABLE_RFC1918 = (1 << 5);
 
     /**
+     * If set, indicates that the socket should not connect if the hostname
+     * resolves to a loopback address.
+     */
+    const unsigned long DISABLE_LOOPBACK = (1 << 6);
+
+    /**
      * Socket QoS/ToS markings. Valid values are IPTOS_DSCP_AFxx or
      * IPTOS_CLASS_CSx (or IPTOS_DSCP_EF, but currently no supported
      * services require expedited-forwarding).
      * Not setting this value will leave the socket with the default
      * ToS value, which on most systems if IPTOS_CLASS_CS0 (formerly
      * IPTOS_PREC_ROUTINE).
      */
     attribute octet QoSBits;
--- a/netwerk/base/public/nsNetUtil.h
+++ b/netwerk/base/public/nsNetUtil.h
@@ -2394,9 +2394,31 @@ NS_IsSrcdocChannel(nsIChannel *aChannel)
   nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(aChannel);
   if (vsc) {
     vsc->GetIsSrcdocChannel(&isSrcdoc);
     return isSrcdoc;
   }
   return false;
 }
 
+/**
+ *  Provides 32 bits of PRNG; workaround for platform variances of RAND_MAX.
+ */
+inline uint32_t
+NS_Get32BitsOfPseudoRandom()
+{
+    // rand() provides different amounts of PRNG on different platforms.
+    // 15 or 31 bits are common amounts.
+
+    PR_STATIC_ASSERT(RAND_MAX >= 0xfff);
+
+#if RAND_MAX < 0xffffU
+    return ((uint16_t) rand() << 20) |
+            (((uint16_t) rand() & 0xfff) << 8) |
+            ((uint16_t) rand() & 0xff);
+#elif RAND_MAX < 0xffffffffU
+    return ((uint16_t) rand() << 16) | ((uint16_t) rand() & 0xffff);
+#else
+    return (uint32_t) rand();
+#endif
+}
+
 #endif // !nsNetUtil_h__
--- a/netwerk/base/src/moz.build
+++ b/netwerk/base/src/moz.build
@@ -42,16 +42,17 @@ UNIFIED_SOURCES += [
     'nsInputStreamChannel.cpp',
     'nsInputStreamPump.cpp',
     'nsIOService.cpp',
     'nsLoadGroup.cpp',
     'nsMediaFragmentURIParser.cpp',
     'nsMIMEInputStream.cpp',
     'nsNetAddr.cpp',
     'nsNetStrings.cpp',
+    'nsNetworkZonePolicy.cpp',
     'nsPACMan.cpp',
     'nsPreloadedStream.cpp',
     'nsProtocolProxyService.cpp',
     'nsProxyInfo.cpp',
     'nsRequestObserverProxy.cpp',
     'nsSerializationHelper.cpp',
     'nsServerSocket.cpp',
     'nsSimpleNestedURI.cpp',
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -144,16 +144,25 @@ nsIOService::nsIOService()
     , mManageOfflineStatus(false)
     , mSettingOffline(false)
     , mSetOfflineValue(false)
     , mShutdown(false)
     , mNetworkLinkServiceInitialized(false)
     , mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
     , mAutoDialEnabled(false)
 {
+    // XXX May need to remove this once Bug 939319 and associated bugs for
+    // NS_NETWORK_LINK_DATA_CHANGED are complete on all supported platforms.
+
+    // Subfields of unions cannot be targeted in an initializer list
+    mNetworkLinkSelfAddr.raw.family = PR_AF_UNSPEC;
+
+    // Right now, the network link ID is just 64bits of pseudo-randonmess.
+    mNetworkLinkID = NS_Get32BitsOfPseudoRandom();
+    mNetworkLinkID = (mNetworkLinkID << 32) | NS_Get32BitsOfPseudoRandom();
 }
 
 nsresult
 nsIOService::Init()
 {
     nsresult rv;
 
     // We need to get references to the DNS service so that we can shut it
@@ -1079,16 +1088,48 @@ nsIOService::SetManageOfflineStatus(bool
 }
 
 NS_IMETHODIMP
 nsIOService::GetManageOfflineStatus(bool* aManage) {
     *aManage = mManageOfflineStatus;
     return NS_OK;
 }
 
+uint64_t
+nsIOService::GetNetworkLinkID() const
+{
+    return mNetworkLinkID;
+}
+
+NS_IMETHODIMP
+nsIOService::GetNetworkLinkID(nsACString &aNetworkLinkID)
+{
+    char temp[21];
+    PR_snprintf(temp, sizeof(temp), "%llu", mNetworkLinkID);
+    aNetworkLinkID.Append(temp);
+
+    return NS_OK;
+}
+
+// XXX Remove this once Bug 939319 and associated bugs for
+// NS_NETWORK_LINK_DATA_CHANGED are complete on all supported platforms.
+// Right now, the network link ID is just 64bits of pseudo-randonmess.
+void
+nsIOService::UpdateNetworkLinkID(const mozilla::net::NetAddr aCurrentSelfAddr)
+{
+    if (IsLoopBackAddress(&aCurrentSelfAddr) ||
+        mNetworkLinkSelfAddr.EqualsIP(aCurrentSelfAddr)) {
+        return;
+    }
+    mNetworkLinkSelfAddr = aCurrentSelfAddr;
+
+    mNetworkLinkID = NS_Get32BitsOfPseudoRandom();
+    mNetworkLinkID = (mNetworkLinkID << 32) | NS_Get32BitsOfPseudoRandom();
+}
+
 nsresult
 nsIOService::TrackNetworkLinkStatusForOffline()
 {
     NS_ASSERTION(mManageOfflineStatus,
                  "Don't call this unless we're managing the offline status");
     if (!mNetworkLinkService)
         return NS_ERROR_FAILURE;
 
--- a/netwerk/base/src/nsIOService.h
+++ b/netwerk/base/src/nsIOService.h
@@ -13,16 +13,17 @@
 #include "nsWeakPtr.h"
 #include "nsIObserver.h"
 #include "nsWeakReference.h"
 #include "nsINetUtil.h"
 #include "nsIChannelEventSink.h"
 #include "nsCategoryCache.h"
 #include "nsISpeculativeConnect.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/net/DNS.h"
 
 #define NS_N(x) (sizeof(x)/sizeof(*x))
 
 // We don't want to expose this observer topic.
 // Intended internal use only for remoting offline/inline events.
 // See Bug 552829
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 
@@ -69,16 +70,25 @@ public:
 
     bool IsOffline() { return mOffline; }
     bool IsLinkUp();
 
     bool IsComingOnline() const {
       return mOffline && mSettingOffline && !mSetOfflineValue;
     }
 
+    // Returns the NetworkLinkID.
+    uint64_t GetNetworkLinkID() const;
+
+    // XXX Remove this once Bug 939319 and associated bugs for
+    // NS_NETWORK_LINK_DATA_CHANGED are complete on all supported
+    // platforms.
+    // Primes the network link ID with the current self address of this host.
+    void UpdateNetworkLinkID(const mozilla::net::NetAddr aCurrentSelfAddr);
+
 private:
     // These shouldn't be called directly:
     // - construct using GetInstance
     // - destroy using Release
     nsIOService();
     ~nsIOService();
 
     nsresult TrackNetworkLinkStatusForOffline();
@@ -128,16 +138,26 @@ private:
 
     nsTArray<int32_t>                    mRestrictedPortList;
 
     bool                                 mAutoDialEnabled;
 public:
     // Used for all default buffer sizes that necko allocates.
     static uint32_t   gDefaultSegmentSize;
     static uint32_t   gDefaultSegmentCount;
+
+private:
+    // XXX Maybe remove these once Bug 939319 and associated bugs for
+    // NS_NETWORK_LINK_DATA_CHANGED are complete on all supported platforms.
+    // Right now, the network link ID is just 64bits of pseudo-randonmess.
+    //
+    // ID of the current network link.
+    uint64_t                             mNetworkLinkID;
+    // IP address of this host for the current network link.
+    mozilla::net::NetAddr                mNetworkLinkSelfAddr;
 };
 
 /**
  * Reference to the IO service singleton. May be null.
  */
 extern nsIOService* gIOService;
 
 #endif // nsIOService_h__
--- a/netwerk/base/src/nsLoadGroup.cpp
+++ b/netwerk/base/src/nsLoadGroup.cpp
@@ -113,16 +113,17 @@ RescheduleRequests(PLDHashTable *table, 
 
 nsLoadGroup::nsLoadGroup(nsISupports* outer)
     : mForegroundCount(0)
     , mLoadFlags(LOAD_NORMAL)
     , mDefaultLoadFlags(0)
     , mStatus(NS_OK)
     , mPriority(PRIORITY_NORMAL)
     , mIsCanceling(false)
+    , mAllowLoadsFromPrivateNetworks(true)
     , mDefaultLoadIsTimed(false)
     , mTimedRequests(0)
     , mCachedRequests(0)
     , mTimedNonCachedRequestsUntilOnEndPageLoad(0)
 {
     NS_INIT_AGGREGATED(outer);
 
 #if defined(PR_LOGGING)
@@ -1089,16 +1090,30 @@ nsresult nsLoadGroup::MergeLoadFlags(nsI
 
     if (flags != oldFlags)
         rv = aRequest->SetLoadFlags(flags);
 
     outFlags = flags;
     return rv;
 }
 
+NS_IMETHODIMP
+nsLoadGroup::GetAllowLoadsFromPrivateNetworks(bool *aAllowed)
+{
+    *aAllowed = mAllowLoadsFromPrivateNetworks;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsLoadGroup::SetAllowLoadsFromPrivateNetworks(bool aAllowed)
+{
+    mAllowLoadsFromPrivateNetworks = aAllowed;
+    return NS_OK;
+}
+
 // nsLoadGroupConnectionInfo
 
 class nsLoadGroupConnectionInfo MOZ_FINAL : public nsILoadGroupConnectionInfo
 {
     ~nsLoadGroupConnectionInfo() {}
 
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
--- a/netwerk/base/src/nsLoadGroup.h
+++ b/netwerk/base/src/nsLoadGroup.h
@@ -77,16 +77,19 @@ protected:
 
     nsWeakPtr                       mObserver;
     nsWeakPtr                       mParentLoadGroup;
     
     nsresult                        mStatus;
     int32_t                         mPriority;
     bool                            mIsCanceling;
 
+    // Set if this loadgroup allows loads from private networks (RFC1918 etc).
+    bool                            mAllowLoadsFromPrivateNetworks;
+
     /* Telemetry */
     mozilla::TimeStamp              mDefaultRequestCreationTime;
     bool                            mDefaultLoadIsTimed;
     uint32_t                        mTimedRequests;
     uint32_t                        mCachedRequests;
 
     /* For nsPILoadGroupInternal */
     uint32_t                        mTimedNonCachedRequestsUntilOnEndPageLoad;
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/nsNetworkZonePolicy.cpp
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "prlog.h"
+#include "nsAutoPtr.h"
+#include "nsNetworkZonePolicy.h"
+#include "nsIChannel.h"
+#include "nsIDocShell.h"
+#include "nsIDocumentLoader.h"
+#include "nsILoadGroup.h"
+#include "nsILoadGroupChild.h"
+#include "nsIObserverService.h"
+#include "nsIRequestObserver.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#ifdef PR_LOGGING
+#include "nsString.h"
+#endif
+
+namespace mozilla
+{
+namespace net
+{
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo *gNZPLog;
+#define NZPLOG(msg, ...) \
+  PR_LOG(gNZPLog, PR_LOG_DEBUG, ("[NZP] %p: " msg, this, ##__VA_ARGS__))
+#else
+#define NZPLOG(msg, ...)
+#endif
+
+/* Keeps track of whether or not NZP is enabled */
+bool nsNetworkZonePolicy::sNZPEnabled = true;
+
+/* True if shutdown notification has been received. */
+bool nsNetworkZonePolicy::sShutdown = false;
+
+/* Singleton pointer. */
+StaticRefPtr<nsNetworkZonePolicy> nsNetworkZonePolicy::sSingleton;
+
+nsNetworkZonePolicy::nsNetworkZonePolicy()
+{
+  Preferences::AddBoolVarCache(&sNZPEnabled, "network.zonepolicy.enabled");
+
+  // Register for shutdown notification.
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+  } else {
+    NS_WARNING("failed to get observer service");
+  }
+
+#ifdef PR_LOGGING
+  if (!gNZPLog) {
+    gNZPLog = PR_NewLogModule("NZP");
+  }
+#endif
+}
+
+already_AddRefed<nsNetworkZonePolicy>
+nsNetworkZonePolicy::GetSingleton()
+{
+  if (sShutdown) {
+    return nullptr;
+  }
+
+  if (!sSingleton) {
+    sSingleton = new nsNetworkZonePolicy();
+  }
+
+  // Return a ref ptr to the singleton.
+  nsRefPtr<nsNetworkZonePolicy> nzp = sSingleton.get();
+  return nzp.forget();
+}
+
+nsNetworkZonePolicy::~nsNetworkZonePolicy() {}
+
+NS_IMPL_ISUPPORTS(nsNetworkZonePolicy,
+                  nsINetworkZonePolicy,
+                  nsIObserver)
+
+// nsIObserver interface
+NS_IMETHODIMP
+nsNetworkZonePolicy::Observe(nsISupports *aSubject,
+                             const char *aTopic,
+                             const char16_t *aData)
+{
+  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    sShutdown = true;
+    sSingleton = nullptr;
+  }
+  return NS_OK;
+}
+
+already_AddRefed<nsILoadGroup>
+nsNetworkZonePolicy::GetLoadGroupParent(nsILoadGroup *aLoadGroup)
+{
+  if (NS_WARN_IF(!aLoadGroup)) {
+    return nullptr;
+  }
+  MOZ_ASSERT(aLoadGroup);
+
+  DebugOnly<nsresult> rv = NS_OK;
+  nsCOMPtr<nsILoadGroup> parent;
+  nsCOMPtr<nsILoadGroupChild> loadGroupAsChild = do_QueryInterface(aLoadGroup);
+  if (!loadGroupAsChild) {
+    return nullptr;
+  }
+  rv = loadGroupAsChild->GetParentLoadGroup(getter_AddRefs(parent));
+  if (!parent) {
+    return nullptr;
+  }
+  NZPLOG("loadgroup %p getting parent loadgroup %p", aLoadGroup,
+         parent.get());
+  return parent.forget();
+}
+
+already_AddRefed<nsILoadGroup>
+nsNetworkZonePolicy::GetOwningLoadGroup(nsILoadGroup *aLoadGroup)
+{
+  if (NS_WARN_IF(!aLoadGroup)) {
+    return nullptr;
+  }
+  MOZ_ASSERT(aLoadGroup);
+
+  DebugOnly<nsresult> rv = NS_OK;
+  nsCOMPtr<nsILoadGroup> owner;
+  rv = aLoadGroup->GetLoadGroup(getter_AddRefs(owner));
+  if (!owner) {
+    return nullptr;
+  }
+  NZPLOG("loadgroup %p getting owning loadgroup %p", aLoadGroup, owner.get());
+  return owner.forget();
+}
+
+already_AddRefed<nsILoadGroup>
+nsNetworkZonePolicy::GetParentDocShellsLoadGroup(nsILoadGroup *aLoadGroup)
+{
+  if (NS_WARN_IF(!aLoadGroup)) {
+    return nullptr;
+  }
+  MOZ_ASSERT(aLoadGroup);
+
+  DebugOnly<nsresult> rv = NS_OK;
+  nsCOMPtr<nsIRequestObserver> observer;
+  rv = aLoadGroup->GetGroupObserver(getter_AddRefs(observer));
+  if (!observer) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(observer);
+  if (!docShell) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIDocShellTreeItem> parentAsTreeItem;
+  docShell->GetSameTypeParent(getter_AddRefs(parentAsTreeItem));
+  if (!parentAsTreeItem) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIDocumentLoader> parentAsDocLoader =
+    do_QueryInterface(parentAsTreeItem);
+  if (!parentAsDocLoader) {
+    return nullptr;
+  }
+  nsCOMPtr<nsILoadGroup> dsParent;
+  rv = parentAsDocLoader->GetLoadGroup(getter_AddRefs(dsParent));
+  if (!dsParent) {
+    return nullptr;
+  }
+  NZPLOG("loadgroup %p getting docshell parent's loadgroup %p",
+         aLoadGroup, dsParent.get());
+  return dsParent.forget();
+}
+
+bool
+nsNetworkZonePolicy::CheckLoadGroupAncestorHierarchies(nsILoadGroup *aLoadGroup)
+{
+  if (NS_WARN_IF(!aLoadGroup)) {
+    return false;
+  }
+  MOZ_ASSERT(aLoadGroup);
+
+  // Check the hierarchies of appropriate ancestors.
+  // 1. Parent loadgroup.
+  nsCOMPtr<nsILoadGroup> ancestor = GetLoadGroupParent(aLoadGroup);
+  if (ancestor) {
+    bool ancestorAllows = CheckLoadGroupHierarchy(ancestor);
+
+    NZPLOG("Loadgroup %p's parent loadgroup hierarchy %s private loads.",
+           aLoadGroup, ancestorAllows ? "allows" : "forbids");
+    if (!ancestorAllows) {
+      return false;
+    }
+  }
+
+  // 2. Owning loadgroup.
+  ancestor = GetOwningLoadGroup(aLoadGroup);
+  if (ancestor) {
+    bool ancestorAllows = CheckLoadGroupHierarchy(ancestor);
+
+    NZPLOG("Loadgroup %p's owning loadgroup hierarchy %s private loads.",
+           aLoadGroup, ancestorAllows ? "allows" : "forbids");
+    if (!ancestorAllows) {
+      return false;
+    }
+  }
+
+  // 3. Parent docshell's loadgroup.
+  ancestor = GetParentDocShellsLoadGroup(aLoadGroup);
+  if (ancestor) {
+    bool ancestorAllows = CheckLoadGroupHierarchy(ancestor);
+
+    NZPLOG("Loadgroup %p's parent docshell's loadgroup hierarchy %s private "
+           "loads.",
+           aLoadGroup, ancestorAllows ? "allows" : "forbids");
+    if (!ancestorAllows) {
+      return false;
+    }
+  }
+
+  // If there is no ancestor or they all have permission to load from private
+  // networks, return true.
+  NZPLOG("Loadgroup %p: no ancestor forbids loads from private networks.",
+         aLoadGroup);
+  return true;
+}
+
+bool
+nsNetworkZonePolicy::CheckLoadGroupHierarchy(nsILoadGroup *aLoadGroup)
+{
+  if (NS_WARN_IF(!aLoadGroup)) {
+    return false;
+  }
+  MOZ_ASSERT(aLoadGroup);
+
+  // Recurse until root load group of same type, or until ancestor forbids
+  // private loads.
+
+  // If current loadgroup does not allow private loads, just return.
+  bool allowed = false;
+  aLoadGroup->GetAllowLoadsFromPrivateNetworks(&allowed);
+  if (!allowed) {
+    NZPLOG("Loadgroup %p forbids loads from private networks.", aLoadGroup);
+    return false;
+  }
+
+  // Else, check the hierarchies of appropriate ancestors.
+  return CheckLoadGroupAncestorHierarchies(aLoadGroup);
+}
+
+/*
+ * nsNetworkZonePolicy : nsINetworkZonePolicy
+ */
+NS_IMETHODIMP
+nsNetworkZonePolicy::CheckPrivateNetworkPermission(nsIRequest *aRequest,
+                                                   bool *aAllowed)
+{
+  if (NS_WARN_IF(!aRequest)) {
+    return NS_ERROR_NULL_POINTER;
+  }
+  if (NS_WARN_IF(!aAllowed)) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  if (NS_WARN_IF(!aRequest || !aAllowed)) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  if (!sNZPEnabled) {
+    *aAllowed = true;
+    return NS_OK;
+  }
+
+#ifdef PR_LOGGING
+  nsAutoCString nameStr;
+  aRequest->GetName(nameStr);
+#endif
+  NZPLOG("CheckPrivateNetworkPermission for request %p [%s].", aRequest,
+         nameStr.get());
+
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (!loadGroup) {
+    NZPLOG("No loadgroup for request %p [%s]; private networks allowed.",
+           aRequest, nameStr.get());
+    *aAllowed = true;
+    return NS_OK;
+  }
+
+  // Find out if this loadgroup's hierarchy allows private loads.
+  bool hierarchyAllows = CheckLoadGroupHierarchy(loadGroup);
+
+  NZPLOG("LoadGroup %p for request %p [%s] is %s private loads.",
+         loadGroup.get(), aRequest, nameStr.get(),
+         hierarchyAllows ? "allowed" : "forbidden");
+
+  *aAllowed = hierarchyAllows;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNetworkZonePolicy::SetPrivateNetworkPermission(nsIRequest *aRequest,
+                                                 bool aAllowed)
+{
+  if (NS_WARN_IF(!aRequest)) {
+    return NS_ERROR_NULL_POINTER;
+  }
+
+  if (!sNZPEnabled) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsILoadGroup> loadGroup;
+  nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
+  if (!loadGroup || NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+#ifdef PR_LOGGING
+  nsAutoCString nameStr;
+  aRequest->GetName(nameStr);
+#endif
+  NZPLOG("SetPrivateNetworkPermission: try to %s for loadgroup %p of request "
+         "%p [%s].",
+         aAllowed ? "allow" : "forbid", loadGroup.get(), aRequest,
+         nameStr.get());
+
+  // Only allow a document request to set its loadgroup's permissions.
+  nsLoadFlags flags;
+  aRequest->GetLoadFlags(&flags);
+
+  if (!(flags & nsIChannel::LOAD_DOCUMENT_URI)) {
+    NZPLOG("Skipping request %p [%s] - not a document load", aRequest,
+           nameStr.get());
+    return NS_OK;
+  }
+
+  // Do NOT allow a document load to override the loadgroup's hierarchy.
+  bool ancestorsAllow = CheckLoadGroupAncestorHierarchies(loadGroup);
+
+  NZPLOG("LoadGroup %p ancestors for request %p [%s] %s private loads.",
+         loadGroup.get(), aRequest, nameStr.get(),
+         ancestorsAllow ? "allows" : "forbids");
+
+  if (!ancestorsAllow) {
+    NZPLOG("Request %p [%s] can't override hierarchy.", aRequest,
+           nameStr.get());
+    return NS_OK;
+  }
+
+  return loadGroup->SetAllowLoadsFromPrivateNetworks(aAllowed);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/nsNetworkZonePolicy.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nznetworkzonepolicy_h__
+#define __nznetworkzonepolicy_h__
+
+#include "nsCOMPtr.h"
+#include "mozilla/StaticPtr.h"
+#include "nsINetworkZonePolicy.h"
+#include "nsIObserver.h"
+
+class nsILoadGroup;
+
+namespace mozilla
+{
+namespace net
+{
+
+/**
+ * class nsNetworkZonePolicy
+ *
+ * Implements nsINetworkZonePolicy: used by nsIRequest objects to check if
+ * they have permission to load from private, RFC1918-like IP addresses.
+ * See nsINetworkZonePolicy for more info.
+ */
+class nsNetworkZonePolicy : public nsINetworkZonePolicy
+                          , public nsIObserver
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSINETWORKZONEPOLICY
+  NS_DECL_NSIOBSERVER
+
+  static already_AddRefed<nsNetworkZonePolicy> GetSingleton();
+
+private:
+  nsNetworkZonePolicy();
+  virtual ~nsNetworkZonePolicy();
+
+  // Returns the parent loadgroup of aLoadGroup, or nullptr if none exists.
+  already_AddRefed<nsILoadGroup> GetLoadGroupParent(nsILoadGroup *aLoadGroup);
+
+  // Returns the owning loadgroup of aLoadGroup, or nullptr if none exists.
+  already_AddRefed<nsILoadGroup> GetOwningLoadGroup(nsILoadGroup *aLoadGroup);
+
+  // Returns the loadgroup of the parent docshell of aLoadGroup's docshell, or
+  // nullptr if none exists.
+  already_AddRefed<nsILoadGroup>
+    GetParentDocShellsLoadGroup(nsILoadGroup *aLoadGroup);
+
+  // Checks aLoadGroup and its ancestors (parent-, owning- and docshell
+  // parent's loadgroups) to check for permission to load from private IP
+  // addresses. The function follows the ancestor hierarchy to the root
+  // loadgroup, or until a loadgroup is forbidden to load from private
+  // networks. In this way, the loadgroup and all of its ancestor loadgroups
+  // must have permission for this function to return true.
+  bool CheckLoadGroupHierarchy(nsILoadGroup *aLoadGroup);
+
+  // Similar to CheckLoadGroupHierarchy, except this function checks the
+  // ancestor hierarchy only for permission to load from private networks;
+  // aLoadGroup is not checked.
+  bool CheckLoadGroupAncestorHierarchies(nsILoadGroup *aLoadGroup);
+
+  // Keeps track of whether or not NZP is enabled.
+  static bool sNZPEnabled;
+
+  // True if shutdown notification has been received.
+  static bool sShutdown;
+
+  // Singleton pointer.
+  static StaticRefPtr<nsNetworkZonePolicy> sSingleton;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif /* __nznetworkzonepolicy_h__ */
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -1025,16 +1025,20 @@ nsSocketTransport::ResolveHost()
 
     uint32_t dnsFlags = 0;
     if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE)
         dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
     if (mConnectionFlags & nsSocketTransport::DISABLE_IPV6)
         dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV6;
     if (mConnectionFlags & nsSocketTransport::DISABLE_IPV4)
         dnsFlags |= nsIDNSService::RESOLVE_DISABLE_IPV4;
+    if (mConnectionFlags & nsSocketTransport::DISABLE_RFC1918)
+        dnsFlags |= nsIDNSService::RESOLVE_DISABLE_RFC1918;
+    if (mConnectionFlags & nsSocketTransport::DISABLE_LOOPBACK)
+        dnsFlags |= nsIDNSService::RESOLVE_DISABLE_LOOPBACK;
 
     NS_ASSERTION(!(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV6) ||
                  !(dnsFlags & nsIDNSService::RESOLVE_DISABLE_IPV4),
                  "Setting both RESOLVE_DISABLE_IPV6 and RESOLVE_DISABLE_IPV4");
 
     SendStatus(NS_NET_STATUS_RESOLVING_HOST);
     rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nullptr,
                            getter_AddRefs(mDNSRequest));
@@ -1194,35 +1198,40 @@ nsSocketTransport::InitiateSocket()
                            "available via the test networking proxy (if running mochitests) "
                            "or from a test-specific httpd.js server (if running xpcshell tests)."
                            " Browser services should be disabled or redirected to a local server.\n",
                            mHost.get(), ipaddr.get());
             MOZ_CRASH("Attempting to connect to non-local address!");
         }
     }
 
-    // Hosts/Proxy Hosts that are Local IP Literals should not be speculatively
-    // connected - Bug 853423.
-    if (mConnectionFlags & nsISocketTransport::DISABLE_RFC1918 &&
-        IsIPAddrLocal(&mNetAddr)) {
+    // Ensure that we're not using a private IP if such addresses are disabled.
+    bool disableRFC1918 = mConnectionFlags & nsISocketTransport::DISABLE_RFC1918;
+    bool disableLoopback = mConnectionFlags & nsISocketTransport::DISABLE_LOOPBACK;
+
+    if ((disableRFC1918 && IsIPAddrLocal(&mNetAddr)) ||
+        (disableLoopback && IsLoopBackAddress(&mNetAddr))) {
 #ifdef PR_LOGGING
         if (SOCKET_LOG_ENABLED()) {
             nsAutoCString netAddrCString;
             netAddrCString.SetCapacity(kIPv6CStrBufSize);
             if (!NetAddrToString(&mNetAddr,
                                  netAddrCString.BeginWriting(),
                                  kIPv6CStrBufSize))
                 netAddrCString = NS_LITERAL_CSTRING("<IP-to-string failed>");
-            SOCKET_LOG(("nsSocketTransport::InitiateSocket skipping "
-                        "speculative connection for host [%s:%d] proxy "
-                        "[%s:%d] with Local IP address [%s]",
+            SOCKET_LOG(("nsSocketTransport::InitiateSocket refusing to "
+                        "connect to %s host [%s:%d] proxy [%s:%d] with IP "
+                        "address [%s]",
+                        IsIPAddrLocal(&mNetAddr) ? "private" : "loopback",
                         mHost.get(), mPort, mProxyHost.get(), mProxyPort,
                         netAddrCString.get()));
         }
 #endif
+        MOZ_ASSERT(false,
+                   "Local or Loopback IP addresses disabled for this socket!");
         return NS_ERROR_CONNECTION_REFUSED;
     }
 
     //
     // find out if it is going to be ok to attach another socket to the STS.
     // if not then we have to wait for the STS to tell us that it is ok.
     // the notification is asynchronous, which means that when we could be
     // in a race to call AttachSocket once notified.  for this reason, when
@@ -1694,18 +1703,25 @@ nsSocketTransport::OnSocketEvent(uint32_
 
     case MSG_DNS_LOOKUP_COMPLETE:
         if (mDNSRequest)  // only send this if we actually resolved anything
             SendStatus(NS_NET_STATUS_RESOLVED_HOST);
 
         SOCKET_LOG(("  MSG_DNS_LOOKUP_COMPLETE\n"));
         mDNSRequest = 0;
         if (param) {
+            MOZ_ASSERT(NS_SUCCEEDED(status),
+                       "Shouldn't have DNS record if request failed.");
             mDNSRecord = static_cast<nsIDNSRecord *>(param);
-            mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
+            nsresult rv = mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
+            if (NS_FAILED(rv)) {
+                NS_WARNING("Unable to get address after name resolution "
+                           "succeeded.");
+                status = rv;
+            }
         }
         // status contains DNS lookup status
         if (NS_FAILED(status)) {
             // When using a HTTP proxy, NS_ERROR_UNKNOWN_HOST means the HTTP 
             // proxy host is not found, so we fixup the error code.
             // For SOCKS proxies (mProxyTransparent == true), the socket 
             // transport resolves the real host here, so there's no fixup 
             // (see bug 226943).
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -452,16 +452,26 @@
 #define NS_NETWORKPREDICTOR_CID \
 { /* {969adfdf-7221-4419-aecf-05f8faf00c9b} */ \
     0x969adfdf, \
     0x7221, \
     0x4419, \
     { 0xae, 0xcf, 0x05, 0xf8, 0xfa, 0xf0, 0x0c, 0x9b } \
 }
 
+// service implementing nsINetworkZonePolicy
+#define NS_NETWORKZONEPOLICY_CONTRACTID "@mozilla.org/network/networkzonepolicy;1"
+#define NS_NETWORKZONEPOLICY_CID \
+{ /* {afcabf86-d401-41f0-a511-7a444ce31c71} */ \
+    0xafcabf86, \
+    0xd401, \
+    0x41f0, \
+    { 0xa5, 0x11, 0x7a, 0x44, 0x4c, 0xe3, 0x1c, 0x71 } \
+}
+
 /******************************************************************************
  * netwerk/cache/ classes
  */
 
 // service implementing nsICacheService.
 #define NS_CACHESERVICE_CONTRACTID \
     "@mozilla.org/network/cache-service;1"
 #define NS_CACHESERVICE_CID                          \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -33,16 +33,17 @@
 #include "nsMimeTypes.h"
 #include "nsNetStrings.h"
 #include "nsDNSPrefetch.h"
 #include "nsAboutProtocolHandler.h"
 #include "nsXULAppAPI.h"
 #include "nsCategoryCache.h"
 #include "nsIContentSniffer.h"
 #include "Predictor.h"
+#include "nsNetworkZonePolicy.h"
 #include "nsNetUtil.h"
 #include "nsIThreadPool.h"
 #include "mozilla/net/NeckoChild.h"
 
 #include "nsNetCID.h"
 
 #ifndef XP_MACOSX
 #define BUILD_BINHEX_DECODER 1
@@ -441,16 +442,20 @@ static const mozilla::Module::CategoryEn
     NS_BINARYDETECTOR_CATEGORYENTRY,
     { nullptr }
 };
 
 #ifdef BUILD_BINHEX_DECODER
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBinHexDecoder)
 #endif
 
+typedef mozilla::net::nsNetworkZonePolicy nsNetworkZonePolicy;
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsNetworkZonePolicy,
+                                        nsNetworkZonePolicy::GetSingleton)
+
 static nsresult
 CreateNewStreamConvServiceFactory(nsISupports* aOuter, REFNSIID aIID, void **aResult) 
 {
     if (!aResult) {                                                  
         return NS_ERROR_INVALID_POINTER;                             
     }
     if (aOuter) {                                                    
         *aResult = nullptr;                                           
@@ -793,16 +798,17 @@ NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERV
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #elif defined(MOZ_WIDGET_ANDROID)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_SERIALIZATION_HELPER_CID);
 NS_DEFINE_NAMED_CID(NS_REDIRECTCHANNELREGISTRAR_CID);
 NS_DEFINE_NAMED_CID(NS_CACHE_STORAGE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_NETWORKPREDICTOR_CID);
+NS_DEFINE_NAMED_CID(NS_NETWORKZONEPOLICY_CID);
 
 static const mozilla::Module::CIDEntry kNeckoCIDs[] = {
     { &kNS_IOSERVICE_CID, false, nullptr, nsIOServiceConstructor },
     { &kNS_STREAMTRANSPORTSERVICE_CID, false, nullptr, nsStreamTransportServiceConstructor },
     { &kNS_SOCKETTRANSPORTSERVICE_CID, false, nullptr, nsSocketTransportServiceConstructor },
     { &kNS_SERVERSOCKET_CID, false, nullptr, nsServerSocketConstructor },
     { &kNS_UDPSOCKET_CID, false, nullptr, nsUDPSocketConstructor },
     { &kNS_SOCKETPROVIDERSERVICE_CID, false, nullptr, nsSocketProviderService::Create },
@@ -936,16 +942,17 @@ static const mozilla::Module::CIDEntry k
     { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsQtNetworkLinkServiceConstructor },
 #elif defined(MOZ_WIDGET_ANDROID)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsAndroidNetworkLinkServiceConstructor },
 #endif
     { &kNS_SERIALIZATION_HELPER_CID, false, nullptr, nsSerializationHelperConstructor },
     { &kNS_REDIRECTCHANNELREGISTRAR_CID, false, nullptr, RedirectChannelRegistrarConstructor },
     { &kNS_CACHE_STORAGE_SERVICE_CID, false, nullptr, CacheStorageServiceConstructor },
     { &kNS_NETWORKPREDICTOR_CID, false, nullptr, mozilla::net::Predictor::Create },
+    { &kNS_NETWORKZONEPOLICY_CID, false, nullptr, nsNetworkZonePolicyConstructor },
     { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kNeckoContracts[] = {
     { NS_IOSERVICE_CONTRACTID, &kNS_IOSERVICE_CID },
     { NS_NETUTIL_CONTRACTID, &kNS_IOSERVICE_CID },
     { NS_STREAMTRANSPORTSERVICE_CONTRACTID, &kNS_STREAMTRANSPORTSERVICE_CID },
     { NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &kNS_SOCKETTRANSPORTSERVICE_CID },
@@ -1083,16 +1090,17 @@ static const mozilla::Module::ContractID
 #elif defined(MOZ_WIDGET_ANDROID)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
 #endif
     { NS_SERIALIZATION_HELPER_CONTRACTID, &kNS_SERIALIZATION_HELPER_CID },
     { NS_REDIRECTCHANNELREGISTRAR_CONTRACTID, &kNS_REDIRECTCHANNELREGISTRAR_CID },
     { NS_CACHE_STORAGE_SERVICE_CONTRACTID, &kNS_CACHE_STORAGE_SERVICE_CID },
     { NS_CACHE_STORAGE_SERVICE_CONTRACTID2, &kNS_CACHE_STORAGE_SERVICE_CID },
     { NS_NETWORKPREDICTOR_CONTRACTID, &kNS_NETWORKPREDICTOR_CID },
+    { NS_NETWORKZONEPOLICY_CONTRACTID, &kNS_NETWORKZONEPOLICY_CID },
     { nullptr }
 };
 
 static const mozilla::Module kNeckoModule = {
     mozilla::Module::kVersion,
     kNeckoCIDs,
     kNeckoContracts,
     kNeckoCategories,
--- a/netwerk/cache/nsDiskCache.h
+++ b/netwerk/cache/nsDiskCache.h
@@ -6,17 +6,17 @@
 
 
 #ifndef _nsDiskCache_h_
 #define _nsDiskCache_h_
 
 #include "nsCacheEntry.h"
 
 #ifdef XP_WIN
-#include <winsock.h>  // for htonl/ntohl
+#include <winsock2.h>  // for htonl/ntohl
 #endif
 
 
 class nsDiskCache {
 public:
     enum {
             kCurrentVersion = 0x00010013      // format = 16 bits major version/16 bits minor version
     };
--- a/netwerk/dns/DNS.cpp
+++ b/netwerk/dns/DNS.cpp
@@ -199,16 +199,21 @@ bool IsIPAddrLocal(const NetAddr *addr)
         addr16 >> 6 == 0xfe80 >> 6) { // fe80::/10 Link Local Address.
       return true;
     }
   }
   // Not an IPv4/6 local address.
   return false;
 }
 
+bool IsIPAddrPrivate(const NetAddr *addr)
+{
+  return IsIPAddrLocal(addr) || IsLoopBackAddress(addr);
+}
+
 bool
 NetAddr::operator == (const NetAddr& other) const
 {
   if (this->raw.family != other.raw.family) {
     return false;
   } else if (this->raw.family == AF_INET) {
     return (this->inet.port == other.inet.port) &&
            (this->inet.ip == other.inet.ip);
@@ -222,17 +227,29 @@ NetAddr::operator == (const NetAddr& oth
   } else if (this->raw.family == AF_LOCAL) {
     return PL_strncmp(this->local.path, other.local.path,
                       ArrayLength(this->local.path));
 #endif
   }
   return false;
 }
 
-
+bool
+NetAddr::EqualsIP(const NetAddr& other) const
+{
+  if (this->raw.family != other.raw.family) {
+    return false;
+  } else if (this->raw.family == AF_INET) {
+    return (this->inet.ip == other.inet.ip);
+  } else if (this->raw.family == AF_INET6) {
+    return (memcmp(&this->inet6.ip, &other.inet6.ip,
+                   sizeof(this->inet6.ip)) == 0);
+  }
+  return false;
+}
 
 NetAddrElement::NetAddrElement(const PRNetAddr *prNetAddr)
 {
   PRNetAddrToNetAddr(prNetAddr, &mAddress);
 }
 
 NetAddrElement::NetAddrElement(const NetAddrElement& netAddr)
 {
--- a/netwerk/dns/DNS.h
+++ b/netwerk/dns/DNS.h
@@ -106,16 +106,18 @@ union NetAddr {
 #if defined(XP_UNIX)
   struct {                          /* Unix domain socket address */
     uint16_t family;                /* address family (AF_UNIX) */
     char path[104];                 /* null-terminated pathname */
   } local;
 #endif
   // introduced to support nsTArray<NetAddr> (for DNSRequestParent.cpp)
   bool operator == (const NetAddr& other) const;
+  // Compares ip fields only; excludes port and other data; IPv4 and v6 only.
+  bool EqualsIP(const NetAddr& other) const;
 };
 
 // This class wraps a NetAddr union to provide C++ linked list
 // capabilities and other methods. It is created from a PRNetAddr,
 // which is converted to a mozilla::dns::NetAddr.
 class NetAddrElement : public LinkedListElement<NetAddrElement> {
 public:
   NetAddrElement(const PRNetAddr *prNetAddr);
@@ -159,14 +161,16 @@ void NetAddrToPRNetAddr(const NetAddr *a
 bool NetAddrToString(const NetAddr *addr, char *buf, uint32_t bufSize);
 
 bool IsLoopBackAddress(const NetAddr *addr);
 
 bool IsIPAddrAny(const NetAddr *addr);
 
 bool IsIPAddrV4Mapped(const NetAddr *addr);
 
+bool IsIPAddrPrivate(const NetAddr *addr);
+
 bool IsIPAddrLocal(const NetAddr *addr);
 
 } // namespace net
 } // namespace mozilla
 
 #endif // DNS_h_
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -56,31 +56,58 @@ class nsDNSRecord : public nsIDNSRecord
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDNSRECORD
 
     nsDNSRecord(nsHostRecord *hostRecord)
         : mHostRecord(hostRecord)
         , mIter(nullptr)
         , mIterGenCnt(-1)
+        , mHideLocalIPAddresses(false)
+        , mHideLoopbackIPAddresses(false)
         , mDone(false) {}
 
+    // Do not return private, RFC1918-like IP addresses.
+    void HideLocalIPAddresses();
+
+    // Do not return loopback addresses.
+    void HideLoopbackIPAddresses();
+
+    // Convenience function for nsIDNSRecord.hasMore().
+    bool HasMore();
+
 private:
     virtual ~nsDNSRecord() {}
 
     nsRefPtr<nsHostRecord>  mHostRecord;
     NetAddrElement         *mIter;
     int                     mIterGenCnt; // the generation count of
                                          // mHostRecord->addr_info when we
                                          // start iterating
+    // True if private, RFC1918-like IP addresses should be hidden.
+    bool                    mHideLocalIPAddresses;
+    // True if loopback addresses should be hidden.
+    bool                    mHideLoopbackIPAddresses;
     bool                    mDone;
 };
 
 NS_IMPL_ISUPPORTS(nsDNSRecord, nsIDNSRecord)
 
+void
+nsDNSRecord::HideLocalIPAddresses()
+{
+    mHideLocalIPAddresses = true;
+}
+
+void
+nsDNSRecord::HideLoopbackIPAddresses()
+{
+    mHideLoopbackIPAddresses = true;
+}
+
 NS_IMETHODIMP
 nsDNSRecord::GetCanonicalName(nsACString &result)
 {
     // this method should only be called if we have a CNAME
     NS_ENSURE_TRUE(mHostRecord->flags & nsHostResolver::RES_CANON_NAME,
                    NS_ERROR_NOT_AVAILABLE);
 
     // if the record is for an IP address literal, then the canonical
@@ -117,42 +144,55 @@ nsDNSRecord::GetNextAddr(uint16_t port, 
         bool startedFresh = !mIter;
 
         do {
             if (!mIter) {
                 mIter = mHostRecord->addr_info->mAddresses.getFirst();
             } else {
                 mIter = mIter->getNext();
             }
-        }
-        while (mIter && mHostRecord->Blacklisted(&mIter->mAddress));
+        } while (mIter && (mHostRecord->Blacklisted(&mIter->mAddress) ||
+                           (mHideLocalIPAddresses &&
+                            IsIPAddrLocal(&mIter->mAddress)) ||
+                           (mHideLoopbackIPAddresses &&
+                            IsLoopBackAddress(&mIter->mAddress))));
 
         if (!mIter && startedFresh) {
             // If everything was blacklisted we want to reset the blacklist (and
             // likely relearn it) and return the first address. That is better
             // than nothing.
             mHostRecord->ResetBlacklist();
             mIter = mHostRecord->addr_info->mAddresses.getFirst();
+
+            // If Private IPs are hidden, return the first public address.
+            while (mIter && ((mHideLocalIPAddresses &&
+                             IsIPAddrLocal(&mIter->mAddress)) ||
+                            (mHideLoopbackIPAddresses &&
+                             IsLoopBackAddress(&mIter->mAddress)))) {
+                mIter = mIter->getNext();
+            }
         }
 
         if (mIter) {
             memcpy(addr, &mIter->mAddress, sizeof(NetAddr));
         }
 
         mHostRecord->addr_info_lock.Unlock();
 
         if (!mIter) {
             mDone = true;
             return NS_ERROR_NOT_AVAILABLE;
         }
     }
     else {
         mHostRecord->addr_info_lock.Unlock();
 
-        if (!mHostRecord->addr) {
+        if (!mHostRecord->addr ||
+            (mHideLocalIPAddresses && IsIPAddrLocal(mHostRecord->addr)) ||
+            (mHideLoopbackIPAddresses && IsLoopBackAddress(mHostRecord->addr))) {
             // Both mHostRecord->addr_info and mHostRecord->addr are null.
             // This can happen if mHostRecord->addr_info expired and the
             // attempt to reresolve it failed.
             return NS_ERROR_NOT_AVAILABLE;
         }
         memcpy(addr, mHostRecord->addr, sizeof(NetAddr));
         mDone = true;
     }
@@ -211,16 +251,25 @@ nsDNSRecord::HasMore(bool *result)
     *result = NS_SUCCEEDED(GetNextAddr(0, &addr));
 
     mIter = iterCopy;
     mDone = false;
 
     return NS_OK;
 }
 
+bool
+nsDNSRecord::HasMore()
+{
+    bool more;
+    DebugOnly<nsresult> rv = HasMore(&more);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    return more;
+}
+
 NS_IMETHODIMP
 nsDNSRecord::Rewind()
 {
     mIter = nullptr;
     mIterGenCnt = -1;
     mDone = false;
     return NS_OK;
 }
@@ -288,19 +337,28 @@ nsDNSAsyncRequest::OnLookupComplete(nsHo
                                     nsresult        status)
 {
     // need to have an owning ref when we issue the callback to enable
     // the caller to be able to addref/release multiple times without
     // destroying the record prematurely.
     nsCOMPtr<nsIDNSRecord> rec;
     if (NS_SUCCEEDED(status)) {
         NS_ASSERTION(hostRecord, "no host record");
-        rec = new nsDNSRecord(hostRecord);
-        if (!rec)
-            status = NS_ERROR_OUT_OF_MEMORY;
+        nsRefPtr<nsDNSRecord> recImpl = new nsDNSRecord(hostRecord);
+        if (mFlags & nsIDNSService::RESOLVE_DISABLE_RFC1918) {
+            recImpl->HideLocalIPAddresses();
+        }
+        if (mFlags & nsIDNSService::RESOLVE_DISABLE_LOOPBACK) {
+            recImpl->HideLoopbackIPAddresses();
+        }
+        if (!recImpl->HasMore()) {
+            status = NS_ERROR_UNKNOWN_HOST;
+            recImpl = nullptr;
+        }
+        rec = recImpl.forget();
     }
 
     MOZ_EVENT_TRACER_DONE(this, "net::dns::lookup");
 
     mListener->OnLookupComplete(this, rec, status);
     mListener = nullptr;
 
     // release the reference to ourselves that was added before we were
@@ -832,21 +890,28 @@ nsDNSService::Resolve(const nsACString &
         // wait for result
         while (!syncReq.mDone)
             PR_Wait(mon, PR_INTERVAL_NO_TIMEOUT);
 
         if (NS_FAILED(syncReq.mStatus))
             rv = syncReq.mStatus;
         else {
             NS_ASSERTION(syncReq.mHostRecord, "no host record");
-            nsDNSRecord *rec = new nsDNSRecord(syncReq.mHostRecord);
-            if (!rec)
-                rv = NS_ERROR_OUT_OF_MEMORY;
-            else
-                NS_ADDREF(*result = rec);
+            nsRefPtr<nsDNSRecord> rec = new nsDNSRecord(syncReq.mHostRecord);
+            if (flags & nsIDNSService::RESOLVE_DISABLE_RFC1918) {
+              rec->HideLocalIPAddresses();
+            }
+            if (flags & nsIDNSService::RESOLVE_DISABLE_LOOPBACK) {
+              rec->HideLoopbackIPAddresses();
+            }
+            if (!rec->HasMore()) {
+                rv = NS_ERROR_UNKNOWN_HOST;
+                rec = nullptr;
+            }
+            rec.forget(result);
         }
     }
 
     PR_ExitMonitor(mon);
     PR_DestroyMonitor(mon);
     return rv;
 }
 
--- a/netwerk/dns/nsIDNSService.idl
+++ b/netwerk/dns/nsIDNSService.idl
@@ -133,9 +133,21 @@ interface nsIDNSService : nsISupports
      * asyncResolve.
      */
     const unsigned long RESOLVE_OFFLINE = (1 << 6);
 
     /**
      * If set, only IPv6 addresses will be returned from resolve/asyncResolve.
      */
     const unsigned long RESOLVE_DISABLE_IPV4 = (1 << 7);
+
+    /**
+     * If set, local (RFC1918) addresses will NOT be returned from
+     * resolve/asyncResolve.
+     */
+    const unsigned long RESOLVE_DISABLE_RFC1918 = (1 << 8);
+
+    /**
+     * If set, loopback addresses will NOT be returned from
+     * resolve/asyncResolve.
+     */
+    const unsigned long RESOLVE_DISABLE_LOOPBACK = (1 << 9);
 };
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -71,17 +71,19 @@ HttpBaseChannel::HttpBaseChannel()
   , mHttpHandler(gHttpHandler)
   , mRedirectCount(0)
   , mForcePending(false)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // Subfields of unions cannot be targeted in an initializer list
   mSelfAddr.raw.family = PR_AF_UNSPEC;
+  memset(&mSelfAddr, 0, sizeof(mSelfAddr.raw.data));
   mPeerAddr.raw.family = PR_AF_UNSPEC;
+  memset(&mPeerAddr, 0, sizeof(mPeerAddr.raw.data));
 }
 
 HttpBaseChannel::~HttpBaseChannel()
 {
   LOG(("Destroying HttpBaseChannel @%x\n", this));
 
   // Make sure we don't leak
   CleanRedirectCacheChainIfNecessary();
--- a/netwerk/protocol/http/nsAHttpConnection.h
+++ b/netwerk/protocol/http/nsAHttpConnection.h
@@ -135,16 +135,19 @@ public:
 
     // The number of transaction bytes written out on this HTTP Connection, does
     // not count CONNECT tunnel setup
     virtual int64_t BytesWritten() = 0;
 
     // Update the callbacks used to provide security info. May be called on
     // any thread.
     virtual void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks) = 0;
+
+    // Returns true if the socket peer has a private (RFC1918-like) address.
+    virtual bool PeerHasPrivateIP() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpConnection, NS_AHTTPCONNECTION_IID)
 
 #define NS_DECL_NSAHTTPCONNECTION(fwdObject)                    \
     nsresult OnHeadersAvailable(nsAHttpTransaction *, nsHttpRequestHead *, nsHttpResponseHead *, bool *reset); \
     void CloseTransaction(nsAHttpTransaction *, nsresult); \
     nsresult TakeTransport(nsISocketTransport **,    \
@@ -225,13 +228,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpCon
         return (fwdObject)->Classify(newclass);             \
     }                                                       \
     int64_t BytesWritten()                                  \
     {     return fwdObject ? (fwdObject)->BytesWritten() : 0; } \
     void SetSecurityCallbacks(nsIInterfaceRequestor* aCallbacks) \
     {                                                       \
         if (fwdObject)                                      \
             (fwdObject)->SetSecurityCallbacks(aCallbacks);  \
+    }                                                       \
+    bool PeerHasPrivateIP()                                 \
+    { \
+        return fwdObject ? (fwdObject)->PeerHasPrivateIP() : false; \
     }
 
 }} // namespace mozilla::net
 
 #endif // nsAHttpConnection_h__
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -78,16 +78,19 @@ typedef uint8_t nsHttpVersion;
 // a transaction with this flag loads without respect to whether the load
 // group is currently blocking on some resources
 #define NS_HTTP_LOAD_UNBLOCKED       (1<<8)
 
 // These flags allow a transaction to use TLS false start with
 // weaker security profiles based on past history
 #define NS_HTTP_ALLOW_RSA_FALSESTART (1<<9)
 
+// Allows a transaction to use a connection to a private, RFC1918-like address.
+#define NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES (1<<10)
+
 //-----------------------------------------------------------------------------
 // some default values
 //-----------------------------------------------------------------------------
 
 #define NS_HTTP_DEFAULT_PORT  80
 #define NS_HTTPS_DEFAULT_PORT 443
 
 #define NS_HTTP_HEADER_SEPS ", \t"
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -15,16 +15,17 @@
 #include "nsICacheStorageService.h"
 #include "nsICacheStorage.h"
 #include "nsICacheEntry.h"
 #include "nsICryptoHash.h"
 #include "nsIStringBundle.h"
 #include "nsIStreamListenerTee.h"
 #include "nsISeekableStream.h"
 #include "nsILoadGroupChild.h"
+#include "nsINetworkZonePolicy.h"
 #include "nsIProtocolProxyService2.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "prprf.h"
 #include "prnetdb.h"
 #include "nsEscape.h"
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
@@ -2702,16 +2703,44 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
         // The cached response does not contain an entity.  We can only reuse
         // the response if the current request is also HEAD.
         if (!mRequestHead.IsHead()) {
             return NS_OK;
         }
     }
     buf.Adopt(0);
 
+    // If the entry was loaded from a private/RFC1918 network, verify that
+    // private loads are allowed for this loadgroup and that the network
+    // link ID matches.
+    rv = entry->GetMetaDataElement("loaded-from-private-network",
+                                   getter_Copies(buf));
+    if (NS_SUCCEEDED(rv) && !buf.IsEmpty()) {
+        bool privateIPAddrOK = true;
+        nsCString currentNetworkIDString;
+        rv = gIOService->GetNetworkLinkID(currentNetworkIDString);
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+            return rv;
+        }
+        if (!buf.Equals(currentNetworkIDString)) {
+            LOG(("nsHttpChannel::OnCacheEntryCheck %p private entry "
+                 "does not match network link ID - not wanted.", this));
+            privateIPAddrOK = false;
+        } else {
+            privateIPAddrOK = mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES;
+            LOG(("nsHttpChannel::OnCacheEntryCheck %p private entry %s.",
+                 this, privateIPAddrOK ? "allowed" : "forbidden"));
+        }
+        if (!privateIPAddrOK) {
+            *aResult = ENTRY_NOT_WANTED;
+            return NS_OK;
+        }
+    }
+    buf.Adopt(0);
+
     // We'll need this value in later computations...
     uint32_t lastModifiedTime;
     rv = entry->GetLastModified(&lastModifiedTime);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Determine if this is the first time that this cache entry
     // has been accessed during this session.
     bool fromPreviousSession =
@@ -3118,16 +3147,40 @@ nsHttpChannel::OnNormalCacheEntryAvailab
     if (NS_SUCCEEDED(aEntryStatus)) {
         mCacheEntry = aEntry;
         mCacheEntryIsWriteOnly = aNew;
 
         if (mLoadFlags & LOAD_INITIAL_DOCUMENT_URI) {
             Telemetry::Accumulate(Telemetry::HTTP_OFFLINE_CACHE_DOCUMENT_LOAD,
                                   false);
         }
+
+        // For cached doument loads, we must set Private/Public Network load
+        // permissions for the document's sub-resources.
+        // -- Forbid private loads if the doc was loaded on a public network.
+        // -- Allow private loads if the doc was loaded from a private IP.
+        // Note: Private docs should only be loaded from the same network.
+        //       This check should already have been done in OnCacheEntryCheck.
+        if (mNZP && !aNew && mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
+            nsXPIDLCString buf;
+            nsresult rv =
+                mCacheEntry->GetMetaDataElement("loaded-from-private-network",
+                                                getter_Copies(buf));
+            bool privateIPAddrOK = NS_SUCCEEDED(rv) && !buf.IsEmpty();
+
+            LOG(("nsHttpChannel::OnNormalCacheEntryAvailable %p document "
+                 "load: %s sub-resource loads from private networks.",
+                 this, privateIPAddrOK ? "allows" : "forbids"));
+
+            rv = mNZP->SetPrivateNetworkPermission(this, privateIPAddrOK);
+            if (NS_FAILED(rv)) {
+                LOG(("nsHttpChannel::OnNormalCacheEntryAvailable %p failed "
+                     "SetPrivateNetworkPermission rv=0x%x", this, rv));
+            }
+        }
     }
 
     return NS_OK;
 }
 
 nsresult
 nsHttpChannel::OnOfflineCacheEntryAvailable(nsICacheEntry *aEntry,
                                             bool aNew,
@@ -3862,16 +3915,33 @@ nsHttpChannel::AddCacheEntryHeaders(nsIC
 
     // Store the received HTTP head with the cache entry as an element of
     // the meta data.
     nsAutoCString head;
     mResponseHead->Flatten(head, true);
     rv = entry->SetMetaDataElement("response-head", head.get());
     if (NS_FAILED(rv)) return rv;
 
+    // If the response was loaded from a private/RFC1918 network, store
+    // the networkLinkID in a metadata header.
+    if (IsIPAddrPrivate(&mPeerAddr)) {
+        char privateNetworkIDString[21];
+        PR_snprintf(privateNetworkIDString, sizeof(privateNetworkIDString),
+                    "%llu", mPrivateNetworkID);
+        MOZ_ASSERT(strlen(privateNetworkIDString) > 0);
+
+        LOG(("nsHttpChannel::AddCacheEntryHeaders %p setting loaded-from-"
+             "private-network=%s", this, privateNetworkIDString));
+        rv = entry->SetMetaDataElement("loaded-from-private-network",
+                                       privateNetworkIDString);
+        if (NS_FAILED(rv)) {
+            return rv;
+        }
+    }
+
     // Indicate we have successfully finished setting metadata on the cache entry.
     rv = entry->MetaDataReady();
 
     return rv;
 }
 
 inline void
 GetAuthType(const char *challenge, nsCString &authType)
@@ -4468,18 +4538,42 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     mIsPending = true;
     mWasOpened = true;
 
     mListener = listener;
     mListenerContext = context;
 
     // add ourselves to the load group.  from this point forward, we'll report
     // all failures asynchronously.
-    if (mLoadGroup)
+    if (mLoadGroup) {
         mLoadGroup->AddRequest(this, nullptr);
+    }
+
+    // Check if the channel is allowed to load from private addresses.
+    mNZP = do_GetService(NS_NETWORKZONEPOLICY_CONTRACTID, &rv);
+    if (NS_SUCCEEDED(rv) && mNZP) {
+        bool privateIPAddrOK = false;
+        rv = mNZP->CheckPrivateNetworkPermission(this, &privateIPAddrOK);
+        if (NS_SUCCEEDED(rv) && privateIPAddrOK) {
+            mCaps |= NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES;
+        } else if (NS_FAILED(rv)) {
+            LOG(("nsHttpChannel::AsyncOpen %p CheckPrivateNetworkPermission "
+                 "failed with rv=0x%x", this, rv));
+        }
+#ifdef PR_LOGGING
+        nsAutoCString host;
+        rv = mURI->GetAsciiHost(host);
+        LOG(("nsHttpChannel::AsyncOpen %p private addresses %s for "
+             "%s", this, privateIPAddrOK ? "allowed" : "forbidden",
+             host.get()));
+#endif
+    } else {
+        LOG(("nsHttpChannel::AsyncOpen %p No NetworkZonePolicy object rv=0x%x",
+             this, rv));
+    }
 
     // record asyncopen time unconditionally and clear it if we
     // don't want it after OnModifyRequest() weighs in. But waiting for
     // that to complete would mean we don't include proxy resolution in the
     // timing.
     mAsyncOpenTime = TimeStamp::Now();
 
     // the only time we would already know the proxy information at this
@@ -5416,18 +5510,55 @@ nsHttpChannel::OnTransportStatus(nsITran
     if (!mProgressSink)
         GetCallback(mProgressSink);
 
     if (status == NS_NET_STATUS_CONNECTED_TO ||
         status == NS_NET_STATUS_WAITING_FOR) {
         nsCOMPtr<nsISocketTransport> socketTransport =
             do_QueryInterface(trans);
         if (socketTransport) {
-            socketTransport->GetSelfAddr(&mSelfAddr);
-            socketTransport->GetPeerAddr(&mPeerAddr);
+            nsresult selfRv = socketTransport->GetSelfAddr(&mSelfAddr);
+            nsresult peerRv = socketTransport->GetPeerAddr(&mPeerAddr);
+
+            // XXX Remove this call to UpdateNetworkLinkID once Bug 939319 and
+            // associated bugs for NS_NETWORK_LINK_DATA_CHANGED are complete on
+            // all supported platforms. For now, update the network link ID with
+            // mSelfAddr, assuming we were able to get it successfully.
+            if (NS_SUCCEEDED(selfRv)) {
+                gIOService->UpdateNetworkLinkID(mSelfAddr);
+            }
+
+            // If the peer is private, store the network link ID now to be
+            // saved in the cache headers later. Also check permissions.
+            bool peerHasPrivateAddr = NS_SUCCEEDED(peerRv) &&
+                                      IsIPAddrPrivate(&mPeerAddr);
+            if (peerHasPrivateAddr) {
+                mPrivateNetworkID = gIOService->GetNetworkLinkID();
+
+                if (!(mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES)) {
+                    LOG(("nsHttpChannel::OnTransportStatus %p not permitted "
+                         "to load from private IP address! Canceling.", this));
+                    return Cancel(NS_ERROR_CONNECTION_REFUSED);
+                }
+            }
+
+            // If this is a document load, set permissions for the rest of the
+            // loadgroup's requests.
+            if (mNZP && NS_SUCCEEDED(peerRv) &&
+                mLoadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
+                LOG(("nsHttpChannel::OnTransportStatus %p document load: "
+                     "%s sub-resource loads from private networks.",
+                     this, peerHasPrivateAddr ? "allows" : "forbids"));
+                nsresult rv =
+                    mNZP->SetPrivateNetworkPermission(this, peerHasPrivateAddr);
+                if (NS_FAILED(rv)) {
+                    LOG(("nsHttpChannel::OnTransportStatus %p failed "
+                         "SetPrivateNetworkPermission rv=0x%x", this, rv));
+                }
+            }
         }
     }
 
     // block socket status event after Cancel or OnStopRequest has been called.
     if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) {
         LOG(("sending status notification [this=%p status=%x progress=%llu/%llu]\n",
             this, status, progress, progressMax));
 
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -14,16 +14,17 @@
 #include "nsICacheEntryOpenCallback.h"
 #include "nsIDNSListener.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsIProtocolProxyCallback.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIThreadRetargetableStreamListener.h"
+#include "nsINetworkZonePolicy.h"
 #include "nsWeakReference.h"
 #include "TimingStruct.h"
 #include "AutoClose.h"
 
 class nsIPrincipal;
 class nsDNSPrefetch;
 class nsICacheEntryDescriptor;
 class nsICancelable;
@@ -417,13 +418,19 @@ private:
     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 
 protected:
     virtual void DoNotifyListenerCleanup();
     nsPerformance* GetPerformance();
 
 private: // cache telemetry
     bool mDidReval;
+
+    // The network link ID generated by nsIOService at the time of connection.
+    uint64_t mPrivateNetworkID;
+
+    // Set during AsyncOpen
+    nsCOMPtr<nsINetworkZonePolicy> mNZP;
 };
 
 } } // namespace mozilla::net
 
 #endif // nsHttpChannel_h__
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -1707,16 +1707,33 @@ nsHttpConnection::OnSocketReadable()
             }
         }
         // read more from the socket until error...
     } while (again);
 
     return rv;
 }
 
+bool
+nsHttpConnection::PeerHasPrivateIP()
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+    if (!mSocketTransport) {
+        return false;
+    }
+
+    NetAddr peerAddr;
+    nsresult rv = mSocketTransport->GetPeerAddr(&peerAddr);
+    if (NS_FAILED(rv)) {
+        return false;
+    }
+
+    return IsIPAddrPrivate(&peerAddr);
+}
+
 void
 nsHttpConnection::SetupSecondaryTLS()
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     MOZ_ASSERT(!mTLSFilter);
     LOG(("nsHttpConnection %p SetupSecondaryTLS %s %d\n",
          this, mConnInfo->Host(), mConnInfo->Port()));
     mTLSFilter = new TLSFilterTransaction(mTransaction,
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -195,16 +195,19 @@ public:
     bool    IsExperienced() { return mExperienced; }
 
     static nsresult MakeConnectString(nsAHttpTransaction *trans,
                                       nsHttpRequestHead *request,
                                       nsACString &result);
     void    SetupSecondaryTLS();
     void    SetInSpdyTunnel(bool arg);
 
+    // Returns true if the socket peer has a private (RFC1918-like) address.
+    bool    PeerHasPrivateIP();
+
 private:
     // Value (set in mTCPKeepaliveConfig) indicates which set of prefs to use.
     enum TCPKeepaliveConfig {
       kTCPKeepaliveDisabled = 0,
       kTCPKeepaliveShortLivedConfig,
       kTCPKeepaliveLongLivedConfig
     };
 
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -2933,20 +2933,30 @@ nsHalfOpenSocket::SetupStreams(nsISocket
     if (mEnt->mPreferIPv6) {
         tmpFlags |= nsISocketTransport::DISABLE_IPV4;
     }
     else if (mEnt->mPreferIPv4 ||
              (isBackup && gHttpHandler->FastFallbackToIPv4())) {
         tmpFlags |= nsISocketTransport::DISABLE_IPV6;
     }
 
-    if (IsSpeculative()) {
+    // Allow speculative connections for loopback so we can run tests.
+    if (IsSpeculative() && gHttpHandler->AllowSpeculativeConnectOnLoopback()) {
         tmpFlags |= nsISocketTransport::DISABLE_RFC1918;
+        LOG(("nsHalfOpenSocket::SetupStreams %p Disable private IPs for "
+             "speculative connections", this));
+    } else if (IsSpeculative() ||
+               !(mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES)) {
+        tmpFlags |= nsISocketTransport::DISABLE_LOOPBACK |
+                    nsISocketTransport::DISABLE_RFC1918;
+        LOG(("nsHalfOpenSocket::SetupStreams %p Disable loopback and private "
+             "IPs", this));
     }
 
+
     socketTransport->SetConnectionFlags(tmpFlags);
 
     socketTransport->SetQoSBits(gHttpHandler->GetQoSBits());
 
     rv = socketTransport->SetEventSink(this, nullptr);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = socketTransport->SetSecurityCallbacks(this);
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -191,16 +191,17 @@ nsHttpHandler::nsHttpHandler()
     , mSpdySendingChunkSize(ASpdySession::kSendingChunkSize)
     , mSpdySendBufferSize(ASpdySession::kTCPSendBufferSize)
     , mSpdyPushAllowance(32768)
     , mSpdyPingThreshold(PR_SecondsToInterval(58))
     , mSpdyPingTimeout(PR_SecondsToInterval(8))
     , mConnectTimeout(90000)
     , mBypassCacheLockThreshold(250.0)
     , mParallelSpeculativeConnectLimit(6)
+    , mAllowSpeculativeConnectOnLoopback(false)
     , mRequestTokenBucketEnabled(true)
     , mRequestTokenBucketMinParallelism(6)
     , mRequestTokenBucketHz(100)
     , mRequestTokenBucketBurst(32)
     , mTCPKeepaliveShortLivedEnabled(false)
     , mTCPKeepaliveShortLivedTimeS(60)
     , mTCPKeepaliveShortLivedIdleTimeS(10)
     , mTCPKeepaliveLongLivedEnabled(false)
@@ -515,38 +516,16 @@ nsHttpHandler::GetCookieService()
 
 nsresult
 nsHttpHandler::GetIOService(nsIIOService** result)
 {
     NS_ADDREF(*result = mIOService);
     return NS_OK;
 }
 
-uint32_t
-nsHttpHandler::Get32BitsOfPseudoRandom()
-{
-    // only confirm rand seeding on socket thread
-    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
-
-    // rand() provides different amounts of PRNG on different platforms.
-    // 15 or 31 bits are common amounts.
-
-    PR_STATIC_ASSERT(RAND_MAX >= 0xfff);
-
-#if RAND_MAX < 0xffffU
-    return ((uint16_t) rand() << 20) |
-            (((uint16_t) rand() & 0xfff) << 8) |
-            ((uint16_t) rand() & 0xff);
-#elif RAND_MAX < 0xffffffffU
-    return ((uint16_t) rand() << 16) | ((uint16_t) rand() & 0xffff);
-#else
-    return (uint32_t) rand();
-#endif
-}
-
 void
 nsHttpHandler::NotifyObservers(nsIHttpChannel *chan, const char *event)
 {
     LOG(("nsHttpHandler::NotifyObservers [chan=%x event=\"%s\"]\n", chan, event));
     if (mObserverService)
         mObserverService->NotifyObservers(chan, event, nullptr);
 }
 
@@ -1251,16 +1230,23 @@ nsHttpHandler::PrefsChanged(nsIPrefBranc
     // The maximum number of current global half open sockets allowable
     // for starting a new speculative connection.
     if (PREF_CHANGED(HTTP_PREF("speculative-parallel-limit"))) {
         rv = prefs->GetIntPref(HTTP_PREF("speculative-parallel-limit"), &val);
         if (NS_SUCCEEDED(rv))
             mParallelSpeculativeConnectLimit = (uint32_t) clamped(val, 0, 1024);
     }
 
+    if (PREF_CHANGED(HTTP_PREF("speculative.allowLoopback"))) {
+        rv = prefs->GetBoolPref(HTTP_PREF("speculative.allowLoopback"), &cVar);
+        if (NS_SUCCEEDED(rv)) {
+            mAllowSpeculativeConnectOnLoopback = cVar;
+        }
+    }
+
     // Whether or not to block requests for non head js/css items (e.g. media)
     // while those elements load.
     if (PREF_CHANGED(HTTP_PREF("rendering-critical-requests-prioritization"))) {
         rv = prefs->GetBoolPref(HTTP_PREF("rendering-critical-requests-prioritization"), &cVar);
         if (NS_SUCCEEDED(rv))
             mCriticalRequestPrioritization = cVar;
     }
 
--- a/netwerk/protocol/http/nsHttpHandler.h
+++ b/netwerk/protocol/http/nsHttpHandler.h
@@ -104,16 +104,17 @@ public:
     uint32_t       SpdySendingChunkSize() { return mSpdySendingChunkSize; }
     uint32_t       SpdySendBufferSize()      { return mSpdySendBufferSize; }
     uint32_t       SpdyPushAllowance()       { return mSpdyPushAllowance; }
     PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
     PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
     bool           AllowPush()   { return mAllowPush; }
     uint32_t       ConnectTimeout()  { return mConnectTimeout; }
     uint32_t       ParallelSpeculativeConnectLimit() { return mParallelSpeculativeConnectLimit; }
+    bool           AllowSpeculativeConnectOnLoopback() { return mAllowSpeculativeConnectOnLoopback; }
     bool           CriticalRequestPrioritization() { return mCriticalRequestPrioritization; }
     double         BypassCacheLockThreshold() { return mBypassCacheLockThreshold; }
 
     uint32_t       MaxConnectionsPerOrigin() { return mMaxPersistentConnectionsPerServer; }
     bool           UseRequestTokenBucket() { return mRequestTokenBucketEnabled; }
     uint16_t       RequestTokenBucketMinParallelism() { return mRequestTokenBucketMinParallelism; }
     uint32_t       RequestTokenBucketHz() { return mRequestTokenBucketHz; }
     uint32_t       RequestTokenBucketBurst() {return mRequestTokenBucketBurst; }
@@ -223,19 +224,16 @@ public:
     // The HTTP handler caches pointers to specific XPCOM services, and
     // provides the following helper routines for accessing those services:
     //
     nsresult GetStreamConverterService(nsIStreamConverterService **);
     nsresult GetIOService(nsIIOService** service);
     nsICookieService * GetCookieService(); // not addrefed
     nsISiteSecurityService * GetSSService();
 
-    // callable from socket thread only
-    uint32_t Get32BitsOfPseudoRandom();
-
     // Called by the channel synchronously during asyncOpen
     void OnOpeningRequest(nsIHttpChannel *chan)
     {
         NotifyObservers(chan, NS_HTTP_ON_OPENING_REQUEST_TOPIC);
     }
 
     // Called by the channel before writing a request
     void OnModifyRequest(nsIHttpChannel *chan)
@@ -477,16 +475,19 @@ private:
     // The maximum amount of time the nsICacheSession lock can be held
     // before a new transaction bypasses the cache. In milliseconds.
     double         mBypassCacheLockThreshold;
 
     // The maximum number of current global half open sockets allowable
     // when starting a new speculative connection.
     uint32_t       mParallelSpeculativeConnectLimit;
 
+    // Allow speculative connections on loopback. Primarily for testing.
+    bool           mAllowSpeculativeConnectOnLoopback;
+
     // For Rate Pacing of HTTP/1 requests through a netwerk/base/src/EventTokenBucket
     // Active requests <= *MinParallelism are not subject to the rate pacing
     bool           mRequestTokenBucketEnabled;
     uint16_t       mRequestTokenBucketMinParallelism;
     uint32_t       mRequestTokenBucketHz;  // EventTokenBucket HZ
     uint32_t       mRequestTokenBucketBurst; // EventTokenBucket Burst
 
     // Whether or not to block requests for non head js/css items (e.g. media)
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -627,16 +627,25 @@ nsHttpTransaction::ReadSegments(nsAHttpS
         return mStatus;
     }
 
     if (!mConnected) {
         mConnected = true;
         mConnection->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
     }
 
+    // Verify permission to load from private (RFC1918-like) addresses.
+    if (!(mCaps & NS_HTTP_ALLOW_PRIVATE_IP_ADDRESSES) &&
+        mConnection->PeerHasPrivateIP()) {
+        LOG(("nsHttpTransaction::ReadSegments %p private IPs forbidden; "
+             "closing transaction.", this));
+        Close(NS_ERROR_CONNECTION_REFUSED);
+        return NS_ERROR_CONNECTION_REFUSED;
+    }
+
     mReader = reader;
 
     nsresult rv = mRequestStream->ReadSegments(ReadRequestSegment, this, count, countRead);
 
     mReader = nullptr;
 
     // if read would block then we need to AsyncWait on the request stream.
     // have callback occur on socket thread so we stay synchronized.
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -2420,21 +2420,26 @@ WebSocketChannel::OnProxyAvailable(nsICa
 
   nsAutoCString type;
   if (NS_SUCCEEDED(status) && pi &&
       NS_SUCCEEDED(pi->GetType(type)) &&
       !type.EqualsLiteral("direct")) {
     LOG(("WebSocket OnProxyAvailable [%p] Proxy found skip DNS lookup\n", this));
     // call DNS callback directly without DNS resolver
     OnLookupComplete(nullptr, nullptr, NS_ERROR_FAILURE);
-    return NS_OK;
+  } else {
+    LOG(("WebSocketChannel::OnProxyAvailable[%] checking DNS resolution\n", this));
+    nsresult rv = DoAdmissionDNS();
+    if (NS_FAILED(rv)) {
+      LOG(("WebSocket OnProxyAvailable [%p] DNS lookup failed\n", this));
+      // call DNS callback directly without DNS resolver
+      OnLookupComplete(nullptr, nullptr, NS_ERROR_FAILURE);
+    }
   }
 
-  LOG(("WebSocketChannel::OnProxyAvailable[%] checking DNS resolution\n", this));
-  DoAdmissionDNS();
   return NS_OK;
 }
 
 // nsIInterfaceRequestor
 
 NS_IMETHODIMP
 WebSocketChannel::GetInterface(const nsIID & iid, void **result)
 {
--- a/netwerk/test/unit/test_httpResponseTimeout.js
+++ b/netwerk/test/unit/test_httpResponseTimeout.js
@@ -38,20 +38,16 @@ TimeoutListener.prototype = {
     } else {
       do_check_eq(status, Cr.NS_ERROR_NET_TIMEOUT);
     }
 
     run_next_test();
   },
 };
 
-function serverStopListener() {
-  do_test_finished();
-}
-
 function testTimeout(timeoutEnabled, expectResponse) {
   // Set timeout pref.
   if (timeoutEnabled) {
     prefService.setIntPref(kResponseTimeoutPref, kResponseTimeout);
   } else {
     prefService.setIntPref(kResponseTimeoutPref, 0);
   }
 
@@ -132,30 +128,33 @@ function setup_tests() {
   for (var i=0; i < tests.length; i++) {
     add_test(tests[i]);
   }
 }
 
 function setup_http_server() {
   // Start server; will be stopped at test cleanup time.
   server.start(-1);
-  baseURL = "http://localhost:" + server.identity.primaryPort + "/";
+  baseURL = server.identity.primaryScheme + "://" +
+            server.identity.primaryHost + ":" +
+            server.identity.primaryPort + "/";
   do_print("Using baseURL: " + baseURL);
+
   server.registerPathHandler('/', function(metadata, response) {
     // Wait until the timeout should have passed, then respond.
     response.processAsync();
 
     do_timeout((kResponseTimeout+1)*1000 /* ms */, function() {
       response.setStatusLine(metadata.httpVersion, 200, "OK");
       response.write("Hello world");
       response.finish();
     });
   });
   do_register_cleanup(function() {
-    server.stop(serverStopListener);
+    server.stop(function() {});
   });
 }
 
 function run_test() {
   setup_http_server();
 
   setup_tests();
 
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_networkZonePolicy.js
@@ -0,0 +1,613 @@
+/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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";
+
+Cu.import("resource://testing-common/httpd.js");
+
+var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+var nzp = Cc["@mozilla.org/network/networkzonepolicy;1"]
+          .createInstance(Ci.nsINetworkZonePolicy);
+// HTTP Server for 'network' requests.
+var httpServ;
+// URI Base for requests.
+var uriBase;
+
+// Listener implements nsIStreamListener.
+//
+// @param expectSuccess  If true, Listener will check for request success.
+//                       If false, Listener will check for failure and ensure
+//                       no onDataAvailable calls are made.
+// @param loadGroupAllows
+//                       Indicates if loadGroup should allow or forbid private
+//                       loads AFTER the response is received. This may be
+//                       changed by the channel based on the type of channel
+//                       load.
+function Listener(expectSuccess, loadGroupAllows) {
+  this._expectSuccess = expectSuccess;
+  this._loadGroupAllows = loadGroupAllows;
+}
+
+Listener.prototype = {
+  _expectSuccess: false,
+  _loadGroupAllows: true,
+  _buffer: null,
+
+  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, ctx) {
+    do_check_true(request instanceof Ci.nsIHttpChannel);
+    if (this._expectSuccess) {
+      do_check_true(Components.isSuccessCode(request.status));
+      do_check_eq(request.requestSucceeded, true);
+      do_check_eq(request.responseStatus, 200);
+      request.visitResponseHeaders({ visitHeader: function(aHeader, aValue) {
+        do_print(aHeader + ": " + aValue);
+      }});
+      do_print(request.responseStatus + ": " + request.responseStatusText);
+      this._buffer = "";
+    } else {
+      do_check_false(Components.isSuccessCode(request.status));
+    }
+  },
+
+  onDataAvailable: function(request, ctx, stream, off, cnt) {
+    do_check_true(request instanceof Ci.nsIHttpChannel);
+    if (!this._expectSuccess) {
+      do_throw("Should not get data; private load forbidden!");
+    }
+    this._buffer = this._buffer.concat(read_stream(stream, cnt));
+  },
+
+  onStopRequest: function(request, ctx, status) {
+    do_check_true(request instanceof Ci.nsIHttpChannel);
+    // Check loadgroup permission has not changed.
+    do_check_eq(request.loadGroup.allowLoadsFromPrivateNetworks,
+                this._loadGroupAllows);
+
+    if (this._expectSuccess) {
+      do_check_true(Components.isSuccessCode(status));
+    } else {
+      do_check_false(Components.isSuccessCode(status));
+    }
+    run_next_test();
+  }
+};
+
+//
+// Test Functions
+//
+
+// Ensure that the pref enables and disables private load restrictions.
+function test_basic_NetworkZonePolicy_pref() {
+  // Create loadgroup and channel for non-doc load.
+  var loadGroup = Cc["@mozilla.org/network/load-group;1"]
+                  .createInstance(Ci.nsILoadGroup);
+  var chan = ios.newChannel("http://localhost/failme/", null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+
+  // Verify that we're starting with the pref enabled. This should have been set
+  // during this scripts setup phase.
+  var prefs = Cc["@mozilla.org/preferences-service;1"]
+              .getService(Ci.nsIPrefBranch);
+  var nzpEnabled = prefs.getBoolPref("network.zonepolicy.enabled");
+  do_check_true(nzpEnabled);
+
+  // Verify defaults
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Set permission for doc load; verify permission changed.
+  chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
+
+  nzp.setPrivateNetworkPermission(chan, false);
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
+
+  // Disable pref; ensure restrictions lifted.
+  prefs.setBoolPref("network.zonepolicy.enabled", false);
+  // Loadgroup permission will still be "forbid private loads".
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
+  // NZP will report that private loads are ok.
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Enable pref again; ensure restrictions are again in play.
+  prefs.setBoolPref("network.zonepolicy.enabled", true);
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
+
+  // Leaving pref on for the remainder of the tests.
+
+  run_next_test();
+}
+
+// Ensure that NetworkZonePolicy can manage permissions for a channel's
+// loadgroup; no loadgroup ancestors.
+function test_basic_NetworkZonePolicy_and_loadGroup() {
+  // Create loadgroup and channel for non-doc load.
+  var loadGroup = Cc["@mozilla.org/network/load-group;1"]
+                  .createInstance(Ci.nsILoadGroup);
+  var chan = ios.newChannel("http://localhost/failme/", null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+
+  // Verify defaults
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Set permission for non-doc load; verify no changes.
+  nzp.setPrivateNetworkPermission(chan, false);
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Set permission for doc load; verify permission changed.
+  chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
+
+  nzp.setPrivateNetworkPermission(chan, false);
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
+
+  run_next_test();
+}
+
+// Ensure that NetworkZonePolicy can manage permissions for a channel's
+// loadgroup and one of its ancestors. Ancestor is specified by calling
+// function.
+function test_loadGroup_and_ancestor(loadGroup, ancestor, chan) {
+  // Verify permission defaults
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Set permission for non-doc load; verify no changes.
+  nzp.setPrivateNetworkPermission(chan, false);
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Set permission for doc load; verify permission changed for loadgroup, but
+  // not for ancestor loadgroup.
+  chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
+
+  nzp.setPrivateNetworkPermission(chan, false);
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, false);
+  do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
+
+  // Verify we can set permission allowed again.
+  nzp.setPrivateNetworkPermission(chan, true);
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(ancestor.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Set ancestor to forbid private loads; verify chan permission forbidden.
+  ancestor.allowLoadsFromPrivateNetworks = false;
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
+  // ... loadgroup's own persmission should still be allow.
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  // ... nzp should not be able to set permission to true.
+  nzp.setPrivateNetworkPermission(chan, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
+
+  // Reset ancestor and verify.
+  ancestor.allowLoadsFromPrivateNetworks = true;
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+  nzp.setPrivateNetworkPermission(chan, false);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), false);
+}
+
+// Ensure that NetworkZonePolicy can manage permissions for a channel's
+// loadgroup; loadgroup has a parent loadgroup.
+function test_basic_NetworkZonePolicy_loadGroup_and_parent() {
+  // Create loadgroup, parent loadgroup and channel for non-doc load.
+  var loadGroup = Cc["@mozilla.org/network/load-group;1"]
+                  .createInstance(Ci.nsILoadGroup);
+  var loadGroupAsChild = loadGroup.QueryInterface(Ci.nsILoadGroupChild)
+  var chan = ios.newChannel("http://localhost/failme/", null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+
+  var parent = Cc["@mozilla.org/network/load-group;1"]
+               .createInstance(Ci.nsILoadGroup);
+  loadGroupAsChild.parentLoadGroup = parent;
+  do_check_eq(parent, loadGroupAsChild.parentLoadGroup);
+
+  test_loadGroup_and_ancestor(loadGroup, parent, chan);
+
+  run_next_test();
+}
+
+// Ensure that NetworkZonePolicy can manage permissions for a channel's
+// loadgroup; loadgroup is member of another loadgroup.
+function test_basic_NetworkZonePolicy_loadGroup_and_owner() {
+  // Create loadgroup, parent loadgroup and channel for non-doc load.
+  var loadGroup = Cc["@mozilla.org/network/load-group;1"]
+                  .createInstance(Ci.nsILoadGroup);
+  var chan = ios.newChannel("http://localhost/failme/", null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+
+  var owner = Cc["@mozilla.org/network/load-group;1"]
+              .createInstance(Ci.nsILoadGroup);
+  loadGroup.loadGroup = owner;
+  do_check_eq(owner, loadGroup.loadGroup);
+
+  test_loadGroup_and_ancestor(loadGroup, owner, chan);
+
+  run_next_test();
+}
+
+// Ensure that NetworkZonePolicy can manage permissions for a channel's
+// loadgroup; loadgroup is a docshell loadgroup that has a parent docshell.
+function test_basic_NetworkZonePolicy_loadGroup_and_docshell() {
+  // Create docshell and docshell parent, and get their loadgroups.
+  var docShell = Cc["@mozilla.org/docshell;1"].createInstance(Ci.nsIDocShell);
+
+  var docShellParent = Cc["@mozilla.org/docshell;1"]
+                       .createInstance(Ci.nsIDocShell);
+  docShellParent.addChild(docShell);
+
+  var loadGroup = docShell.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
+  var dsParent = docShellParent.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
+
+  // Create a channel for non-doc load.
+  var chan = ios.newChannel("http://localhost/failme/", null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+
+  test_loadGroup_and_ancestor(loadGroup, dsParent, chan);
+
+  run_next_test();
+}
+
+// Ensure that a loadgroup's immediate ancestors dictate its private load
+// permissions.
+function test_loadGroup_immediate_ancestors() {
+  // Create docshell and docshell parent, and get their loadgroups.
+  var docShell = Cc["@mozilla.org/docshell;1"].createInstance(Ci.nsIDocShell);
+  var docShellParent = Cc["@mozilla.org/docshell;1"]
+                       .createInstance(Ci.nsIDocShell);
+  docShellParent.addChild(docShell);
+
+  var loadGroup = docShell.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
+  var dsParent = docShellParent.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
+
+  // Add owning loadgroup.
+  var owner = Cc["@mozilla.org/network/load-group;1"]
+              .createInstance(Ci.nsILoadGroup);
+  loadGroup.loadGroup = owner;
+  do_check_eq(owner, loadGroup.loadGroup);
+
+  // Add parent loadgroup.
+  var loadGroupAsChild = loadGroup.QueryInterface(Ci.nsILoadGroupChild)
+  var parent = Cc["@mozilla.org/network/load-group;1"]
+               .createInstance(Ci.nsILoadGroup);
+  loadGroupAsChild.parentLoadGroup = parent;
+  do_check_eq(parent, loadGroupAsChild.parentLoadGroup);
+
+  // Create a channel for non-doc load.
+  var chan = ios.newChannel("http://localhost/failme/", null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+
+  // Verify permission defaults
+  do_check_eq(loadGroup.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(dsParent.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(owner.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(parent.allowLoadsFromPrivateNetworks, true);
+  do_check_eq(nzp.checkPrivateNetworkPermission(chan), true);
+
+  // Set ancestors to forbid.
+  for (var i = 0; i < 8; i++) {
+    dsParent.allowLoadsFromPrivateNetworks = !!(i & 1);
+    owner.allowLoadsFromPrivateNetworks = !!(i & 2);
+    parent.allowLoadsFromPrivateNetworks = !!(i & 4);
+    // Permission allowed only when all ancestors allow private loads.
+    do_check_eq(nzp.checkPrivateNetworkPermission(chan), (i == 7));
+  }
+
+  run_next_test();
+}
+
+// Checks a channel load based on its loadgroup private load permissions.
+//
+// @param allowPrivateLoads  Indicates if private loads should be allowed on
+//                           the channel's loadgroup or not.
+// @param expectSuccessfulResponse
+//                           Indicates if the nsIStreamListener for the channel
+//                           load should expect a response with success or
+//                           failure.
+// @param urlStr             The url that should be loaded by the channel.
+//
+function test_single_loadGroup(allowPrivateLoads,
+                               expectSuccessfulResponse,
+                               urlStr) {
+  // Create loadgroup and channel for non-doc load.
+  var loadGroup = Cc["@mozilla.org/network/load-group;1"]
+                  .createInstance(Ci.nsILoadGroup);
+  var chan = ios.newChannel(urlStr, null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+
+  do_print("Setting loadgroup permission: " +
+           (allowPrivateLoads ? "Allowing" : "Forbidding") +
+           " private loads for " + urlStr + ".");
+  loadGroup.allowLoadsFromPrivateNetworks = allowPrivateLoads;
+
+  // Try to load channel with IP literal. For non-doc loads like this, load
+  // group permissions should not change after the response is received.
+  var listener = new Listener(expectSuccessfulResponse, allowPrivateLoads);
+  chan.asyncOpen(listener, null);
+}
+
+// Same as test_single_loadGroup but for a document load.
+function test_single_loadGroup_doc_load(allowPrivateLoads,
+                                        expectSuccessfulResponse,
+                                        loadGroupAllowsAfter,
+                                        urlStr) {
+  // Create loadgroup and channel for document load.
+  var loadGroup = Cc["@mozilla.org/network/load-group;1"]
+                  .createInstance(Ci.nsILoadGroup);
+  var chan = ios.newChannel(urlStr, null, null)
+             .QueryInterface(Ci.nsIHttpChannel);
+  chan.loadGroup = loadGroup;
+  chan.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI;
+
+  do_print("Setting loadgroup permission: " +
+           (allowPrivateLoads ? "Allowing" : "Forbidding") +
+           " private loads for doc load " + urlStr + ".");
+  loadGroup.allowLoadsFromPrivateNetworks = allowPrivateLoads;
+
+  // Try to load channel with IP literal.
+  var listener = new Listener(expectSuccessfulResponse, loadGroupAllowsAfter);
+  chan.asyncOpen(listener, null);
+}
+
+function prime_cache_entry(uri, isPrivate, networkID, onEntryPrimed) {
+  do_print("Priming cache with a " + (isPrivate ? "private" : "public") +
+           " entry.");
+
+  var fakeResponseHead = "HTTP/1.1 200 OK\r\n" +
+                         "Content-Type: text/plain\r\n" +
+                         "Server: httpd.js\r\n" +
+                         "Date: " + (new Date()).toString() + "\r\n";
+
+  asyncOpenCacheEntry(uri, "disk", Ci.nsICacheStorage.OPEN_TRUNCATE, null,
+    new OpenCallback(NEW, "a1m", "a1d", function(entry) {
+      do_print("Created " + (isPrivate ? "private" : "public") + " entry");
+      entry.setMetaDataElement("request-method", "GET");
+      entry.setMetaDataElement("response-head", fakeResponseHead);
+      if (isPrivate) {
+        entry.setMetaDataElement("loaded-from-private-network", networkID);
+      }
+      do_print("Added metadata");
+      asyncOpenCacheEntry(uri, "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
+        new OpenCallback(NORMAL, "a1m", "a1d", function(entry) {
+          do_print("Verifying " + (isPrivate ? "private" : "public") +
+                   " entry created");
+          if (isPrivate) {
+            do_check_eq(entry.getMetaDataElement("loaded-from-private-network"),
+                        networkID);
+          }
+
+          do_print((isPrivate ? "Private" : "Public") + " cache entry primed.");
+          onEntryPrimed();
+        })
+      );
+    })
+  );
+}
+
+// Create a private cache entry; if private loads are allowed, the entry will
+// be accepted. If not, the entry will be rejected, but since the server is
+// localhost the network load should also be rejected.
+function test_private_cached_entry_same_network(privateLoadAllowed) {
+  var uri = uriBase + "/failme/";
+  prime_cache_entry(uri, true, ios.networkLinkID, function() {
+    test_single_loadGroup(privateLoadAllowed, privateLoadAllowed, uri);
+  });
+}
+
+// Create a private cache entry; if private loads are allowed, the entry will
+// be accepted. If not, the entry will be rejected, but since the server is
+// localhost the network load should also be rejected.
+// LoadGroup permissions should not change after response.
+function test_private_cached_entry_same_network_doc_load(privateLoadAllowed) {
+  var uri = uriBase + "/failme/";
+  prime_cache_entry(uri, true, ios.networkLinkID, function() {
+    test_single_loadGroup_doc_load(privateLoadAllowed,
+                                   privateLoadAllowed,
+                                   privateLoadAllowed,
+                                   uri);
+  });
+}
+
+// UUID to fake a load on a different private network.
+var fakeNetworkID = "{86437A10-658B-4637-8D41-9B3693F72762}";
+
+// Ensure that privately cached entries from different networks than the current
+// one are not loaded.
+function test_private_cached_entry_diff_network(privateLoadAllowed) {
+  // We should bypass the cache entry since it was created on a different
+  // network. As such, use /passme for private loads allowed, and /failme for
+  // private loads forbidden.
+  var uri = uriBase + (privateLoadAllowed ? "/passme/" : "/failme/");
+  prime_cache_entry(uri, true, fakeNetworkID, function() {
+    test_single_loadGroup(privateLoadAllowed, privateLoadAllowed, uri);
+  });
+}
+
+// Ensure that privately cached entries from different networks than the current
+// one are not loaded; doc load.
+function test_private_cached_entry_diff_network_doc_load(privateLoadAllowed) {
+  // We should bypass the cache entry since it was created on a different
+  // network. As such, use /passme for private loads allowed, and /failme for
+  // private loads forbidden.
+  // LoadGroup permissions should not change after response.
+  var uri = uriBase + (privateLoadAllowed ? "/passme/" : "/failme/");
+  prime_cache_entry(uri, true, fakeNetworkID, function() {
+    test_single_loadGroup_doc_load(privateLoadAllowed,
+                                   privateLoadAllowed,
+                                   privateLoadAllowed,
+                                   uri);
+  });
+}
+
+// Ensure that publicly cached entries are always loaded.
+function test_public_cached_entry(privateCacheEntryAllowed) {
+  var uri = uriBase + "/failme/";
+  prime_cache_entry(uri, false, null, function() {
+    test_single_loadGroup(privateCacheEntryAllowed, true, uri);
+  });
+}
+
+// Ensure that publicly cached entries are always loaded; doc loads.
+function test_public_cached_entry_doc_load(privateCacheEntryAllowed) {
+  // Create a public cache entry for /failme; the entry should be accepted and
+  // we should not go to the network.
+  // LoadGroup permissions should forbid private loads after response.
+  var uri = uriBase + "/failme/";
+  prime_cache_entry(uri, false, null, function() {
+    test_single_loadGroup_doc_load(privateCacheEntryAllowed, true, false, uri);
+  });
+}
+
+//
+// Initialization
+//
+
+// Setup HTTP server to handle for http://localhost/passme/ and
+// http://localhost/failme/. Requests received by /failme/ will cause the test
+// script to fail. Thus, it is used to verify that specific request do not go
+// to the network.
+function setup_http_server() {
+  httpServ = new HttpServer();
+
+  httpServ.registerPathHandler("/passme/", function (metadata, response) {
+    do_print("Received request on http://localhost/passme/");
+    var httpbody = "0123456789";
+    response.setStatusLine(metadata.httpVersion, 200, "OK");
+    response.setHeader("Content-Type", "text/plain", false);
+    response.bodyOutputStream.write(httpbody, httpbody.length);
+  });
+
+  httpServ.registerPathHandler("/failme/", function (metadata, response) {
+    do_throw("http://localhost/failme/ should not not receive requests!");
+  });
+  httpServ.start(-1);
+
+  do_register_cleanup(function() {
+    httpServ.stop(function() {});
+  });
+
+  do_print("Started HTTP Server on " +
+           httpServ.identity.primaryScheme + "://" +
+           httpServ.identity.primaryHost + ":" +
+           httpServ.identity.primaryPort);
+}
+
+function setup_and_add_tests() {
+  // Get profile for disk caching.
+  do_get_profile();
+
+  // Run tests with NetworkZonePolicy enabled; set pref back to default after
+  // tests complete.
+  {
+    var prefs = Cc["@mozilla.org/preferences-service;1"]
+                .getService(Ci.nsIPrefBranch);
+    if (!prefs.getBoolPref("network.zonepolicy.enabled")) {
+      prefs.setBoolPref("network.zonepolicy.enabled", true);
+      do_register_cleanup(function() {
+        prefs.setBoolPref("network.zonepolicy.enabled", false);
+      });
+    }
+  }
+
+  uriBase = httpServ.identity.primaryScheme + "://" +
+            httpServ.identity.primaryHost + ":" +
+            httpServ.identity.primaryPort;
+
+  var tests = [
+    // Basic pref test.
+    test_basic_NetworkZonePolicy_pref,
+
+    // Verify NetworkZonePolicy can manage loadgroup permisssions.
+    test_basic_NetworkZonePolicy_and_loadGroup,
+    test_basic_NetworkZonePolicy_loadGroup_and_parent,
+    test_basic_NetworkZonePolicy_loadGroup_and_owner,
+    test_basic_NetworkZonePolicy_loadGroup_and_docshell,
+    test_loadGroup_immediate_ancestors,
+
+    // Private (localhost) network requests.
+    function test_network_private_allowed() {
+      test_single_loadGroup(true, true, uriBase + "/passme/"); },
+    function test_network_private_forbidden() {
+      test_single_loadGroup(false, false, uriBase + "/failme/"); },
+
+    // Private (localhost) network requests for document loads.
+    function test_network_private_allowed_doc_load() {
+      test_single_loadGroup_doc_load(true, true, true, uriBase + "/passme/"); },
+    function test_network_private_forbidden_doc_load() {
+      test_single_loadGroup_doc_load(false, false, false, uriBase + "/failme/"); },
+
+    // Private cache entries; same network.
+    function test_private_cache_same_network_private_allowed() {
+      test_private_cached_entry_same_network(true); },
+    function test_private_cache_same_network_private_forbidden() {
+      test_private_cached_entry_same_network(false); },
+
+    // Private cache entries; same network; doc load.
+    function test_private_cache_same_network_private_allowed_doc_load() {
+      test_private_cached_entry_same_network_doc_load(true); },
+    function test_private_cache_same_network_private_forbidden_doc_load() {
+      test_private_cached_entry_same_network_doc_load(false); },
+
+    // Private cache entries, different network.
+    function test_private_cache_diff_network_private_allowed() {
+      test_private_cached_entry_diff_network(true); },
+    function test_private_cache_diff_network_private_forbidden() {
+      test_private_cached_entry_diff_network(false); },
+
+    // Private cache entries, different network; doc load.
+    function test_private_cache_diff_network_private_allowed_doc_load() {
+      test_private_cached_entry_diff_network_doc_load(true); },
+    function test_private_cache_diff_network_private_forbidden_doc_load() {
+      test_private_cached_entry_diff_network_doc_load(false); },
+
+    // Public cache entries.
+    function test_public_cache_private_allowed() {
+      test_public_cached_entry(true); },
+    function test_public_cache_private_forbidden() {
+      test_public_cached_entry(false); },
+
+    // Public cache entries for document loads.
+    function test_public_cache_private_allowed_doc_load() {
+      test_public_cached_entry_doc_load(true); },
+    function test_public_cache_private_forbidden_doc_load() {
+      test_public_cached_entry_doc_load(false); }
+  ];
+
+  tests.forEach(function(test) {
+    add_test(test);
+  });
+
+  do_print("Added tests.");
+}
+
+function run_test() {
+  setup_http_server();
+
+  setup_and_add_tests();
+
+  run_next_test();
+}
--- a/netwerk/test/unit/test_speculative_connect.js
+++ b/netwerk/test/unit/test_speculative_connect.js
@@ -114,17 +114,19 @@ TestOutputStreamCallback.prototype = {
                     do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT ||
                                   e.result == Cr.NS_ERROR_PROXY_CONNECTION_REFUSED);
                 } else {
                     do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT ||
                                   e.result == Cr.NS_ERROR_CONNECTION_REFUSED);
                 }
             } else {
                 // A refusal to connect speculatively should throw an error.
-                do_check_eq(e.result, Cr.NS_ERROR_CONNECTION_REFUSED);
+                do_check_true(e.result == Cr.NS_ERROR_UNKNOWN_HOST ||
+                              e.result == Cr.NS_ERROR_UNKNOWN_PROXY_HOST ||
+                              e.result == Cr.NS_ERROR_CONNECTION_REFUSED);
             }
             this.transport.close(Cr.NS_BINDING_ABORTED);
             this.next();
             return;
         }
         // Spec Connect SUCCEEDED.
         if (this.expectSuccess) {
             do_check_true(true, "Success for " + this.hostname);
@@ -316,15 +318,27 @@ function next_test() {
     testList[testIdx++]();
 }
 
 /** run_test
  *
  * Main entry function for test execution.
  */
 function run_test() {
+    // Enable speculative connects on loopback for testing.
+    {
+        var prefs = Cc["@mozilla.org/preferences-service;1"]
+                    .getService(Ci.nsIPrefBranch);
+        if (!prefs.getBoolPref("network.http.speculative.allowLoopback")) {
+            prefs.setBoolPref("network.http.speculative.allowLoopback", true);
+            do_register_cleanup(function() {
+                prefs.setBoolPref("network.http.speculative.allowLoopback", false);
+            });
+        }
+    }
+
     ios = Cc["@mozilla.org/network/io-service;1"]
         .getService(Ci.nsIIOService);
 
     do_test_pending();
     next_test();
 }
 
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_websocket_offline.js
@@ -0,0 +1,45 @@
+/* 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/. */
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+// checking to make sure we don't hang as per 1038304
+// offline so url isn't impt
+var url = "ws://localhost";
+var chan;
+var offlineStatus;
+
+var listener = {
+  onAcknowledge: function(aContext, aSize) {},
+  onBinaryMessageAvailable: function(aContext, aMsg) {},
+  onMessageAvailable: function(aContext, aMsg) {},
+  onServerClose: function(aContext, aCode, aReason) {},
+  onStart: function(aContext)
+  {
+    // onStart is not called when a connection fails
+    do_check_true(false);
+  },
+  onStop: function(aContext, aStatusCode)
+  {
+    do_check_neq(aStatusCode, Cr.NS_OK);
+    Services.io.offline = offlineStatus;
+    do_test_finished();
+  }
+};
+
+function run_test() {
+  offlineStatus = Services.io.offline;
+  Services.io.offline = true;
+
+  try {
+    chan = Cc["@mozilla.org/network/protocol;1?name=ws"].
+      createInstance(Components.interfaces.nsIWebSocketChannel);
+    var uri = Services.io.newURI(url, null, null);
+    chan.asyncOpen(uri, url, listener, null);
+    do_test_pending();
+  } catch (x) {
+    dump("throwing " + x);
+    do_throw(x);
+  }
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -231,31 +231,34 @@ skip-if = os == "android"
 skip-if = os == "android"
 [test_head.js]
 [test_header_Accept-Language.js]
 [test_headers.js]
 [test_http_headers.js]
 [test_httpauth.js]
 [test_httpcancel.js]
 [test_httpResponseTimeout.js]
+# Bug 354493: fails on Android, but feature disabled by default.
+skip-if = os == "android"
 [test_httpsuspend.js]
 [test_idnservice.js]
 [test_idn_urls.js]
 [test_invalidport.js]
 [test_localstreams.js]
 [test_mismatch_last-modified.js]
 [test_MIME_params.js]
 [test_mozTXTToHTMLConv.js]
 [test_multipart_byteranges.js]
 [test_multipart_streamconv.js]
 [test_multipart_streamconv_missing_lead_boundary.js]
 [test_nestedabout_serialize.js]
 [test_net_addr.js]
 # Bug 732363: test fails on windows for unknown reasons.
 skip-if = os == "win"
+[test_networkZonePolicy.js]
 [test_nojsredir.js]
 [test_offline_status.js]
 [test_parse_content_type.js]
 [test_permmgr.js]
 [test_plaintext_sniff.js]
 [test_post.js]
 [test_private_necko_channel.js]
 # Bug 675039: intermittent fail on Android-armv6 
@@ -334,8 +337,9 @@ skip-if = os == "android"
 # disable this test on all android versions, even though it's enabled on 2.3+ in
 # the wild.
 skip-if = os == "android"
 [test_signature_extraction.js]
 run-if = os == "win"
 [test_udp_multicast.js]
 [test_redirect_history.js]
 [test_reply_without_content_type.js]
+[test_websocket_offline.js]
--- a/testing/marionette/client/marionette/marionette.py
+++ b/testing/marionette/client/marionette/marionette.py
@@ -518,26 +518,26 @@ class Marionette(object):
                                             binary=emulator_binary,
                                             userdata=emulator_img,
                                             resolution=emulator_res,
                                             profile=profile,
                                             adb_path=adb_path,
                                             process_args=process_args)
             self.emulator = self.runner.device
             self.emulator.start()
-            self.port = self.emulator.setup_port_forwarding(self.port)
+            self.port = self.emulator.setup_port_forwarding(remote_port=self.port)
             assert(self.emulator.wait_for_port(self.port)), "Timed out waiting for port!"
 
         if connect_to_running_emulator:
             self.runner = B2GEmulatorRunner(b2g_home=homedir,
                                             logdir=logdir,
                                             process_args=process_args)
             self.emulator = self.runner.device
             self.emulator.connect()
-            self.port = self.emulator.setup_port_forwarding(self.port)
+            self.port = self.emulator.setup_port_forwarding(remote_port=self.port)
             assert(self.emulator.wait_for_port(self.port)), "Timed out waiting for port!"
 
         self.client = MarionetteTransport(self.host, self.port, self.socket_timeout)
 
         if emulator:
             if busybox:
                 self.emulator.install_busybox(busybox=busybox)
             self.emulator.wait_for_system_message(self)
--- a/testing/mozbase/mozrunner/mozrunner/devices/base.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/base.py
@@ -155,36 +155,40 @@ class Device(object):
         #      while calling adb shell directly does.
         args = [self.app_ctx.adb, '-s', self.dm._deviceSerial,
                 'shell', 'cd /system/bin; chmod 555 busybox;' \
                 'for x in `./busybox --list`; do ln -s ./busybox $x; done']
         adb = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
         adb.wait()
         self.dm._verifyZip()
 
-    def setup_port_forwarding(self, remote_port):
+    def setup_port_forwarding(self, local_port=None, remote_port=2828):
         """
         Set up TCP port forwarding to the specified port on the device,
-        using any availble local port, and return the local port.
+        using any availble local port (if none specified), and return the local port.
 
-        :param remote_port: The remote port to wait on.
+        :param local_port: The local port to forward from, if unspecified a
+                           random port is chosen.
+        :param remote_port: The remote port to forward to, defaults to 2828.
+        :returns: The local_port being forwarded.
         """
-        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
-        s.bind(("",0))
-        local_port = s.getsockname()[1]
-        s.close()
+        if not local_port:
+            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            s.bind(("",0))
+            local_port = s.getsockname()[1]
+            s.close()
 
-        self.dm.forward('tcp:%d' % local_port, 'tcp:%d' % remote_port)
+        self.dm.forward('tcp:%d' % int(local_port), 'tcp:%d' % int(remote_port))
         return local_port
 
     def wait_for_net(self):
         active = False
         time_out = 0
         while not active and time_out < 40:
-            proc = subprocess.Popen([self.dm._adbPath, 'shell', '/system/bin/netcfg'], stdout=subprocess.PIPE)
+            proc = subprocess.Popen([self.app_ctx.adb, 'shell', '/system/bin/netcfg'], stdout=subprocess.PIPE)
             proc.stdout.readline() # ignore first line
             line = proc.stdout.readline()
             while line != "":
                 if (re.search(r'UP\s+[1-9]\d{0,2}\.\d{1,3}\.\d{1,3}\.\d{1,3}', line)):
                     active = True
                     break
                 line = proc.stdout.readline()
             time_out += 1
--- a/toolkit/components/addoncompat/RemoteAddonsChild.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsChild.jsm
@@ -2,20 +2,27 @@
 // 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/.
 
 this.EXPORTED_SYMBOLS = ["RemoteAddonsChild"];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
+const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+                                  "resource://gre/modules/BrowserUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "SystemPrincipal",
+                                   "@mozilla.org/systemprincipal;1", "nsIPrincipal");
+
 // Similar to Python. Returns dict[key] if it exists. Otherwise,
 // sets dict[key] to default_ and returns default_.
 function setDefault(dict, key, default_)
 {
   if (key in dict) {
     return dict[key];
   }
   dict[key] = default_;
@@ -107,19 +114,19 @@ let ContentPolicyChild = {
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPolicy, Ci.nsIObserver,
                                          Ci.nsIChannelEventSink, Ci.nsIFactory,
                                          Ci.nsISupportsWeakReference]),
 
   track: function(path, count) {
     let catMan = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
-    if (count) {
+    if (count == 1) {
       catMan.addCategoryEntry("content-policy", this._contractID, this._contractID, false, true);
-    } else {
+    } else if (count == 0) {
       catMan.deleteCategoryEntry("content-policy", this._contractID, false);
     }
   },
 
   shouldLoad: function(contentType, contentLocation, requestOrigin, node, mimeTypeGuess, extra) {
     let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
                .getService(Ci.nsISyncMessageSender);
     var rval = cpmm.sendRpcMessage("Addons:ContentPolicy:Run", {}, {
@@ -143,28 +150,180 @@ let ContentPolicyChild = {
   createInstance: function(outer, iid) {
     if (outer) {
       throw Cr.NS_ERROR_NO_AGGREGATION;
     }
     return this.QueryInterface(iid);
   },
 };
 
+// This is a shim channel whose only purpose is to return some string
+// data from an about: protocol handler.
+function AboutProtocolChannel(data, uri, originalURI, contentType)
+{
+  let stream = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream);
+  stream.setData(data, data.length);
+  this._stream = stream;
+
+  this.URI = BrowserUtils.makeURI(uri);
+  this.originalURI = BrowserUtils.makeURI(originalURI);
+  this.contentType = contentType;
+}
+
+AboutProtocolChannel.prototype = {
+  contentCharset: "utf-8",
+  contentLength: 0,
+  owner: SystemPrincipal,
+  securityInfo: null,
+  notificationCallbacks: null,
+  loadFlags: 0,
+  loadGroup: null,
+  name: null,
+  status: Cr.NS_OK,
+
+  asyncOpen: function(listener, context) {
+    let runnable = {
+      run: () => {
+        try {
+          listener.onStartRequest(this, context);
+        } catch(e) {}
+        try {
+          listener.onDataAvailable(this, context, this._stream, 0, this._stream.available());
+        } catch(e) {}
+        try {
+          listener.onStopRequest(this, context, Cr.NS_OK);
+        } catch(e) {}
+      }
+    };
+    Services.tm.currentThread.dispatch(runnable, Ci.nsIEventTarget.DISPATCH_NORMAL);
+  },
+
+  open: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  isPending: function() {
+    return false;
+  },
+
+  cancel: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  suspend: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  resume: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannel, Ci.nsIRequest])
+};
+
+// This shim protocol handler is used when content fetches an about: URL.
+function AboutProtocolInstance(contractID)
+{
+  this._contractID = contractID;
+  this._uriFlags = null;
+}
+
+AboutProtocolInstance.prototype = {
+  createInstance: function(outer, iid) {
+    if (outer != null) {
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    }
+
+    return this.QueryInterface(iid);
+  },
+
+  getURIFlags: function(uri) {
+    // Cache the result to avoid the extra IPC.
+    if (this._uriFlags !== undefined) {
+      return this._uriFlags;
+    }
+
+    let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
+               .getService(Ci.nsISyncMessageSender);
+
+    var rval = cpmm.sendRpcMessage("Addons:AboutProtocol:GetURIFlags", {
+      uri: uri.spec,
+      contractID: this._contractID
+    });
+
+    if (rval.length != 1) {
+      throw Cr.NS_ERROR_FAILURE;
+    }
+
+    this._uriFlags = rval[0];
+    return this._uriFlags;
+  },
+
+  // We take some shortcuts here. Ideally, we would return a CPOW that
+  // wraps the add-on's nsIChannel. However, many of the methods
+  // related to nsIChannel are marked [noscript], so they're not
+  // available to CPOWs. Consequently, the parent simply reads all the
+  // data out of the add-on's channel and returns that as a string. We
+  // create a new AboutProtocolChannel whose only purpose is to return
+  // the string data via an nsIStringInputStream.
+  newChannel: function(uri) {
+    let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
+               .getService(Ci.nsISyncMessageSender);
+
+    var rval = cpmm.sendRpcMessage("Addons:AboutProtocol:NewChannel", {
+      uri: uri.spec,
+      contractID: this._contractID
+    });
+
+    if (rval.length != 1) {
+      throw Cr.NS_ERROR_FAILURE;
+    }
+
+    let {data, uri, originalURI, contentType} = rval[0];
+    return new AboutProtocolChannel(data, uri, originalURI, contentType);
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory, Ci.nsIAboutModule])
+};
+
+let AboutProtocolChild = {
+  _classDescription: "Addon shim about: protocol handler",
+  _classID: Components.ID("8d56a310-0c80-11e4-9191-0800200c9a66"),
+
+  init: function() {
+    this._instances = {};
+    NotificationTracker.watch("about-protocol", (path, count) => this.track(path, count));
+  },
+
+  track: function(path, count) {
+    let contractID = path[1];
+    let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+    if (count == 1) {
+      let instance = new AboutProtocolInstance(contractID);
+      this._instances[contractID] = instance;
+      registrar.registerFactory(this._classID, this._classDescription, contractID, instance);
+    } else if (count == 0) {
+      delete this._instances[contractID];
+      registerFactory.unregisterFactory(this._classID, this);
+    }
+  },
+};
+
 // This code registers observers in the child whenever an add-on in
 // the parent asks for notifications on the given topic.
 let ObserverChild = {
   init: function() {
     NotificationTracker.watch("observer", (path, count) => this.track(path, count));
   },
 
   track: function(path, count) {
     let topic = path[1];
-    if (count) {
+    if (count == 1) {
       Services.obs.addObserver(this, topic, false);
-    } else {
+    } else if (count == 0) {
       Services.obs.removeObserver(this, topic);
     }
   },
 
   observe: function(subject, topic, data) {
     let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
                .getService(Ci.nsISyncMessageSender);
     cpmm.sendRpcMessage("Addons:Observer:Run", {}, {
@@ -183,19 +342,19 @@ function EventTargetChild(childGlobal)
   this._childGlobal = childGlobal;
   NotificationTracker.watch("event", (path, count) => this.track(path, count));
 }
 
 EventTargetChild.prototype = {
   track: function(path, count) {
     let eventType = path[1];
     let useCapture = path[2];
-    if (count) {
+    if (count == 1) {
       this._childGlobal.addEventListener(eventType, this, useCapture, true);
-    } else {
+    } else if (count == 0) {
       this._childGlobal.removeEventListener(eventType, this, useCapture);
     }
   },
 
   handleEvent: function(event) {
     this._childGlobal.sendRpcMessage("Addons:Event:Run",
                                      {type: event.type, isTrusted: event.isTrusted},
                                      {event: event});
@@ -248,16 +407,17 @@ SandboxChild.prototype = {
 };
 
 let RemoteAddonsChild = {
   _ready: false,
 
   makeReady: function() {
     NotificationTracker.init();
     ContentPolicyChild.init();
+    AboutProtocolChild.init();
     ObserverChild.init();
   },
 
   init: function(global) {
     if (!this._ready) {
       this.makeReady();
       this._ready = true;
     }
--- a/toolkit/components/addoncompat/RemoteAddonsParent.jsm
+++ b/toolkit/components/addoncompat/RemoteAddonsParent.jsm
@@ -6,16 +6,21 @@ this.EXPORTED_SYMBOLS = ["RemoteAddonsPa
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import('resource://gre/modules/Services.jsm');
 
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+                                  "resource://gre/modules/BrowserUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
+                                  "resource://gre/modules/NetUtil.jsm");
+
 // Similar to Python. Returns dict[key] if it exists. Otherwise,
 // sets dict[key] to default_ and returns default_.
 function setDefault(dict, key, default_)
 {
   if (key in dict) {
     return dict[key];
   }
   dict[key] = default_;
@@ -167,16 +172,109 @@ CategoryManagerInterposition.methods.del
   function(addon, target, category, entry, persist) {
     if (category == "content-policy") {
       ContentPolicyParent.remoteContentPolicy(entry);
     }
 
     target.deleteCategoryEntry(category, entry, persist);
   };
 
+// This shim handles the case where an add-on registers an about:
+// protocol handler in the parent and we want the child to be able to
+// use it. This code is pretty specific to Adblock's usage.
+let AboutProtocolParent = {
+  init: function() {
+    let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
+               .getService(Ci.nsIMessageBroadcaster);
+    ppmm.addMessageListener("Addons:AboutProtocol:GetURIFlags", this);
+    ppmm.addMessageListener("Addons:AboutProtocol:NewChannel", this);
+    this._protocols = [];
+  },
+
+  registerFactory: function(class_, className, contractID, factory) {
+    this._protocols.push({contractID: contractID, factory: factory});
+    NotificationTracker.add(["about-protocol", contractID]);
+  },
+
+  unregisterFactory: function(class_, factory) {
+    for (let i = 0; i < this._protocols.length; i++) {
+      if (this._protocols[i].factory == factory) {
+        NotificationTracker.remove(["about-protocol", this._protocols[i].contractID]);
+        this._protocols.splice(i, 1);
+        break;
+      }
+    }
+  },
+
+  receiveMessage: function (msg) {
+    switch (msg.name) {
+      case "Addons:AboutProtocol:GetURIFlags":
+        return this.getURIFlags(msg);
+      case "Addons:AboutProtocol:NewChannel":
+        return this.newChannel(msg);
+        break;
+    }
+  },
+
+  getURIFlags: function(msg) {
+    let uri = BrowserUtils.makeURI(msg.data.uri);
+    let contractID = msg.data.contractID;
+    let module = Cc[contractID].getService(Ci.nsIAboutModule);
+    try {
+      return module.getURIFlags(uri);
+    } catch (e) {
+      Cu.reportError(e);
+    }
+  },
+
+  // We take some shortcuts here. Ideally, we would return a CPOW that
+  // wraps the add-on's nsIChannel. However, many of the methods
+  // related to nsIChannel are marked [noscript], so they're not
+  // available to CPOWs. Consequently, we immediately read all the
+  // data out of the channel here and pass it to the child. The child
+  // then returns a shim channel that wraps an nsIStringInputStream
+  // for the string we read.
+  newChannel: function(msg) {
+    let uri = BrowserUtils.makeURI(msg.data.uri);
+    let contractID = msg.data.contractID;
+    let module = Cc[contractID].getService(Ci.nsIAboutModule);
+    try {
+      let channel = module.newChannel(uri);
+      let stream = channel.open();
+      let data = NetUtil.readInputStreamToString(stream, stream.available(), {});
+      return {
+        data: data,
+        uri: channel.URI.spec,
+        originalURI: channel.originalURI.spec,
+        contentType: channel.contentType
+      };
+    } catch (e) {
+      Cu.reportError(e);
+    }
+  },
+};
+AboutProtocolParent.init();
+
+let ComponentRegistrarInterposition = new Interposition();
+
+ComponentRegistrarInterposition.methods.registerFactory =
+  function(addon, target, class_, className, contractID, factory) {
+    if (contractID.startsWith("@mozilla.org/network/protocol/about;1?")) {
+      AboutProtocolParent.registerFactory(class_, className, contractID, factory);
+    }
+
+    target.registerFactory(class_, className, contractID, factory);
+  };
+
+ComponentRegistrarInterposition.methods.unregisterFactory =
+  function(addon, target, class_, factory) {
+    AboutProtocolParent.tryUnregisterFactory(class_, factory);
+    target.unregisterFactory(class_, factory);
+  };
+
 // This object manages add-on observers that might fire in the child
 // process. Rather than managing the observers itself, it uses the
 // parent's observer service. When an add-on listens on topic T,
 // ObserverParent asks the child process to listen on T. It also adds
 // an observer in the parent for the topic e10s-T. When the T observer
 // fires in the child, the parent fires all the e10s-T observers,
 // passing them CPOWs for the subject and data. We don't want to use T
 // in the parent because there might be non-add-on T observers that
@@ -544,16 +642,17 @@ let RemoteAddonsParent = {
   getInterfaceInterpositions: function() {
     let result = {};
 
     function register(intf, interp) {
       result[intf.number] = interp;
     }
 
     register(Ci.nsICategoryManager, CategoryManagerInterposition);
+    register(Ci.nsIComponentRegistrar, ComponentRegistrarInterposition);
     register(Ci.nsIObserverService, ObserverInterposition);
     register(Ci.nsIXPCComponents_Utils, ComponentsUtilsInterposition);
 
     return result;
   },
 
   getTaggedInterpositions: function() {
     let result = {};
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
@@ -437,31 +437,45 @@ add_task(function* test_noServerPing() {
 
 // Checks that a sent ping is correctly received by a dummy http server.
 add_task(function* test_simplePing() {
   gHttpServer.start(-1);
   gServerStarted = true;
   gRequestIterator = Iterator(new Request());
 
   yield sendPing();
-  decodeRequestPayload(yield gRequestIterator.next());
+  let request = yield gRequestIterator.next();
+  let payload = decodeRequestPayload(request);
+
+  checkPayloadInfo(payload, "test-ping");
 });
 
 // Saves the current session histograms, reloads them, perfoms a ping
 // and checks that the dummy http server received both the previously
 // saved histograms and the new ones.
 add_task(function* test_saveLoadPing() {
   let histogramsFile = getSavedHistogramsFile("saved-histograms.dat");
 
   setupTestData();
   yield TelemetryPing.testSaveHistograms(histogramsFile);
   yield TelemetryPing.testLoadHistograms(histogramsFile);
   yield sendPing();
-  checkPayload((yield gRequestIterator.next()), "test-ping", 1);
-  checkPayload((yield gRequestIterator.next()), "saved-session", 1);
+
+  // Get requests received by dummy server.
+  let request1 = yield gRequestIterator.next();
+  let request2 = yield gRequestIterator.next();
+
+  // Check we have the correct two requests. Ordering is not guaranteed.
+  if (request1.path.contains("test-ping")) {
+    checkPayload(request1, "test-ping", 1);
+    checkPayload(request2, "saved-session", 1);
+  } else {
+    checkPayload(request1, "saved-session", 1);
+    checkPayload(request2, "test-ping", 1);
+  }
 });
 
 // Checks that an expired histogram file is deleted when loaded.
 add_task(function* test_runOldPingFile() {
   let histogramsFile = getSavedHistogramsFile("old-histograms.dat");
 
   yield TelemetryPing.testSaveHistograms(histogramsFile);
   do_check_true(histogramsFile.exists());
--- a/toolkit/content/xul.css
+++ b/toolkit/content/xul.css
@@ -416,17 +416,17 @@ panel[type="arrow"]:not([animate="false"
   transition-timing-function: ease-out;
 }
 
 panel[type="arrow"][animate="open"] {
   transform: none;
   opacity: 1.0;
 }
 
-panel[animate="cancel"] {
+panel[type="arrow"][animate="cancel"] {
   transform: none;
 }
 
 panel[arrowposition="after_start"]:-moz-locale-dir(ltr),
 panel[arrowposition="after_end"]:-moz-locale-dir(rtl) {
   transform-origin: 20px top;
 }
 
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -5,16 +5,17 @@
 
 #include "nsExceptionHandler.h"
 #include "nsDataHashtable.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/dom/CrashReporterChild.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "mozilla/unused.h"
+#include "mozilla/SyncRunnable.h"
 
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 #if defined(XP_WIN32)
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
 #endif
@@ -2537,16 +2538,31 @@ static bool ChildFilter(void *context) {
   mozilla::IOInterposer::Disable();
   return true;
 }
 #endif
 
 void
 OOPInit()
 {
+  class ProxyToMainThread : public nsRunnable
+  {
+  public:
+    NS_IMETHOD Run() {
+      OOPInit();
+      return NS_OK;
+    }
+  };
+  if (!NS_IsMainThread()) {
+    // This logic needs to run on the main thread
+    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+    mozilla::SyncRunnable::DispatchToThread(mainThread, new ProxyToMainThread());
+    return;
+  }
+
   if (OOPInitialized())
     return;
 
   MOZ_ASSERT(NS_IsMainThread());
 
   NS_ABORT_IF_FALSE(gExceptionHandler != nullptr,
                     "attempt to initialize OOP crash reporter before in-process crashreporter!");
 
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -104,17 +104,19 @@ void RenameAdditionalHangMinidump(nsIFil
   nsresult AppendObjCExceptionInfoToAppNotes(void *inException);
 #endif
 nsresult GetSubmitReports(bool* aSubmitReport);
 nsresult SetSubmitReports(bool aSubmitReport);
 
 // Out-of-process crash reporter API.
 
 // Initializes out-of-process crash reporting. This method must be called
-// before the platform-specifi notificationpipe APIs are called.
+// before the platform-specific notification pipe APIs are called. If called
+// from off the main thread, this method will synchronously proxy to the main
+// thread.
 void OOPInit();
 
 // Return true if a dump was found for |childPid|, and return the
 // path in |dump|.  The caller owns the last reference to |dump| if it
 // is non-nullptr. The sequence parameter will be filled with an ordinal
 // indicating which remote process crashed first.
 bool TakeMinidumpForChild(uint32_t childPid,
                           nsIFile** dump,
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -1816,18 +1816,18 @@ var AddonManagerInternal = {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     if (!aMimetype || typeof aMimetype != "string")
       throw Components.Exception("aMimetype must be a non-empty string",
                                  Cr.NS_ERROR_INVALID_ARG);
 
-    if (aSource && !(aSource instanceof Ci.nsIDOMWindow))
-      throw Components.Exception("aSource must be a nsIDOMWindow or null",
+    if (aSource && !(aSource instanceof Ci.nsIDOMWindow) && !(aSource instanceof Ci.nsIDOMNode))
+      throw Components.Exception("aSource must be a nsIDOMWindow, a XUL element, or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (aURI && !(aURI instanceof Ci.nsIURI))
       throw Components.Exception("aURI must be a nsIURI or null",
                                  Cr.NS_ERROR_INVALID_ARG);
 
     if (!Array.isArray(aInstalls))
       throw Components.Exception("aInstalls must be an array",
--- a/toolkit/mozapps/extensions/addonManager.js
+++ b/toolkit/mozapps/extensions/addonManager.js
@@ -37,21 +37,21 @@ let gParentMM = null;
 
 
 function amManager() {
   Cu.import("resource://gre/modules/AddonManager.jsm");
 
   let globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
                  .getService(Ci.nsIMessageListenerManager);
   globalMM.loadFrameScript(CHILD_SCRIPT, true);
+  globalMM.addMessageListener(MSG_INSTALL_ADDONS, this);
 
   gParentMM = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                  .getService(Ci.nsIMessageListenerManager);
   gParentMM.addMessageListener(MSG_INSTALL_ENABLED, this);
-  gParentMM.addMessageListener(MSG_INSTALL_ADDONS, this);
 }
 
 amManager.prototype = {
   observe: function AMC_observe(aSubject, aTopic, aData) {
     if (aTopic == "addons-startup")
       AddonManagerPrivate.startup();
   },
 
@@ -69,43 +69,44 @@ amManager.prototype = {
   isInstallEnabled: function AMC_isInstallEnabled(aMimetype, aReferer) {
     return AddonManager.isInstallEnabled(aMimetype);
   },
 
   /**
    * @see amIWebInstaller.idl
    */
   installAddonsFromWebpage: function AMC_installAddonsFromWebpage(aMimetype,
-                                                                  aWindow,
+                                                                  aOriginator,
                                                                   aReferer, aUris,
                                                                   aHashes, aNames,
                                                                   aIcons, aCallback) {
     if (aUris.length == 0)
       return false;
 
     let retval = true;
     if (!AddonManager.isInstallAllowed(aMimetype, aReferer)) {
       aCallback = null;
       retval = false;
     }
 
     let loadGroup = null;
 
     try {
-      loadGroup = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                         .getInterface(Ci.nsIWebNavigation)
-                         .QueryInterface(Ci.nsIDocumentLoader).loadGroup;
+      loadGroup = aOriginator.QueryInterface(Ci.nsIDOMWindow)
+                             .QueryInterface(Ci.nsIInterfaceRequestor)
+                             .getInterface(Ci.nsIWebNavigation)
+                             .QueryInterface(Ci.nsIDocumentLoader).loadGroup;
     }
     catch (e) {
     }
 
     let installs = [];
     function buildNextInstall() {
       if (aUris.length == 0) {
-        AddonManager.installAddonsFromWebpage(aMimetype, aWindow, aReferer, installs);
+        AddonManager.installAddonsFromWebpage(aMimetype, aOriginator, aReferer, installs);
         return;
       }
       let uri = aUris.shift();
       AddonManager.getInstallForURL(uri, function buildNextInstall_getInstallForURL(aInstall) {
         function callCallback(aUri, aStatus) {
           try {
             aCallback.onInstallEnded(aUri, aStatus);
           }
@@ -163,37 +164,38 @@ amManager.prototype = {
   receiveMessage: function AMC_receiveMessage(aMessage) {
     let payload = aMessage.data;
     let referer = Services.io.newURI(payload.referer, null, null);
 
     switch (aMessage.name) {
       case MSG_INSTALL_ENABLED:
         return this.isInstallEnabled(payload.mimetype, referer);
 
-      case MSG_INSTALL_ADDONS:
+      case MSG_INSTALL_ADDONS: {
         let callback = null;
         if (payload.callbackID != -1) {
           callback = {
             onInstallEnded: function ITP_callback(url, status) {
               gParentMM.broadcastAsyncMessage(MSG_INSTALL_CALLBACK, {
                 callbackID: payload.callbackID,
                 url: url,
                 status: status
               });
             },
           };
         }
 
-        // Should reimplement this properly with Window IDs when possible,
-        // see bug 596109.
-        let window = aMessage.objects.win;
-
+        // If aMessage.objects.window exists, then we're same-process and we
+        // can target any modal prompts more correctly. Otherwise, we use the
+        // browser element for the remote browser as the best bet.
+        let originator = aMessage.objects.window || aMessage.target;
         return this.installAddonsFromWebpage(payload.mimetype,
-          window, referer, payload.uris, payload.hashes, payload.names,
-          payload.icons, callback);
+          originator, referer, payload.uris, payload.hashes,
+          payload.names, payload.icons, callback);
+      }
     }
   },
 
   classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
   _xpcom_factory: {
     createInstance: function AMC_createInstance(aOuter, aIid) {
       if (aOuter != null)
         throw Components.Exception("Component does not support aggregation",
--- a/toolkit/mozapps/extensions/amIWebInstallListener.idl
+++ b/toolkit/mozapps/extensions/amIWebInstallListener.idl
@@ -8,20 +8,20 @@ interface nsIDOMWindow;
 interface nsIURI;
 interface nsIVariant;
 
 /**
  * amIWebInstallInfo is used by the default implementation of
  * amIWebInstallListener to communicate with the running application and allow
  * it to warn the user about blocked installs and start the installs running.
  */
-[scriptable, uuid(8710e692-3989-4dc7-b607-40d57610ae75)]
+[scriptable, uuid(502f206a-c6b4-4e98-b442-e335792e2594)]
 interface amIWebInstallInfo : nsISupports
 {
-  readonly attribute nsIDOMWindow originatingWindow;
+  readonly attribute nsISupports originator;
   readonly attribute nsIURI originatingURI;
   readonly attribute nsIVariant installs;
 
   /**
    * Starts all installs.
    */
   void install();
 };
@@ -33,61 +33,61 @@ interface amIWebInstallInfo : nsISupport
  * notify when installations are blocked.
  */
 [scriptable, uuid(a5503979-89c8-441e-9e4a-321df379c172)]
 interface amIWebInstallListener : nsISupports
 {
   /**
    * Called when installation by websites is currently disabled.
    *
-   * @param  aWindow
-   *         The window that triggered the installs
+   * @param  aOriginator
+   *         The window or browser that triggered the installs
    * @param  aUri
    *         The URI of the site that triggered the installs
    * @param  aInstalls
    *         The AddonInstalls that were blocked
    * @param  aCount
    *         The number of AddonInstalls
    */
-  void onWebInstallDisabled(in nsIDOMWindow aWindow, in nsIURI aUri,
+  void onWebInstallDisabled(in nsISupports aOriginator, in nsIURI aUri,
                             [array, size_is(aCount)] in nsIVariant aInstalls,
                             [optional] in uint32_t aCount);
 
   /**
    * Called when the website is not allowed to directly prompt the user to
    * install add-ons.
    *
    * @param  aWindow
-   *         The window that triggered the installs
+   *         The window or browser that triggered the installs
    * @param  aUri
    *         The URI of the site that triggered the installs
    * @param  aInstalls
    *         The AddonInstalls that were blocked
    * @param  aCount
    *         The number of AddonInstalls
    * @return true if the caller should start the installs
    */
-  boolean onWebInstallBlocked(in nsIDOMWindow aWindow, in nsIURI aUri,
+  boolean onWebInstallBlocked(in nsISupports aOriginator, in nsIURI aUri,
                               [array, size_is(aCount)] in nsIVariant aInstalls,
                               [optional] in uint32_t aCount);
 
   /**
    * Called when a website wants to ask the user to install add-ons.
    *
    * @param  aWindow
-   *         The window that triggered the installs
+   *         The window or browser that triggered the installs
    * @param  aUri
    *         The URI of the site that triggered the installs
    * @param  aInstalls
    *         The AddonInstalls that were requested
    * @param  aCount
    *         The number of AddonInstalls
    * @return true if the caller should start the installs
    */
-  boolean onWebInstallRequested(in nsIDOMWindow aWindow, in nsIURI aUri,
+  boolean onWebInstallRequested(in nsISupports aOriginator, in nsIURI aUri,
                                 [array, size_is(aCount)] in nsIVariant aInstalls,
                                 [optional] in uint32_t aCount);
 };
 
 /**
  * amIWebInstallPrompt is used, if available, by the default implementation of 
  * amIWebInstallInfo to display a confirmation UI to the user before running
  * installs.
--- a/toolkit/mozapps/extensions/amIWebInstaller.idl
+++ b/toolkit/mozapps/extensions/amIWebInstaller.idl
@@ -27,17 +27,17 @@ interface amIInstallCallback : nsISuppor
    */
   void onInstallEnded(in AString aUrl, in int32_t aStatus);
 };
 
 
 /**
  * This interface is used to allow webpages to start installing add-ons.
  */
-[scriptable, uuid(4fdf4f84-73dc-4857-9bbe-84895e8afd5d)]
+[scriptable, uuid(8565a9c7-daf9-4af5-aae0-b272afcbccaa)]
 interface amIWebInstaller : nsISupports
 {
   /**
    * Checks if installation is enabled for a webpage.
    *
    * @param  aMimetype
    *         The mimetype for the add-on to be installed
    * @param  referer
@@ -46,18 +46,19 @@ interface amIWebInstaller : nsISupports
    */
   boolean isInstallEnabled(in AString aMimetype, in nsIURI aReferer);
 
   /**
    * Installs an array of add-ons at the request of a webpage
    *
    * @param  aMimetype
    *         The mimetype for the add-ons
-   * @param  aWindow
-   *         The window installing the add-ons
+   * @param  aOriginator
+   *         If not e10s, the window installing the add-ons, otherwise the
+   *         browser installing the add-ons.
    * @param  aReferer
    *         The URI for the webpage installing the add-ons
    * @param  aUris
    *         The URIs of add-ons to be installed
    * @param  aHashes
    *         The hashes for the add-ons to be installed
    * @param  aNames
    *         The names for the add-ons to be installed
@@ -66,17 +67,17 @@ interface amIWebInstaller : nsISupports
    * @param  aCallback
    *         An optional callback to notify about installation success and
    *         failure
    * @param  aInstallCount
    *         An optional argument including the number of add-ons to install
    * @return true if the installation was successfully started
    */
   boolean installAddonsFromWebpage(in AString aMimetype,
-                                   in nsIDOMWindow aWindow,
+                                   in nsISupports aOriginator,
                                    in nsIURI aReferer,
                                    [array, size_is(aInstallCount)] in wstring aUris,
                                    [array, size_is(aInstallCount)] in wstring aHashes,
                                    [array, size_is(aInstallCount)] in wstring aNames,
                                    [array, size_is(aInstallCount)] in wstring aIcons,
                                    [optional] in amIInstallCallback aCallback,
                                    [optional] in uint32_t aInstallCount);
 };
--- a/toolkit/mozapps/extensions/amInstallTrigger.js
+++ b/toolkit/mozapps/extensions/amInstallTrigger.js
@@ -63,23 +63,33 @@ RemoteMediator.prototype = {
     let params = {
       referer: url,
       mimetype: XPINSTALL_MIMETYPE
     };
     return this.mm.sendSyncMessage(MSG_INSTALL_ENABLED, params)[0];
   },
 
   install: function(installs, referer, callback, window) {
+    let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebNavigation)
+                         .QueryInterface(Ci.nsIDocShell)
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIContentFrameMessageManager);
+
     let callbackID = this._addCallback(callback, installs.uris);
 
     installs.mimetype = XPINSTALL_MIMETYPE;
     installs.referer = referer;
     installs.callbackID = callbackID;
 
-    return this.mm.sendSyncMessage(MSG_INSTALL_ADDONS, installs, {win: window})[0];
+    let objects = { window: null };
+    if (Services.appinfo.processType === Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT)
+      objects.window = window;
+
+    return messageManager.sendSyncMessage(MSG_INSTALL_ADDONS, installs, objects)[0];
   },
 
   _addCallback: function(callback, urls) {
     if (!callback || typeof callback != "function")
       return -1;
 
     let callbackID = this._windowID + "-" + ++this._lastCallbackID;
     let callbackObject = new CallbackObject(callbackID, callback, urls, this);
--- a/toolkit/mozapps/extensions/amWebInstallListener.js
+++ b/toolkit/mozapps/extensions/amWebInstallListener.js
@@ -32,59 +32,59 @@ const READY_STATES = [
 
 Cu.import("resource://gre/modules/Log.jsm");
 const LOGGER_ID = "addons.weblistener";
 
 // Create a new logger for use by the Addons Web Listener
 // (Requires AddonManager.jsm)
 let logger = Log.repository.getLogger(LOGGER_ID);
 
-function notifyObservers(aTopic, aWindow, aUri, aInstalls) {
+function notifyObservers(aTopic, aOriginator, aUri, aInstalls) {
   let info = {
-    originatingWindow: aWindow,
+    originator: aOriginator,
     originatingURI: aUri,
     installs: aInstalls,
 
     QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
   };
   Services.obs.notifyObservers(info, aTopic, null);
 }
 
 /**
  * Creates a new installer to monitor downloads and prompt to install when
  * ready
  *
- * @param  aWindow
- *         The window that started the installations
+ * @param  aOriginator
+ *         The window or browser that started the installations
  * @param  aUrl
  *         The URL that started the installations
  * @param  aInstalls
  *         An array of AddonInstalls
  */
-function Installer(aWindow, aUrl, aInstalls) {
-  this.window = aWindow;
+function Installer(aOriginator, aUrl, aInstalls) {
+  this.originator = aOriginator;
   this.url = aUrl;
   this.downloads = aInstalls;
   this.installed = [];
 
-  notifyObservers("addon-install-started", aWindow, aUrl, aInstalls);
+  notifyObservers("addon-install-started", aOriginator, aUrl, aInstalls);
 
   aInstalls.forEach(function(aInstall) {
     aInstall.addListener(this);
 
     // Start downloading if it hasn't already begun
     if (READY_STATES.indexOf(aInstall.state) != -1)
       aInstall.install();
   }, this);
 
   this.checkAllDownloaded();
 }
 
 Installer.prototype = {
-  window: null,
+  originator: null,
   downloads: null,
   installed: null,
   isDownloading: true,
 
   /**
    * Checks if all downloads are now complete and if so prompts to install.
    */
   checkAllDownloaded: function Installer_checkAllDownloaded() {
@@ -140,55 +140,64 @@ Installer.prototype = {
       // Stop listening and cancel any installs that are failed because of
       // compatibility reasons.
       failed.forEach(function(aInstall) {
         if (aInstall.state == AddonManager.STATE_DOWNLOADED) {
           aInstall.removeListener(this);
           aInstall.cancel();
         }
       }, this);
-      notifyObservers("addon-install-failed", this.window, this.url, failed);
+      notifyObservers("addon-install-failed", this.originator, this.url, failed);
     }
 
     // If none of the downloads were successful then exit early
     if (this.downloads.length == 0)
       return;
 
+    let parentWindow = null;
+    try {
+      parentWindow = this.originator.QueryInterface(Ci.nsIDOMWindow);
+    } catch (e) {
+      // If we're remote, then originator will be a browser. In that case,
+      // we're showing our dialog on behalf of a content process and passing
+      // null is the best we can do for now.
+    }
+
     // Check for a custom installation prompt that may be provided by the
     // applicaton
     if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
       try {
         let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
                      getService(Ci.amIWebInstallPrompt);
-        prompt.confirm(this.window, this.url, this.downloads, this.downloads.length);
+        prompt.confirm(parentWindow, this.url, this.downloads, this.downloads.length);
         return;
       }
       catch (e) {}
     }
 
     let args = {};
     args.url = this.url;
     args.installs = this.downloads;
     args.wrappedJSObject = args;
 
     try {
       Cc["@mozilla.org/base/telemetry;1"].
             getService(Ci.nsITelemetry).
             getHistogramById("SECURITY_UI").
             add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
-      Services.ww.openWindow(this.window, URI_XPINSTALL_DIALOG,
+      Services.ww.openWindow(parentWindow, URI_XPINSTALL_DIALOG,
                              null, "chrome,modal,centerscreen", args);
     } catch (e) {
       this.downloads.forEach(function(aInstall) {
         aInstall.removeListener(this);
         // Cancel the installs, as currently there is no way to make them fail
         // from here.
         aInstall.cancel();
       }, this);
-      notifyObservers("addon-install-cancelled", this.window, this.url,
+      notifyObservers("addon-install-cancelled", this.originator, this.url,
                       this.downloads);
     }
   },
 
   /**
    * Checks if all installs are now complete and if so notifies observers.
    */
   checkAllInstalled: function Installer_checkAllInstalled() {
@@ -205,20 +214,20 @@ Installer.prototype = {
         failed.push(install);
         break;
       }
     }
 
     this.downloads = null;
 
     if (failed.length > 0)
-      notifyObservers("addon-install-failed", this.window, this.url, failed);
+      notifyObservers("addon-install-failed", this.originator, this.url, failed);
 
     if (this.installed.length > 0)
-      notifyObservers("addon-install-complete", this.window, this.url, this.installed);
+      notifyObservers("addon-install-complete", this.originator, this.url, this.installed);
     this.installed = null;
   },
 
   onDownloadCancelled: function Installer_onDownloadCancelled(aInstall) {
     aInstall.removeListener(this);
     this.checkAllDownloaded();
   },
 
@@ -259,52 +268,52 @@ Installer.prototype = {
 
 function extWebInstallListener() {
 }
 
 extWebInstallListener.prototype = {
   /**
    * @see amIWebInstallListener.idl
    */
-  onWebInstallDisabled: function extWebInstallListener_onWebInstallDisabled(aWindow, aUri, aInstalls) {
+  onWebInstallDisabled: function extWebInstallListener_onWebInstallDisabled(aOriginator, aUri, aInstalls) {
     let info = {
-      originatingWindow: aWindow,
+      originator: aOriginator,
       originatingURI: aUri,
       installs: aInstalls,
 
       QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
     };
     Services.obs.notifyObservers(info, "addon-install-disabled", null);
   },
 
   /**
    * @see amIWebInstallListener.idl
    */
-  onWebInstallBlocked: function extWebInstallListener_onWebInstallBlocked(aWindow, aUri, aInstalls) {
+  onWebInstallBlocked: function extWebInstallListener_onWebInstallBlocked(aOriginator, aUri, aInstalls) {
     let info = {
-      originatingWindow: aWindow,
+      originator: aOriginator,
       originatingURI: aUri,
       installs: aInstalls,
 
       install: function onWebInstallBlocked_install() {
-        new Installer(this.originatingWindow, this.originatingURI, this.installs);
+        new Installer(this.originator, this.originatingURI, this.installs);
       },
 
       QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
     };
     Services.obs.notifyObservers(info, "addon-install-blocked", null);
 
     return false;
   },
 
   /**
    * @see amIWebInstallListener.idl
    */
-  onWebInstallRequested: function extWebInstallListener_onWebInstallRequested(aWindow, aUri, aInstalls) {
-    new Installer(aWindow, aUri, aInstalls);
+  onWebInstallRequested: function extWebInstallListener_onWebInstallRequested(aOriginator, aUri, aInstalls) {
+    new Installer(aOriginator, aUri, aInstalls);
 
     // We start the installs ourself
     return false;
   },
 
   classDescription: "XPI Install Handler",
   contractID: "@mozilla.org/addons/web-install-listener;1",
   classID: Components.ID("{0f38e086-89a3-40a5-8ffc-9b694de1d04a}"),
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -6,19 +6,26 @@
 
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
+try {
+  // AddonManager.jsm doesn't allow itself to be imported in the child
+  // process. We're used in the child process (for now), so guard against
+  // this.
+  Components.utils.import("resource://gre/modules/AddonManager.jsm");
+} catch (e) {
+}
+
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
                                   "resource://gre/modules/UpdateChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
                                   "resource://gre/modules/osfile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
--- a/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js
@@ -68,17 +68,17 @@ WindowOpenListener.prototype = {
     this.closecallback = null;
   }
 };
 
 
 var gInstallNotificationObserver = {
   observe: function(aSubject, aTopic, aData) {
     var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
-    isnot(installInfo.originatingWindow, null, "Notification should have non-null originatingWindow");
+    isnot(installInfo.originator, null, "Notification should have non-null originator");
     gSawInstallNotification = true;
     Services.obs.removeObserver(this, "addon-install-started");
   }
 };
 
 
 function test_confirmation(aWindow, aExpectedURLs) {
   var list = aWindow.document.getElementById("itemList");
--- a/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_dragdrop.js
@@ -74,17 +74,17 @@ WindowOpenListener.prototype = {
     this.closecallback = null;
   }
 };
 
 var gSawInstallNotification = false;
 var gInstallNotificationObserver = {
   observe: function(aSubject, aTopic, aData) {
     var installInfo = aSubject.QueryInterface(Ci.amIWebInstallInfo);
-    isnot(installInfo.originatingWindow, null, "Notification should have non-null originatingWindow");
+    isnot(installInfo.originator, null, "Notification should have non-null originator");
     gSawInstallNotification = true;
     Services.obs.removeObserver(this, "addon-install-started");
   }
 };
 
 
 function test() {
   waitForExplicitFinish();
--- a/toolkit/mozapps/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/mozapps/extensions/test/mochitest/mochitest.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g' || e10s
+skip-if = buildapp == 'b2g'
 support-files =
   file_empty.html
 
 [test_bug609794.html]
 [test_bug887098.html]
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_bug645699.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_bug645699.js
@@ -11,17 +11,17 @@ function test() {
   var pm = Services.perms;
   pm.add(makeURI("http://example.org/"), "install", pm.ALLOW_ACTION);
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "bug645699.html");
 }
 
 function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
+  is(installInfo.originator, gBrowser.contentWindow, "Install should have been triggered by the right window");
   is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
   return false;
 }
 
 function confirm_install(window) {
   ok(false, "Should not see the install dialog");
   return false;
 }
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist.js
@@ -12,17 +12,17 @@ function test() {
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": TESTROOT + "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 }
 
 function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
+  is(installInfo.originator, gBrowser.contentWindow, "Install should have been triggered by the right window");
   is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
   return true;
 }
 
 function confirm_install(window) {
   var items = window.document.getElementById("itemList").childNodes;
   is(items.length, 1, "Should only be 1 item listed in the confirmation dialog");
   is(items[0].name, "XPI Test", "Should have seen the name from the trigger list");
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist2.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist2.js
@@ -13,17 +13,17 @@ function test() {
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
 }
 
 function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
+  is(installInfo.originator, gBrowser.contentWindow, "Install should have been triggered by the right window");
   is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
   return false;
 }
 
 function finish_test() {
   Services.perms.remove("example.org", "install");
 
   gBrowser.removeCurrentTab();
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist4.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist4.js
@@ -12,17 +12,17 @@ function test() {
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": TESTROOT2 + "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "unsigned.xpi", makeURI(TESTROOT2 + "test.html"));
 }
 
 function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
+  is(installInfo.originator, gBrowser.contentWindow, "Install should have been triggered by the right window");
   is(installInfo.originatingURI.spec, TESTROOT2 + "test.html", "Install should have been triggered by the right uri");
   return false;
 }
 
 function finish_test(count) {
   is(count, 0, "No add-ons should have been installed");
   Services.perms.remove("example.com", "install");
 
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist5.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist5.js
@@ -7,17 +7,17 @@ function test() {
   Harness.installsCompletedCallback = finish_test;
   Harness.setup();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "startsoftwareupdate.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
 }
 
 function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
+  is(installInfo.originator, gBrowser.contentWindow, "Install should have been triggered by the right window");
   is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
   return false;
 }
 
 function finish_test(count) {
   is(count, 0, "No add-ons should have been installed");
   gBrowser.removeCurrentTab();
   Harness.finish();
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist6.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_whitelist6.js
@@ -7,17 +7,18 @@ function test() {
   Harness.installsCompletedCallback = finish_test;
   Harness.setup();
 
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.loadURI(TESTROOT + "installchrome.html? " + encodeURIComponent(TESTROOT + "unsigned.xpi"));
 }
 
 function allow_blocked(installInfo) {
-  is(installInfo.originatingWindow, gBrowser.contentWindow, "Install should have been triggered by the right window");
+  // XXX Check this for e10s.
+  is(installInfo.originator, gBrowser.contentWindow, "Install should have been triggered by the right window");
   is(installInfo.originatingURI.spec, gBrowser.currentURI.spec, "Install should have been triggered by the right uri");
   return false;
 }
 
 function finish_test(count) {
   is(count, 0, "No add-ons should have been installed");
   gBrowser.removeCurrentTab();
   Harness.finish();
--- a/toolkit/webapps/WebappOSUtils.jsm
+++ b/toolkit/webapps/WebappOSUtils.jsm
@@ -1,18 +1,23 @@
 /* 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/. */
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu, Constructor: CC } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
+Cu.import("resource://gre/modules/Promise.jsm");
+
+#ifndef MOZ_B2G
+#ifdef XP_MACOSX
 Cu.import("resource://gre/modules/osfile.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
+#endif
+#endif
 
 this.EXPORTED_SYMBOLS = ["WebappOSUtils"];
 
 // Returns the MD5 hash of a string.
 function computeHash(aString) {
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
                   createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
--- a/widget/android/nsAppShell.cpp
+++ b/widget/android/nsAppShell.cpp
@@ -21,16 +21,17 @@
 #include "nsIDOMEventListener.h"
 #include "nsIDOMClientRectList.h"
 #include "nsIDOMClientRect.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
 #include "nsINetworkLinkService.h"
 #include "nsCategoryManagerUtils.h"
 
+#include "mozilla/BackgroundHangMonitor.h"
 #include "mozilla/Services.h"
 #include "mozilla/unused.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Hal.h"
 #include "prenv.h"
 
 #include "AndroidBridge.h"
 #include "AndroidBridgeUtilities.h"
@@ -256,16 +257,18 @@ nsAppShell::ProcessNextNativeEvent(bool 
 
             curEvent = PopNextEvent();
         }
     }
 
     if (!curEvent)
         return false;
 
+    mozilla::BackgroundHangMonitor().NotifyActivity();
+
     EVLOG("nsAppShell: event %p %d", (void*)curEvent.get(), curEvent->Type());
 
     switch (curEvent->Type()) {
     case AndroidGeckoEvent::NATIVE_POKE:
         NativeEventCallback();
         break;
 
     case AndroidGeckoEvent::SENSOR_EVENT: {
--- a/widget/cocoa/moz.build
+++ b/widget/cocoa/moz.build
@@ -45,16 +45,17 @@ UNIFIED_SOURCES += [
     'nsNativeThemeCocoa.mm',
     'nsPrintDialogX.mm',
     'nsPrintOptionsX.mm',
     'nsPrintSettingsX.mm',
     'nsScreenCocoa.mm',
     'nsScreenManagerCocoa.mm',
     'nsSound.mm',
     'nsStandaloneNativeMenu.mm',
+    'nsSystemStatusBarCocoa.mm',
     'nsToolkit.mm',
     'nsWidgetFactory.mm',
     'nsWindowMap.mm',
     'OSXNotificationCenter.mm',
     'WidgetTraceEvent.mm',
 ]
 
 # These files cannot be built in unified mode because they force NSPR logging.
--- a/widget/cocoa/nsMenuBaseX.h
+++ b/widget/cocoa/nsMenuBaseX.h
@@ -26,16 +26,23 @@ enum nsMenuObjectTypeX {
 class nsMenuObjectX
 {
 public:
   virtual ~nsMenuObjectX() { }
   virtual nsMenuObjectTypeX MenuObjectType()=0;
   virtual void*             NativeData()=0;
   nsIContent*               Content() { return mContent; }
 
+  /**
+   * Called when an icon of a menu item somewhere in this menu has updated.
+   * Menu objects with parents need to propagate the notification to their
+   * parent.
+   */
+  virtual void IconUpdated() {}
+
 protected:
   nsCOMPtr<nsIContent> mContent;
 };
 
 
 //
 // Object stored as "representedObject" for all menu items
 //
--- a/widget/cocoa/nsMenuItemIconX.mm
+++ b/widget/cocoa/nsMenuItemIconX.mm
@@ -463,12 +463,16 @@ nsMenuItemIconX::OnStopFrame(imgIRequest
   [mNativeMenuItem setImage:newImage];
   
   [newImage release];
   ::CGImageRelease(iconImage);
 
   mLoadedIcon = true;
   mSetIcon = true;
 
+  if (mMenuObject) {
+    mMenuObject->IconUpdated();
+  }
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
--- a/widget/cocoa/nsMenuX.h
+++ b/widget/cocoa/nsMenuX.h
@@ -47,36 +47,37 @@ public:
   // unsafe to do while this is happening.
   static int32_t sIndexingMenuLevel;
 
   NS_DECL_CHANGEOBSERVER
 
   // nsMenuObjectX
   void*             NativeData()     {return (void*)mNativeMenu;}
   nsMenuObjectTypeX MenuObjectType() {return eSubmenuObjectType;}
+  void              IconUpdated() MOZ_OVERRIDE { mParent->IconUpdated(); }
 
   // nsMenuX
   nsresult       Create(nsMenuObjectX* aParent, nsMenuGroupOwnerX* aMenuGroupOwner, nsIContent* aNode);
   uint32_t       GetItemCount();
   nsMenuObjectX* GetItemAt(uint32_t aPos);
   nsresult       GetVisibleItemCount(uint32_t &aCount);
   nsMenuObjectX* GetVisibleItemAt(uint32_t aPos);
   nsEventStatus  MenuOpened();
   void           MenuClosed();
   void           SetRebuild(bool aMenuEvent);
   NSMenuItem*    NativeMenuItem();
+  nsresult       SetupIcon();
 
   static bool    IsXULHelpMenu(nsIContent* aMenuContent);
 
 protected:
   void           MenuConstruct();
   nsresult       RemoveAll();
   nsresult       SetEnabled(bool aIsEnabled);
   nsresult       GetEnabled(bool* aIsEnabled);
-  nsresult       SetupIcon();
   void           GetMenuPopupContent(nsIContent** aResult);
   bool           OnOpen();
   bool           OnClose();
   nsresult       AddMenuItem(nsMenuItemX* aMenuItem);
   nsresult       AddMenu(nsMenuX* aMenu);
   void           LoadMenuItem(nsIContent* inMenuItemContent);  
   void           LoadSubMenu(nsIContent* inMenuContent);
   GeckoNSMenu*   CreateMenuWithGeckoString(nsString& menuTitle);
--- a/widget/cocoa/nsStandaloneNativeMenu.h
+++ b/widget/cocoa/nsStandaloneNativeMenu.h
@@ -16,18 +16,25 @@ public:
   nsStandaloneNativeMenu();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSISTANDALONENATIVEMENU
 
   // nsMenuObjectX
   nsMenuObjectTypeX MenuObjectType() { return eStandaloneNativeMenuObjectType; }
   void * NativeData() { return mMenu != nullptr ? mMenu->NativeData() : nullptr; }
+  virtual void IconUpdated() MOZ_OVERRIDE;
 
   nsMenuX * GetMenuXObject() { return mMenu; }
 
+  // If this menu is the menu of a system status bar item (NSStatusItem),
+  // let the menu know about the status item so that it can propagate
+  // any icon changes to the status item.
+  void SetContainerStatusBarItem(NSStatusItem* aItem);
+
 protected:
   virtual ~nsStandaloneNativeMenu();
 
   nsMenuX * mMenu;
+  NSStatusItem* mContainerStatusBarItem;
 };
 
 #endif
--- a/widget/cocoa/nsStandaloneNativeMenu.mm
+++ b/widget/cocoa/nsStandaloneNativeMenu.mm
@@ -13,16 +13,17 @@
 #include "nsObjCExceptions.h"
 
 
 NS_IMPL_ISUPPORTS_INHERITED(nsStandaloneNativeMenu, nsMenuGroupOwnerX,
                             nsIMutationObserver, nsIStandaloneNativeMenu)
 
 nsStandaloneNativeMenu::nsStandaloneNativeMenu()
 : mMenu(nullptr)
+, mContainerStatusBarItem(nil)
 {
 }
 
 nsStandaloneNativeMenu::~nsStandaloneNativeMenu()
 {
   if (mMenu) delete mMenu;
 }
 
@@ -48,16 +49,18 @@ nsStandaloneNativeMenu::Init(nsIDOMEleme
   mMenu = new nsMenuX();
   rv = mMenu->Create(this, this, content);
   if (NS_FAILED(rv)) {
     delete mMenu;
     mMenu = nullptr;
     return rv;
   }
 
+  mMenu->SetupIcon();
+
   return NS_OK;
 }
 
 static void
 UpdateMenu(nsMenuX * aMenu)
 {
   aMenu->MenuOpened();
   aMenu->MenuClosed();
@@ -190,8 +193,23 @@ nsStandaloneNativeMenu::ForceUpdateNativ
           break;
         }
       }
     }
   }
 
   return NS_OK;
 }
+
+void
+nsStandaloneNativeMenu::IconUpdated()
+{
+  if (mContainerStatusBarItem) {
+    [mContainerStatusBarItem setImage:[mMenu->NativeMenuItem() image]];
+  }
+}
+
+void
+nsStandaloneNativeMenu::SetContainerStatusBarItem(NSStatusItem* aItem)
+{
+  mContainerStatusBarItem = aItem;
+  IconUpdated();
+}
new file mode 100644
--- /dev/null
+++ b/widget/cocoa/nsSystemStatusBarCocoa.h
@@ -0,0 +1,40 @@
+/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsSystemStatusBarCocoa_h_
+#define nsSystemStatusBarCocoa_h_
+
+#include "nsISystemStatusBar.h"
+#include "nsClassHashtable.h"
+#include "nsAutoPtr.h"
+
+class nsStandaloneNativeMenu;
+@class NSStatusItem;
+
+class nsSystemStatusBarCocoa : public nsISystemStatusBar
+{
+public:
+  nsSystemStatusBarCocoa() {}
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISYSTEMSTATUSBAR
+
+protected:
+  virtual ~nsSystemStatusBarCocoa() {}
+
+  struct StatusItem
+  {
+    StatusItem(nsStandaloneNativeMenu* aMenu);
+    ~StatusItem();
+
+  private:
+    nsRefPtr<nsStandaloneNativeMenu> mMenu;
+    NSStatusItem* mStatusItem;
+  };
+
+  nsClassHashtable<nsISupportsHashKey, StatusItem> mItems;
+};
+
+#endif // nsSystemStatusBarCocoa_h_
new file mode 100644
--- /dev/null
+++ b/widget/cocoa/nsSystemStatusBarCocoa.mm
@@ -0,0 +1,70 @@
+/* -*- Mode: c++; tab-width: 2; indent-tabs-mode: nil; -*- */
+/* 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/. */
+
+#import <Cocoa/Cocoa.h>
+
+#include "nsComponentManagerUtils.h"
+#include "nsSystemStatusBarCocoa.h"
+#include "nsStandaloneNativeMenu.h"
+#include "nsObjCExceptions.h"
+#include "nsIDOMElement.h"
+
+NS_IMPL_ISUPPORTS(nsSystemStatusBarCocoa, nsISystemStatusBar)
+
+NS_IMETHODIMP
+nsSystemStatusBarCocoa::AddItem(nsIDOMElement* aDOMElement)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  nsRefPtr<nsStandaloneNativeMenu> menu = new nsStandaloneNativeMenu();
+  nsresult rv = menu->Init(aDOMElement);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  nsCOMPtr<nsISupports> keyPtr = aDOMElement;
+  mItems.Put(keyPtr, new StatusItem(menu));
+
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsSystemStatusBarCocoa::RemoveItem(nsIDOMElement* aDOMElement)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+  mItems.Remove(aDOMElement);
+
+  return NS_OK;
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsSystemStatusBarCocoa::StatusItem::StatusItem(nsStandaloneNativeMenu* aMenu)
+  : mMenu(aMenu)
+{
+  NSMenu* nativeMenu = nil;
+  mMenu->GetNativeMenu(reinterpret_cast<void**>(&nativeMenu));
+
+  mStatusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
+  [mStatusItem setMenu:nativeMenu];
+  [mStatusItem setHighlightMode:YES];
+
+  // We want the status item to get its image from menu item that mMenu was
+  // initialized with. Icon loads are asynchronous, so we need to let the menu
+  // know about the item so that it can update its icon as soon as it has
+  // loaded.
+  mMenu->SetContainerStatusBarItem(mStatusItem);
+}
+
+nsSystemStatusBarCocoa::StatusItem::~StatusItem()
+{
+  mMenu->SetContainerStatusBarItem(nil);
+  [[NSStatusBar systemStatusBar] removeStatusItem:mStatusItem];
+  [mStatusItem release];
+  mStatusItem = nil;
+}
--- a/widget/cocoa/nsWidgetFactory.mm
+++ b/widget/cocoa/nsWidgetFactory.mm
@@ -74,16 +74,19 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsNativeT
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacDockSupport)
 
 #include "nsMacWebAppUtils.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacWebAppUtils)
 
 #include "nsStandaloneNativeMenu.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsStandaloneNativeMenu)
 
+#include "nsSystemStatusBarCocoa.h"
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsSystemStatusBarCocoa)
+
 #include "GfxInfo.h"
 namespace mozilla {
 namespace widget {
 // This constructor should really be shared with all platforms.
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init)
 }
 }
 
@@ -107,16 +110,17 @@ NS_DEFINE_NAMED_CID(NS_PRINTSESSION_CID)
 NS_DEFINE_NAMED_CID(NS_PRINTSETTINGSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_PRINTDIALOGSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_SYSTEMALERTSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_NATIVEMENUSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_MACDOCKSUPPORT_CID);
 NS_DEFINE_NAMED_CID(NS_MACWEBAPPUTILS_CID);
 NS_DEFINE_NAMED_CID(NS_STANDALONENATIVEMENU_CID);
+NS_DEFINE_NAMED_CID(NS_MACSYSTEMSTATUSBAR_CID);
 NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
   { &kNS_WINDOW_CID, false, NULL, nsCocoaWindowConstructor },
   { &kNS_POPUP_CID, false, NULL, nsCocoaWindowConstructor },
   { &kNS_CHILD_CID, false, NULL, nsChildViewConstructor },
   { &kNS_FILEPICKER_CID, false, NULL, nsFilePickerConstructor,
     mozilla::Module::MAIN_PROCESS_ONLY },
@@ -144,16 +148,17 @@ static const mozilla::Module::CIDEntry k
   { &kNS_PRINTDIALOGSERVICE_CID, false, NULL, nsPrintDialogServiceXConstructor,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { &kNS_IDLE_SERVICE_CID, false, NULL, nsIdleServiceXConstructor },
   { &kNS_SYSTEMALERTSSERVICE_CID, false, NULL, OSXNotificationCenterConstructor },
   { &kNS_NATIVEMENUSERVICE_CID, false, NULL, nsNativeMenuServiceXConstructor },
   { &kNS_MACDOCKSUPPORT_CID, false, NULL, nsMacDockSupportConstructor },
   { &kNS_MACWEBAPPUTILS_CID, false, NULL, nsMacWebAppUtilsConstructor },
   { &kNS_STANDALONENATIVEMENU_CID, false, NULL, nsStandaloneNativeMenuConstructor },
+  { &kNS_MACSYSTEMSTATUSBAR_CID, false, NULL, nsSystemStatusBarCocoaConstructor },
   { &kNS_GFXINFO_CID, false, NULL, mozilla::widget::GfxInfoConstructor },
   { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
   { "@mozilla.org/widgets/window/mac;1", &kNS_WINDOW_CID },
   { "@mozilla.org/widgets/popup/mac;1", &kNS_POPUP_CID },
   { "@mozilla.org/widgets/childwindow/mac;1", &kNS_CHILD_CID },
@@ -183,16 +188,17 @@ static const mozilla::Module::ContractID
   { NS_PRINTDIALOGSERVICE_CONTRACTID, &kNS_PRINTDIALOGSERVICE_CID,
     mozilla::Module::MAIN_PROCESS_ONLY },
   { "@mozilla.org/widget/idleservice;1", &kNS_IDLE_SERVICE_CID },
   { "@mozilla.org/system-alerts-service;1", &kNS_SYSTEMALERTSSERVICE_CID },
   { "@mozilla.org/widget/nativemenuservice;1", &kNS_NATIVEMENUSERVICE_CID },
   { "@mozilla.org/widget/macdocksupport;1", &kNS_MACDOCKSUPPORT_CID },
   { "@mozilla.org/widget/mac-web-app-utils;1", &kNS_MACWEBAPPUTILS_CID },
   { "@mozilla.org/widget/standalonenativemenu;1", &kNS_STANDALONENATIVEMENU_CID },
+  { "@mozilla.org/widget/macsystemstatusbar;1", &kNS_MACSYSTEMSTATUSBAR_CID },
   { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID },
   { NULL }
 };
 
 static void
 nsWidgetCocoaModuleDtor()
 {
   NativeKeyBindings::Shutdown();
--- a/widget/moz.build
+++ b/widget/moz.build
@@ -29,16 +29,17 @@ if toolkit == 'windows':
         'nsITaskbarWindowPreview.idl',
         'nsIWinTaskbar.idl',
     ]
 elif toolkit == 'cocoa':
     XPIDL_SOURCES += [
         'nsIMacDockSupport.idl',
         'nsIMacWebAppUtils.idl',
         'nsIStandaloneNativeMenu.idl',
+        'nsISystemStatusBar.idl',
         'nsITaskbarProgress.idl',
     ]
     EXPORTS += [
         'nsINativeMenuService.h',
         'nsIPrintDialogService.h',
     ]
 
 TEST_TOOL_DIRS += ['tests']
new file mode 100644
--- /dev/null
+++ b/widget/nsISystemStatusBar.idl
@@ -0,0 +1,36 @@
+/* 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 "nsISupports.idl"
+
+interface nsIDOMElement;
+
+/**
+ * Allow applications to interface with the Mac OS X system status bar.
+ */
+
+[scriptable, uuid(24493180-ee81-4b7c-8b17-9e69480b7b8a)]
+interface nsISystemStatusBar : nsISupports
+{
+  /**
+   * Add an item to the system status bar. Each item can only be present once,
+   * subsequent addItem calls with the same element will be ignored.
+   * The system status bar holds a strong reference to the added XUL menu
+   * element and the item will stay in the status bar until it is removed via
+   * a call to removeItem, or until the process shuts down.
+   * @param aDOMMenuElement A XUL menu element that contains a XUL menupopup
+   *                        with regular menu content. The menu's icon is put
+   *                        into the system status bar; clicking it will open
+   *                        a menu with the contents of the menupopup.
+   *                        The menu label is not shown.
+   */
+  void addItem(in nsIDOMElement aDOMMenuElement);
+
+  /**
+   * Remove a previously-added item from the menu bar. Calling this with an
+   * element that has not been added before will be silently ignored.
+   * @param aDOMMenuElement The XUL menu element that you called addItem with.
+   */
+  void removeItem(in nsIDOMElement aDOMMenuElement);
+};
--- a/widget/nsWidgetsCID.h
+++ b/widget/nsWidgetsCID.h
@@ -61,16 +61,21 @@
 { 0x1F39AE50, 0xB6A0, 0x4B37,                         \
   { 0x90, 0xF4, 0x60, 0xAF, 0x61, 0x41, 0x93, 0xD8 }}
 
 // {2451BAED-8DC3-46D9-9E30-96E1BAA03666}
 #define NS_MACDOCKSUPPORT_CID \
 { 0x2451BAED, 0x8DC3, 0x46D9, \
   { 0x9E, 0x30, 0x96, 0xE1, 0xBA, 0xA0, 0x36, 0x66 } }
 
+// {b6e1a890-b2b8-4883-a65f-9476f6185313}
+#define NS_MACSYSTEMSTATUSBAR_CID \
+{ 0xb6e1a890, 0xb2b8, 0x4883, \
+  { 0xa6, 0x5f, 0x94, 0x76, 0xf6, 0x18, 0x53, 0x13 } }
+
 //-----------------------------------------------------------
 //Drag & Drop & Clipboard
 //-----------------------------------------------------------
 // {8B5314BB-DB01-11d2-96CE-0060B0FB9956}
 #define NS_DRAGSERVICE_CID      \
 { 0x8b5314bb, 0xdb01, 0x11d2, { 0x96, 0xce, 0x0, 0x60, 0xb0, 0xfb, 0x99, 0x56 } }
 
 // {8B5314BC-DB01-11d2-96CE-0060B0FB9956}
--- a/xpcom/threads/nsThread.cpp
+++ b/xpcom/threads/nsThread.cpp
@@ -682,17 +682,18 @@ void canary_alarm_handler(int signum)
     }                                                                          \
   PR_END_MACRO
 
 NS_IMETHODIMP
 nsThread::ProcessNextEvent(bool aMayWait, bool* aResult)
 {
   LOG(("THRD(%p) ProcessNextEvent [%u %u]\n", this, aMayWait, mRunningEvent));
 
-  MOZ_RELEASE_ASSERT(!ipc::ProcessingUrgentMessages());
+  // If we're on the main thread, we shouldn't be dispatching CPOWs.
+  MOZ_RELEASE_ASSERT(mIsMainThread != MAIN_THREAD || !ipc::ProcessingUrgentMessages());
 
   if (NS_WARN_IF(PR_GetCurrentThread() != mThread)) {
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
   // The toplevel event loop normally blocks waiting for the next event, but
   // if we're trying to shut this thread down, we must exit the event loop when
   // the event queue is empty.