Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 28 Sep 2016 16:52:13 +0200
changeset 315528 3a5db9f20b43e149a61507707b559773e6a3debe
parent 315527 30f0eae664b4ef1e328d6b46c658e811e9edef4e (current diff)
parent 315518 b1d60f2f68c7cccc96fcf9a2075bb430a500a0f2 (diff)
child 315529 d5b3c15bbbdf8776320ed316c186f3e638a5a411
push id30751
push usercbook@mozilla.com
push dateThu, 29 Sep 2016 09:43:23 +0000
treeherdermozilla-central@b67dc49095dc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone52.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 mozilla-central to autoland
dom/canvas/CanvasRenderingContext2D.cpp
dom/html/HTMLMediaElement.cpp
dom/ipc/ContentParent.cpp
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/mediasource/TrackBuffersManager.cpp
dom/tests/mochitest/bugs/bug918719.sjs
dom/tests/mochitest/bugs/test_bug918719.html
layout/base/nsLayoutUtils.cpp
testing/web-platform/meta/IndexedDB/transaction-lifetime-empty.html.ini
toolkit/components/places/nsPlacesAutoComplete.js
toolkit/components/places/nsPlacesAutoComplete.manifest
toolkit/components/places/tests/autocomplete/.eslintrc
toolkit/components/places/tests/autocomplete/head_autocomplete.js
toolkit/components/places/tests/autocomplete/test_416211.js
toolkit/components/places/tests/autocomplete/test_416214.js
toolkit/components/places/tests/autocomplete/test_417798.js
toolkit/components/places/tests/autocomplete/test_418257.js
toolkit/components/places/tests/autocomplete/test_422277.js
toolkit/components/places/tests/autocomplete/test_autocomplete_on_value_removed_479089.js
toolkit/components/places/tests/autocomplete/test_download_embed_bookmarks.js
toolkit/components/places/tests/autocomplete/test_empty_search.js
toolkit/components/places/tests/autocomplete/test_enabled.js
toolkit/components/places/tests/autocomplete/test_escape_self.js
toolkit/components/places/tests/autocomplete/test_ignore_protocol.js
toolkit/components/places/tests/autocomplete/test_keyword_search.js
toolkit/components/places/tests/autocomplete/test_match_beginning.js
toolkit/components/places/tests/autocomplete/test_multi_word_search.js
toolkit/components/places/tests/autocomplete/test_special_search.js
toolkit/components/places/tests/autocomplete/test_swap_protocol.js
toolkit/components/places/tests/autocomplete/test_tabmatches.js
toolkit/components/places/tests/autocomplete/test_word_boundary_search.js
toolkit/components/places/tests/autocomplete/xpcshell.ini
--- a/addon-sdk/source/python-lib/cuddlefish/prefs.py
+++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py
@@ -45,16 +45,17 @@ DEFAULT_NO_CONNECTIONS_PREFS = {
     'app.update.auto' : False,
     'app.update.url': 'http://localhost/app-dummy/update',
     # Make sure GMPInstallManager won't hit the network.
     'media.gmp-gmpopenh264.autoupdate' : False,
     'media.gmp-manager.cert.checkAttributes' : False,
     'media.gmp-manager.cert.requireBuiltIn' : False,
     'media.gmp-manager.url' : 'http://localhost/media-dummy/gmpmanager',
     'media.gmp-manager.url.override': 'http://localhost/dummy-gmp-manager.xml',
+    'media.gmp-manager.updateEnabled': False,
     'browser.aboutHomeSnippets.updateUrl': 'https://localhost/snippet-dummy',
     'browser.newtab.url' : 'about:blank',
     'browser.search.update': False,
     'browser.search.suggest.enabled' : False,
     'browser.safebrowsing.phishing.enabled' : False,
     'browser.safebrowsing.provider.google.updateURL': 'http://localhost/safebrowsing-dummy/update',
     'browser.safebrowsing.provider.google.gethashURL': 'http://localhost/safebrowsing-dummy/gethash',
     'browser.safebrowsing.malware.reportURL': 'http://localhost/safebrowsing-dummy/malwarereport',
--- a/addon-sdk/source/test/preferences/no-connections.json
+++ b/addon-sdk/source/test/preferences/no-connections.json
@@ -5,16 +5,17 @@
   "app.update.url": "http://localhost/app-dummy/update",
   "app.update.enabled": false,
   "app.update.staging.enabled": false,
   "media.gmp-gmpopenh264.autoupdate": false,
   "media.gmp-manager.cert.checkAttributes": false,
   "media.gmp-manager.cert.requireBuiltIn": false,
   "media.gmp-manager.url": "http://localhost/media-dummy/gmpmanager",
   "media.gmp-manager.url.override": "http://localhost/dummy-gmp-manager.xml",
+  "media.gmp-manager.updateEnabled": false,
   "browser.aboutHomeSnippets.updateUrl": "https://localhost/snippet-dummy",
   "browser.newtab.url": "about:blank",
   "browser.search.update": false,
   "browser.search.suggest.enabled": false,
   "browser.safebrowsing.phishing.enabled": false,
   "browser.safebrowsing.provider.google.updateURL": "http://localhost/safebrowsing-dummy/update",
   "browser.safebrowsing.provider.google.gethashURL": "http://localhost/safebrowsing-dummy/gethash",
   "browser.safebrowsing.provider.google.reportURL": "http://localhost/safebrowsing-dummy/malwarereport",
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1451,17 +1451,17 @@ pref("dom.ipc.cpow.timeout", 500);
 
 // Causes access on unsafe CPOWs from browser code to throw by default.
 pref("dom.ipc.cpows.forbid-unsafe-from-browser", true);
 
 // Don't allow add-ons marked as multiprocessCompatible to use CPOWs.
 pref("dom.ipc.cpows.forbid-cpows-in-compat-addons", true);
 
 // ...except for these add-ons:
-pref("dom.ipc.cpows.allow-cpows-in-compat-addons", "{b9db16a4-6edc-47ec-a1f4-b86292ed211d},privateTab@infocatcher,mousegesturessuite@lemon_juice.addons.mozilla.org,firegestures@xuldev.org,treestyletab@piro.sakura.ne.jp,{DDC359D1-844A-42a7-9AA1-88A850A938A8},ich@maltegoetz.de,{AE93811A-5C9A-4d34-8462-F7B864FC4696}");
+pref("dom.ipc.cpows.allow-cpows-in-compat-addons", "{b9db16a4-6edc-47ec-a1f4-b86292ed211d},firegestures@xuldev.org,{DDC359D1-844A-42a7-9AA1-88A850A938A8},privateTab@infocatcher,mousegesturessuite@lemon_juice.addons.mozilla.org,treestyletab@piro.sakura.ne.jp,cliqz@cliqz.com,{AE93811A-5C9A-4d34-8462-F7B864FC4696},contextsearch2@lwz.addons.mozilla.org,{EF522540-89F5-46b9-B6FE-1829E2B572C6},{677a8f98-fd64-40b0-a883-b8c95d0cbf17},images@wink.su,fx-devtools,toolkit/require,url_advisor@kaspersky.com,{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d},{dc572301-7619-498c-a57d-39143191b318},dta@downthemall.net,{86095750-AD15-46d8-BF32-C0789F7E6A32},screenwise-prod@google.com,{91aa5abe-9de4-4347-b7b5-322c38dd9271},secureLogin@blueimp.net,ich@maltegoetz.de,come.back.block.image.from@cat-in-136.blogspot.com,{7b1bf0b6-a1b9-42b0-b75d-252036438bdc},s3crypto@data,{1e0fd655-5aea-4b4c-a583-f76ef1e3af9c},akahuku.fx.sp@toshiakisp.github.io,{aff87fa2-a58e-4edd-b852-0a20203c1e17},{1018e4d6-728f-4b20-ad56-37578a4de76b},rehostimage@engy.us,lazarus@interclue.com,{b2e69492-2358-071a-7056-24ad0c3defb1},flashstopper@byo.co.il,{e4a8a97b-f2ed-450b-b12d-ee082ba24781},jid1-f3mYMbCpz2AZYl@jetpack,{8c550e28-88c9-4764-bb52-aa489cf2efcd},{37fa1426-b82d-11db-8314-0800200c9a66},{ac2cfa60-bc96-11e0-962b-0800200c9a66},igetter@presenta.net,killspinners@byo.co.il,abhere2@moztw.org,{fc6339b8-9581-4fc7-b824-dffcb091fcb7},wampi@wink.su,backtrack@byalexv.co.uk,Gladiator_X@mail.ru,{73a6fe31-595d-460b-a920-fcc0f8843232},{46551EC9-40F0-4e47-8E18-8E5CF550CFB8},acewebextension_unlisted@acestream.org,@screen_maker,yasearch@yandex.ru,sp@avast.com,s3google@translator,igetterextension@presenta.net,{C1A2A613-35F1-4FCF-B27F-2840527B6556},screenwise-testing@google.com,helper-sig@savefrom.net,browser-loader,ImageSaver@Merci.chao,proxtube@abz.agency,wrc@avast.com,{9AA46F4F-4DC7-4c06-97AF-5035170634FE},jid1-CikLKKPVkw6ipw@jetpack,artur.dubovoy@gmail.com,nlgfeb@nlgfeb.ext,{A065A84F-95B6-433A-A0C8-4C040B77CE8A},fdm_ffext@freedownloadmanager.org");
 
 // Enable e10s hang monitoring (slow script checking and plugin hang
 // detection).
 pref("dom.ipc.processHangMonitor", true);
 
 #ifdef DEBUG
 // Don't report hangs in DEBUG builds. They're too slow and often a
 // debugger is attached.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1123,31 +1123,35 @@ var gBrowserInit = {
           Cu.reportError(e);
         }
       }
       // window.arguments[2]: referrer (nsIURI | string)
       //                 [3]: postData (nsIInputStream)
       //                 [4]: allowThirdPartyFixup (bool)
       //                 [5]: referrerPolicy (int)
       //                 [6]: userContextId (int)
+      //                 [7]: originPrincipal (nsIPrincipal)
       else if (window.arguments.length >= 3) {
         let referrerURI = window.arguments[2];
         if (typeof(referrerURI) == "string") {
           try {
             referrerURI = makeURI(referrerURI);
           } catch (e) {
             referrerURI = null;
           }
         }
         let referrerPolicy = (window.arguments[5] != undefined ?
             window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT);
         let userContextId = (window.arguments[6] != undefined ?
             window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
         loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
-                window.arguments[4] || false, referrerPolicy, userContextId);
+                window.arguments[4] || false, referrerPolicy, userContextId,
+                // pass the origin principal (if any) and force its use to create
+                // an initial about:blank viewer if present:
+                window.arguments[7], !!window.arguments[7]);
         window.focus();
       }
       // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
       // Such callers expect that window.arguments[0] is handled as a single URI.
       else {
         loadOneOrMoreURIs(uriToLoad);
       }
     }
@@ -2028,24 +2032,27 @@ function BrowserCloseTabOrWindow() {
 
 function BrowserTryToCloseWindow()
 {
   if (WindowIsClosing())
     window.close();     // WindowIsClosing does all the necessary checks
 }
 
 function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
-                 userContextId) {
+                 userContextId, originPrincipal, forceAboutBlankViewerInCurrent) {
   try {
     openLinkIn(uri, "current",
                { referrerURI: referrer,
                  referrerPolicy: referrerPolicy,
                  postData: postData,
                  allowThirdPartyFixup: allowThirdPartyFixup,
-                 userContextId: userContextId });
+                 userContextId: userContextId,
+                 originPrincipal,
+                 forceAboutBlankViewerInCurrent,
+               });
   } catch (e) {}
 }
 
 /**
  * Given a urlbar value, discerns between URIs, keywords and aliases.
  *
  * @param url
  *        The urlbar value.
@@ -5579,21 +5586,24 @@ function handleLinkClick(event, href, li
     let referrerAttrValue = Services.netUtils.parseAttributePolicyString(linkNode.
                             getAttribute("referrerpolicy"));
     if (referrerAttrValue != Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
       referrerPolicy = referrerAttrValue;
     }
   }
 
   urlSecurityCheck(href, doc.nodePrincipal);
-  let params = { charset: doc.characterSet,
-                 allowMixedContent: persistAllowMixedContentInChildTab,
-                 referrerURI: referrerURI,
-                 referrerPolicy: referrerPolicy,
-                 noReferrer: BrowserUtils.linkHasNoReferrer(linkNode) };
+  let params = {
+    charset: doc.characterSet,
+    allowMixedContent: persistAllowMixedContentInChildTab,
+    referrerURI: referrerURI,
+    referrerPolicy: referrerPolicy,
+    noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
+    originPrincipal: doc.nodePrincipal,
+  };
 
   // The new tab/window must use the same userContextId
   if (doc.nodePrincipal.originAttributes.userContextId) {
     params.userContextId = doc.nodePrincipal.originAttributes.userContextId;
   }
 
   openLinkIn(href, where, params);
   event.preventDefault();
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -502,16 +502,17 @@ var ClickEventHandler = {
       if (docShell.mixedContentChannel) {
         const sm = Services.scriptSecurityManager;
         try {
           let targetURI = BrowserUtils.makeURI(href);
           sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
           json.allowMixedContent = true;
         } catch (e) {}
       }
+      json.originPrincipal = ownerDoc.nodePrincipal;
 
       sendAsyncMessage("Content:Click", json);
       return;
     }
 
     // This might be middle mouse navigation.
     if (event.button == 1) {
       sendAsyncMessage("Content:Click", json);
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -960,16 +960,17 @@ nsContextMenu.prototype = {
 
   _isProprietaryDRM: function() {
     return this.target.isEncrypted && this.target.mediaKeys &&
            this.target.mediaKeys.keySystem != "org.w3.clearkey";
   },
 
   _openLinkInParameters : function (extra) {
     let params = { charset: gContextMenuContentData.charSet,
+                   originPrincipal: this.principal,
                    referrerURI: gContextMenuContentData.documentURIObject,
                    referrerPolicy: gContextMenuContentData.referrerPolicy,
                    noReferrer: this.linkHasNoReferrer };
     for (let p in extra) {
       params[p] = extra[p];
     }
 
     // If we want to change userContextId, we must be sure that we don't
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1495,16 +1495,17 @@
             var aFromExternal;
             var aRelatedToCurrent;
             var aAllowMixedContent;
             var aSkipAnimation;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
             var aRelatedBrowser;
+            var aOriginPrincipal;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
@@ -1513,16 +1514,17 @@
               aFromExternal         = params.fromExternal;
               aRelatedToCurrent     = params.relatedToCurrent;
               aAllowMixedContent    = params.allowMixedContent;
               aSkipAnimation        = params.skipAnimation;
               aForceNotRemote       = params.forceNotRemote;
               aNoReferrer           = params.noReferrer;
               aUserContextId        = params.userContextId;
               aRelatedBrowser       = params.relatedBrowser;
+              aOriginPrincipal      = params.originPrincipal;
             }
 
             var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :
                          Services.prefs.getBoolPref("browser.tabs.loadInBackground");
             var owner = bgLoad ? null : this.selectedTab;
             var tab = this.addTab(aURI, {
                                   referrerURI: aReferrerURI,
                                   referrerPolicy: aReferrerPolicy,
@@ -1532,16 +1534,17 @@
                                   allowThirdPartyFixup: aAllowThirdPartyFixup,
                                   fromExternal: aFromExternal,
                                   relatedToCurrent: aRelatedToCurrent,
                                   skipAnimation: aSkipAnimation,
                                   allowMixedContent: aAllowMixedContent,
                                   forceNotRemote: aForceNotRemote,
                                   noReferrer: aNoReferrer,
                                   userContextId: aUserContextId,
+                                  originPrincipal: aOriginPrincipal,
                                   relatedBrowser: aRelatedBrowser });
             if (!bgLoad)
               this.selectedTab = tab;
 
             return tab;
          ]]>
         </body>
       </method>
@@ -2038,16 +2041,17 @@
             var aRelatedToCurrent;
             var aSkipAnimation;
             var aAllowMixedContent;
             var aForceNotRemote;
             var aNoReferrer;
             var aUserContextId;
             var aEventDetail;
             var aRelatedBrowser;
+            var aOriginPrincipal;
             if (arguments.length == 2 &&
                 typeof arguments[1] == "object" &&
                 !(arguments[1] instanceof Ci.nsIURI)) {
               let params = arguments[1];
               aReferrerURI          = params.referrerURI;
               aReferrerPolicy       = params.referrerPolicy;
               aCharset              = params.charset;
               aPostData             = params.postData;
@@ -2057,16 +2061,17 @@
               aRelatedToCurrent     = params.relatedToCurrent;
               aSkipAnimation        = params.skipAnimation;
               aAllowMixedContent    = params.allowMixedContent;
               aForceNotRemote       = params.forceNotRemote;
               aNoReferrer           = params.noReferrer;
               aUserContextId        = params.userContextId;
               aEventDetail          = params.eventDetail;
               aRelatedBrowser       = params.relatedBrowser;
+              aOriginPrincipal      = params.originPrincipal;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
             var t = document.createElementNS(NS_XUL, "tab");
 
@@ -2136,16 +2141,20 @@
 
             // Dispatch a new tab notification.  We do this once we're
             // entirely done, so that things are in a consistent state
             // even if the event listener opens or closes tabs.
             var detail = aEventDetail || {};
             var evt = new CustomEvent("TabOpen", { bubbles: true, detail });
             t.dispatchEvent(evt);
 
+            if (!usingPreloadedContent && aOriginPrincipal) {
+              b.createAboutBlankContentViewer(aOriginPrincipal);
+            }
+
             // If we didn't swap docShells with a preloaded browser
             // then let's just continue loading the page normally.
             if (!usingPreloadedContent && !uriIsAboutBlank) {
               // pretend the user typed this so it'll be available till
               // the document successfully loads
               if (aURI && gInitialPages.indexOf(aURI) == -1)
                 b.userTypedValue = aURI;
 
@@ -3664,17 +3673,21 @@
               }
 
               this.lastVisibleTab = this.visibleTab;
             },
 
             assert: function(cond) {
               if (!cond) {
                 dump("Assertion failure\n" + Error().stack);
-                throw new Error("Assertion failure");
+
+                // Don't break a user's browser if an assertion fails.
+                if (this.tabbrowser.AppConstants.DEBUG) {
+                  throw new Error("Assertion failure");
+                }
               }
             },
 
             // We've decided to try to load requestedTab.
             loadRequestedTab: function() {
               this.assert(!this.loadTimer);
               this.assert(!this.minimized);
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -113,16 +113,17 @@ support-files =
   test_mcb_redirect_image.html
   test_mcb_double_redirect_image.html
   test_mcb_redirect.js
   test_mcb_redirect.sjs
   file_bug1045809_1.html
   file_bug1045809_2.html
   file_csp_block_all_mixedcontent.html
   file_csp_block_all_mixedcontent.js
+  !/image/test/mochitest/blue.png
   !/toolkit/components/passwordmgr/test/browser/form_basic.html
   !/toolkit/components/passwordmgr/test/browser/insecure_test.html
   !/toolkit/components/passwordmgr/test/browser/insecure_test_subframe.html
   !/toolkit/content/tests/browser/common/mockTransfer.js
   !/toolkit/modules/tests/browser/metadata_*.html
   !/toolkit/mozapps/extensions/test/xpinstall/amosigned.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi
   !/toolkit/mozapps/extensions/test/xpinstall/incompatible.xpi
@@ -337,16 +338,17 @@ skip-if = toolkit == "windows" # Disable
 skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
 [browser_middleMouse_noJSPaste.js]
 subsuite = clipboard
 [browser_minimize.js]
 [browser_misused_characters_in_strings.js]
 [browser_mixed_content_cert_override.js]
 [browser_mixedcontent_securityflags.js]
 tags = mcb
+[browser_modifiedclick_inherit_principal.js]
 [browser_offlineQuotaNotification.js]
 skip-if = buildapp == 'mulet'
 [browser_feed_discovery.js]
 support-files = feed_discovery.html
 [browser_gZipOfflineChild.js]
 skip-if = buildapp == 'mulet' # Bug 1066070 - I don't think either popup notifications nor addon install stuff works?
 support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^
 [browser_overflowScroll.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js
@@ -0,0 +1,30 @@
+"use strict";
+
+const kURL =
+  "http://example.com/browser/browser/base/content/test/general/dummy_page.html";
+  "data:text/html,<a href=''>Middle-click me</a>";
+
+/*
+ * Check that when manually opening content JS links in new tabs/windows,
+ * we use the correct principal, and we don't clear the URL bar.
+ */
+add_task(function* () {
+ yield BrowserTestUtils.withNewTab(kURL, function* (browser) {
+   let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+   yield ContentTask.spawn(browser, null, function* () {
+     let a = content.document.createElement("a");
+     a.href = "javascript:document.write('spoof'); void(0);";
+     a.textContent = "Some link";
+     content.document.body.appendChild(a);
+   });
+   info("Added element");
+   yield BrowserTestUtils.synthesizeMouseAtCenter("a", {button: 1}, browser);
+   let newTab = yield newTabPromise;
+   is(newTab.linkedBrowser.contentPrincipal.origin, "http://example.com",
+      "Principal should be for example.com");
+   yield BrowserTestUtils.switchTab(gBrowser, newTab);
+   info(gURLBar.value);
+   isnot(gURLBar.value, "", "URL bar should not be empty.");
+   yield BrowserTestUtils.removeTab(newTab);
+ });
+});
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -217,16 +217,19 @@ function openLinkIn(url, where, params) 
   var aInitiatingDoc        = params.initiatingDoc;
   var aIsPrivate            = params.private;
   var aSkipTabAnimation     = params.skipTabAnimation;
   var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
   var aNoReferrer           = params.noReferrer;
   var aAllowPopups          = !!params.allowPopups;
   var aUserContextId        = params.userContextId;
   var aIndicateErrorPageLoad = params.indicateErrorPageLoad;
+  var aPrincipal            = params.originPrincipal;
+  var aForceAboutBlankViewerInCurrent =
+      params.forceAboutBlankViewerInCurrent;
 
   if (where == "save") {
     // TODO(1073187): propagate referrerPolicy.
 
     // ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
     if ("isContentWindowPrivate" in params) {
       saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, null, params.isContentWindowPrivate);
     }
@@ -285,16 +288,17 @@ function openLinkIn(url, where, params) 
 
     sa.AppendElement(wuri);
     sa.AppendElement(charset);
     sa.AppendElement(referrerURISupports);
     sa.AppendElement(aPostData);
     sa.AppendElement(allowThirdPartyFixupSupports);
     sa.AppendElement(referrerPolicySupports);
     sa.AppendElement(userContextIdSupports);
+    sa.AppendElement(aPrincipal);
 
     let features = "chrome,dialog=no,all";
     if (aIsPrivate) {
       features += ",private";
     }
 
     Services.ww.openWindow(w || window, getBrowserURL(), null, features, sa);
     return;
@@ -352,16 +356,20 @@ function openLinkIn(url, where, params) 
 
     if (aAllowPopups) {
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_POPUPS;
     }
     if (aIndicateErrorPageLoad) {
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ERROR_LOAD_CHANGES_RV;
     }
 
+    if (aForceAboutBlankViewerInCurrent) {
+      w.gBrowser.selectedBrowser.createAboutBlankContentViewer(aPrincipal);
+    }
+
     w.gBrowser.loadURIWithFlags(url, {
       flags: flags,
       referrerURI: aNoReferrer ? null : aReferrerURI,
       referrerPolicy: aReferrerPolicy,
       postData: aPostData,
       userContextId: aUserContextId
     });
     break;
@@ -375,17 +383,18 @@ function openLinkIn(url, where, params) 
       charset: aCharset,
       postData: aPostData,
       inBackground: loadInBackground,
       allowThirdPartyFixup: aAllowThirdPartyFixup,
       relatedToCurrent: aRelatedToCurrent,
       skipAnimation: aSkipTabAnimation,
       allowMixedContent: aAllowMixedContent,
       noReferrer: aNoReferrer,
-      userContextId: aUserContextId
+      userContextId: aUserContextId,
+      originPrincipal: aPrincipal,
     });
     break;
   }
 
   w.gBrowser.selectedBrowser.focus();
 
   if (!loadInBackground && w.isBlankPageURL(url)) {
     w.focusAndSelectUrlBar();
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -72,22 +72,25 @@ var ContentClick = {
 
     // This part is based on handleLinkClick.
     var where = window.whereToOpenLink(json);
     if (where == "current")
       return;
 
     // Todo(903022): code for where == save
 
-    let params = { charset: browser.characterSet,
-                   referrerURI: browser.documentURI,
-                   referrerPolicy: json.referrerPolicy,
-                   noReferrer: json.noReferrer,
-                   allowMixedContent: json.allowMixedContent,
-                   isContentWindowPrivate: json.isContentWindowPrivate};
+    let params = {
+      charset: browser.characterSet,
+      referrerURI: browser.documentURI,
+      referrerPolicy: json.referrerPolicy,
+      noReferrer: json.noReferrer,
+      allowMixedContent: json.allowMixedContent,
+      isContentWindowPrivate: json.isContentWindowPrivate,
+      originPrincipal: json.originPrincipal,
+    };
 
     // The new tab/window must use the same userContextId.
     if (json.originAttributes.userContextId) {
       params.userContextId = json.originAttributes.userContextId;
     }
 
     window.openLinkIn(json.href, where, params);
   }
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -186,32 +186,21 @@ toolbar[brighttext] #downloads-indicator
 #navigator-toolbox .searchbar-textbox {
   background-color: var(--url-and-searchbar-background-color) !important;
   background-image: none !important;
   color: inherit !important;
   border: 1px solid var(--chrome-nav-bar-controls-border-color) !important;
   box-shadow: none !important;
 }
 
-:root[devtoolstheme="dark"] #identity-icon:-moz-lwtheme {
-  --identity-icon-normal: url(chrome://browser/skin/identity-icon.svg#normal-white);
-  --identity-icon-hover: url(chrome://browser/skin/identity-icon.svg#hover-white);
-  --identity-icon-notice: url(chrome://browser/skin/identity-icon.svg#notice-white);
-  --identity-icon-notice-hover: url(chrome://browser/skin/identity-icon.svg#notice-hover-white);
-}
-
-:root[devtoolstheme="dark"] #tracking-protection-icon:-moz-lwtheme {
-  --tracking-protection-icon-enabled: url(chrome://browser/skin/tracking-protection-16.svg#enabled-white);
-  --tracking-protection-icon-disabled: url(chrome://browser/skin/tracking-protection-16.svg#disabled-white);
-}
-
-:root[devtoolstheme="dark"] #connection-icon:-moz-lwtheme {
-  --connection-icon-mixed-passive-loaded: url(chrome://browser/skin/connection-mixed-passive-loaded.svg#icon-white);
-  --connection-icon-mixed-active-loaded: url(chrome://browser/skin/connection-mixed-active-loaded.svg#icon-white);
-}
+%filter substitution
+%define selectorPrefix :root[devtoolstheme="dark"] 
+%define selectorSuffix :-moz-lwtheme
+%define iconVariant -white
+%include identity-block/icons.inc.css
 
 #urlbar {
   border-inline-start: none !important;
   opacity: 1 !important;
 }
 
 window:not([chromehidden~="toolbar"]) #urlbar-wrapper {
   overflow: -moz-hidden-unscrollable;
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/identity-block/icons.inc.css
@@ -0,0 +1,62 @@
+%if 0
+/* 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/. */
+%endif
+
+@selectorPrefix@#identity-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/identity-icon.svg#normal@iconVariant@);
+}
+
+@selectorPrefix@#identity-box:hover > #identity-icon:not(.no-hover)@selectorSuffix@,
+@selectorPrefix@#identity-box[open=true] > #identity-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/identity-icon.svg#hover@iconVariant@);
+}
+
+@selectorPrefix@#identity-box.grantedPermissions > #identity-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/identity-icon.svg#notice@iconVariant@);
+}
+
+@selectorPrefix@#identity-box.grantedPermissions:hover > #identity-icon:not(.no-hover)@selectorSuffix@,
+@selectorPrefix@#identity-box.grantedPermissions[open=true] > #identity-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/identity-icon.svg#notice-hover@iconVariant@);
+}
+
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon@selectorSuffix@ {
+  list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
+}
+
+
+@selectorPrefix@#tracking-protection-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/tracking-protection-16.svg#enabled@iconVariant@);
+}
+
+@selectorPrefix@#tracking-protection-icon[state="loaded-tracking-content"]@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/tracking-protection-16.svg#disabled@iconVariant@);
+}
+
+
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.verifiedDomain > #connection-icon@selectorSuffix@,
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #connection-icon@selectorSuffix@,
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveBlocked > #connection-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/connection-secure.svg);
+  visibility: visible;
+}
+
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.certUserOverridden > #connection-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/connection-mixed-passive-loaded.svg#icon@iconVariant@);
+  visibility: visible;
+}
+
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.insecureLoginForms > #connection-icon@selectorSuffix@,
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveContent > #connection-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/connection-mixed-active-loaded.svg#icon@iconVariant@);
+  visibility: visible;
+}
+
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.weakCipher > #connection-icon@selectorSuffix@,
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContent > #connection-icon@selectorSuffix@,
+@selectorPrefix@#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContentLoadedActiveBlocked > #connection-icon@selectorSuffix@ {
+  list-style-image: url(chrome://browser/skin/connection-mixed-passive-loaded.svg#icon@iconVariant@);
+  visibility: visible;
+}
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -1,14 +1,26 @@
 %if 0
 /* 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/. */
 %endif
 
+%filter substitution
+
+%define selectorPrefix
+%define selectorSuffix
+%define iconVariant
+%include icons.inc.css
+
+%define selectorPrefix
+%define selectorSuffix :-moz-lwtheme
+%define iconVariant -black
+%include icons.inc.css
+
 #identity-box {
   font-size: .9em;
   padding: 3px 5px;
   overflow: hidden;
   /* The padding-left and padding-right transitions handle the delayed hiding of
      the forward button when hovered. */
   transition: padding-left, padding-right;
 }
@@ -47,49 +59,18 @@
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] + #urlbar > #identity-box {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-inline-start: calc(var(--backbutton-urlbar-overlap) + 5.01px);
 }
 
 /* MAIN IDENTITY ICON */
 
 #identity-icon {
-  --identity-icon-normal: url(chrome://browser/skin/identity-icon.svg#normal);
-  --identity-icon-hover: url(chrome://browser/skin/identity-icon.svg#hover);
-  --identity-icon-notice: url(chrome://browser/skin/identity-icon.svg#notice);
-  --identity-icon-notice-hover: url(chrome://browser/skin/identity-icon.svg#notice-hover);
-
   width: 16px;
   height: 16px;
-  list-style-image: var(--identity-icon-normal);
-}
-
-#identity-icon:-moz-lwtheme {
-  --identity-icon-normal: url(chrome://browser/skin/identity-icon.svg#normal-black);
-  --identity-icon-hover: url(chrome://browser/skin/identity-icon.svg#hover-black);
-  --identity-icon-notice: url(chrome://browser/skin/identity-icon.svg#notice-black);
-  --identity-icon-notice-hover: url(chrome://browser/skin/identity-icon.svg#notice-hover-black);
-}
-
-#identity-box:hover > #identity-icon:not(.no-hover),
-#identity-box[open=true] > #identity-icon {
-  list-style-image: var(--identity-icon-hover);
-}
-
-#identity-box.grantedPermissions > #identity-icon {
-  list-style-image: var(--identity-icon-notice);
-}
-
-#identity-box.grantedPermissions:hover > #identity-icon:not(.no-hover),
-#identity-box.grantedPermissions[open=true] > #identity-icon {
-  list-style-image: var(--identity-icon-notice-hover);
-}
-
-#urlbar[pageproxystate="valid"] > #identity-box.chromeUI > #identity-icon {
-  list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
 }
 
 #urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon {
   opacity: .3;
 }
 
 #urlbar[actiontype="searchengine"] > #identity-box > #identity-icon {
   -moz-image-region: inherit;
@@ -137,33 +118,20 @@
   33.33%, 66.66% {
     opacity: 1;
   }
 }
 
 /* TRACKING PROTECTION ICON */
 
 #tracking-protection-icon {
-  --tracking-protection-icon-enabled: url(chrome://browser/skin/tracking-protection-16.svg#enabled);
-  --tracking-protection-icon-disabled: url(chrome://browser/skin/tracking-protection-16.svg#disabled);
-
   width: 16px;
   height: 16px;
   margin-inline-start: 2px;
   margin-inline-end: 0;
-  list-style-image: var(--tracking-protection-icon-enabled);
-}
-
-#tracking-protection-icon:-moz-lwtheme {
-  --tracking-protection-icon-enabled: url(chrome://browser/skin/tracking-protection-16.svg#enabled-black);
-  --tracking-protection-icon-disabled: url(chrome://browser/skin/tracking-protection-16.svg#disabled-black);
-}
-
-#tracking-protection-icon[state="loaded-tracking-content"] {
-  list-style-image: var(--tracking-protection-icon-disabled);
 }
 
 #tracking-protection-icon[animate] {
   transition: margin-left 200ms ease-out, margin-right 200ms ease-out;
 }
 
 #tracking-protection-icon:not([state]) {
   margin-inline-end: -18px;
@@ -179,42 +147,10 @@
 
 /* CONNECTION ICON */
 
 #connection-icon {
   width: 16px;
   height: 16px;
   margin-inline-start: 2px;
   visibility: collapse;
-
-  --connection-icon-mixed-passive-loaded: url(chrome://browser/skin/connection-mixed-passive-loaded.svg#icon);
-  --connection-icon-mixed-active-loaded: url(chrome://browser/skin/connection-mixed-active-loaded.svg#icon);
-}
-
-#connection-icon:-moz-lwtheme {
-  --connection-icon-mixed-passive-loaded: url(chrome://browser/skin/connection-mixed-passive-loaded.svg#icon-black);
-  --connection-icon-mixed-active-loaded: url(chrome://browser/skin/connection-mixed-active-loaded.svg#icon-black);
-}
-
-#urlbar[pageproxystate="valid"] > #identity-box.verifiedDomain > #connection-icon,
-#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity > #connection-icon,
-#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveBlocked > #connection-icon {
-  list-style-image: url(chrome://browser/skin/connection-secure.svg);
-  visibility: visible;
 }
 
-#urlbar[pageproxystate="valid"] > #identity-box.certUserOverridden > #connection-icon {
-  list-style-image: var(--connection-icon-mixed-passive-loaded);
-  visibility: visible;
-}
-
-#urlbar[pageproxystate="valid"] > #identity-box.insecureLoginForms > #connection-icon,
-#urlbar[pageproxystate="valid"] > #identity-box.mixedActiveContent > #connection-icon {
-  list-style-image: var(--connection-icon-mixed-active-loaded);
-  visibility: visible;
-}
-
-#urlbar[pageproxystate="valid"] > #identity-box.weakCipher > #connection-icon,
-#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContent > #connection-icon,
-#urlbar[pageproxystate="valid"] > #identity-box.mixedDisplayContentLoadedActiveBlocked > #connection-icon {
-  list-style-image: var(--connection-icon-mixed-passive-loaded);
-  visibility: visible;
-}
--- a/caps/tests/mochitest/test_bug995943.xul
+++ b/caps/tests/mochitest/test_bug995943.xul
@@ -22,17 +22,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   const Ci = Components.interfaces;
   Cu.import("resource://gre/modules/Services.jsm");
   function debug(msg) { info(msg); }
 
   /** Test for CAPS file:// URI prefs. **/
   SimpleTest.waitForExplicitFinish();
   SimpleTest.requestCompleteLog();
   if (navigator.userAgent.indexOf("Mac OS X 10.10") != -1)
-    SimpleTest.expectAssertions(6, 9); // See bug 1067022
+    SimpleTest.expectAssertions(5, 9); // See bug 1067022
   else if (Services.appinfo.OS == "WINNT")
     SimpleTest.expectAssertions(0, 1); // See bug 1067022
 
   var rootdir = Services.appinfo.OS == "WINNT" ? "file:///C:" : "file:///";
 
   function checkLoadFileURI(domain, shouldLoad) {
     debug("Invoking checkLoadFileURI with domain: " + domain + ", shouldLoad: " + shouldLoad);
     return new Promise(function(resolve, reject) {
new file mode 100644
--- /dev/null
+++ b/config/check_js_msg_encoding.py
@@ -0,0 +1,63 @@
+# vim: set ts=8 sts=4 et sw=4 tw=99:
+# 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 script checks encoding of the files that define JSErrorFormatStrings.
+#
+# JSErrorFormatString.format member should be in ASCII encoding.
+#----------------------------------------------------------------------------
+
+from __future__ import print_function
+
+import os
+import sys
+from check_utils import get_all_toplevel_filenames
+
+scriptname = os.path.basename(__file__);
+expected_encoding = 'ascii'
+
+# The following files don't define JSErrorFormatString.
+ignore_files = [
+    'dom/base/domerr.msg',
+    'js/xpconnect/src/xpc.msg',
+]
+
+def log_pass(filename, text):
+    print('TEST-PASS | {} | {} | {}'.format(scriptname, filename, text))
+
+def log_fail(filename, text):
+    print('TEST-UNEXPECTED-FAIL | {} | {} | {}'.format(scriptname, filename,
+                                                       text))
+
+def check_single_file(filename):
+    with open(filename, 'rb') as f:
+        data = f.read()
+        try:
+            data.decode(expected_encoding)
+        except:
+            log_fail(filename, 'not in {} encoding'.format(expected_encoding))
+
+    log_pass(filename, 'ok')
+    return True
+
+def check_files():
+    result = True
+
+    for filename in get_all_toplevel_filenames():
+        if filename.endswith('.msg'):
+            if filename not in ignore_files:
+                if not check_single_file(filename):
+                    result = False
+
+    return result
+
+def main():
+    if not check_files():
+        sys.exit(1)
+
+    sys.exit(0)
+
+if __name__ == '__main__':
+    main()
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -929,19 +929,22 @@ cargo_build_flags += --manifest-path $(C
 cargo_build_flags += --target=$(RUST_TARGET)
 cargo_build_flags += --verbose
 
 # Assume any system libraries rustc links against are already in the target's LIBS.
 #
 # We need to run cargo unconditionally, because cargo is the only thing that
 # has full visibility into how changes in Rust sources might affect the final
 # build.
+#
+# XXX: We're passing `-C debuginfo=1` to rustc to work around an llvm-dsymutil
+# crash (bug 1301751). This should be temporary until we upgrade to Rust 1.12.
 force-cargo-build:
 	$(REPORT_BUILD)
-	env CARGO_TARGET_DIR=. RUSTC=$(RUSTC) $(CARGO) build $(cargo_build_flags) --
+	env CARGO_TARGET_DIR=. RUSTC=$(RUSTC) RUSTFLAGS='-C debuginfo=1' $(CARGO) build $(cargo_build_flags) --
 
 $(RUST_LIBRARY_FILE): force-cargo-build
 endif # CARGO_FILE
 
 ifdef RUST_PRELINK
 # Make target for building a prelinked rust library. This merges rust .rlibs
 # together into a single .a file which is used within the FINAL_LIBRARY.
 #
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -327,16 +327,18 @@ DeveloperToolbar.prototype.createToolbar
   hbox.setAttribute("class", "gclitoolbar-complete-node");
   stack.appendChild(hbox);
 
   let toolboxBtn = this._doc.createElement("toolbarbutton");
   toolboxBtn.setAttribute("id", "developer-toolbar-toolbox-button");
   toolboxBtn.setAttribute("class", "developer-toolbar-button");
   let toolboxTooltip = L10N.getStr("toolbar.toolsButton.tooltip");
   toolboxBtn.setAttribute("tooltiptext", toolboxTooltip);
+  let toolboxOpen = gDevToolsBrowser.hasToolboxOpened(this._chromeWindow);
+  toolboxBtn.setAttribute("checked", toolboxOpen);
   toolboxBtn.addEventListener("command", function (event) {
     let window = event.target.ownerDocument.defaultView;
     gDevToolsBrowser.toggleToolboxCommand(window.gBrowser);
   });
   this._errorCounterButton = toolboxBtn;
   this._errorCounterButton._defaultTooltipText = toolboxTooltip;
 
   // On Mac, the close button is on the left,
@@ -709,16 +711,18 @@ DeveloperToolbar.prototype.handleEvent =
         }
 
         // Propagate other errors as they're more likely to cause real issues
         // and thus should cause tests to fail.
         throw error;
       });
 
       if (ev.type == "TabSelect") {
+        let toolboxOpen = gDevToolsBrowser.hasToolboxOpened(this._chromeWindow);
+        this._errorCounterButton.setAttribute("checked", toolboxOpen);
         this._initErrorsCount(ev.target);
       }
     }
   }
   else if (ev.type == "TabClose") {
     this._stopErrorsCount(ev.target);
   }
   else if (ev.type == "beforeunload") {
--- a/devtools/client/storage/test/browser_storage_values.js
+++ b/devtools/client/storage/test/browser_storage_values.js
@@ -37,16 +37,24 @@ const testCases = [
   ]],
   [null, [
     {name: "c1", value: "Array"},
     {name: "c1.0", value: "foo"},
     {name: "c1.1", value: "Bar"},
     {name: "c1.2", value: "Object"},
     {name: "c1.2.foo", value: "Bar"},
   ], true],
+  ["c_encoded", [
+    {name: "c_encoded", value: encodeURIComponent(JSON.stringify({foo: {foo1: "bar"}}))}
+  ]],
+  [null, [
+    {name: "c_encoded", value: "Object"},
+    {name: "c_encoded.foo", value: "Object"},
+    {name: "c_encoded.foo.foo1", value: "bar"}
+  ], true],
   [["localStorage", "http://test1.example.org"]],
   ["ls2", [
     {name: "ls2", value: "foobar-2"}
   ]],
   ["ls1", [
     {name: "ls1", value: JSON.stringify({
       es6: "for", the: "win", baz: [0, 2, 3, {
         deep: "down",
--- a/devtools/client/storage/test/storage-complex-values.html
+++ b/devtools/client/storage/test/storage-complex-values.html
@@ -14,16 +14,19 @@ let partialHostname = location.hostname.
 let cookieExpiresTime = 2000000000000;
 // Setting up some cookies to eat.
 document.cookie = "c1=" + JSON.stringify([
   "foo", "Bar", {
     foo: "Bar"
   }]) + "; expires=" + new Date(cookieExpiresTime).toGMTString() +
   "; path=/browser";
 document.cookie = "cs2=sessionCookie; path=/; domain=" + partialHostname;
+// URLEncoded cookie
+document.cookie = "c_encoded=" + encodeURIComponent(JSON.stringify({foo: {foo1: "bar"}}));
+
 // ... and some local storage items ..
 const es6 = "for";
 localStorage.setItem("ls1", JSON.stringify({
   es6, the: "win", baz: [0, 2, 3, {
     deep: "down",
     nobody: "cares"
   }]}));
 localStorage.setItem("ls2", "foobar-2");
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -640,17 +640,27 @@ StorageUI.prototype = {
    * object and populates the sidebar with the parsed value. The value can also
    * be a key separated array.
    *
    * @param {string} name
    *        The key corresponding to the `value` string in the object
    * @param {string} value
    *        The string to be parsed into an object
    */
-  parseItemValue: function (name, value) {
+  parseItemValue: function (name, originalValue) {
+    // Find if value is URLEncoded ie
+    let decodedValue = "";
+    try {
+      decodedValue = decodeURIComponent(originalValue);
+    } catch (e) {
+      // Unable to decode, nothing to do
+    }
+    let value = (decodedValue && decodedValue !== originalValue)
+      ? decodedValue : originalValue;
+
     let json = null;
     try {
       json = JSOL.parse(value);
     } catch (ex) {
       json = null;
     }
 
     if (!json && value) {
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -718,8 +718,44 @@ a.learn-more-link.webconsole-learn-more-
 
 .message.info > .icon::before {
   background-position: -36px -36px;
 }
 
 .message.network .method {
   margin-inline-end: 5px;
 }
+
+/* console.table() */
+.new-consoletable {
+  width: 100%;
+  border-collapse: collapse;
+  --consoletable-border: 1px solid var(--table-splitter-color);
+}
+
+.new-consoletable thead,
+.new-consoletable tbody {
+  background-color: var(--theme-body-background);
+}
+
+.new-consoletable th {
+  background-color: var(--theme-selection-background);
+  color: var(--theme-selection-color);
+  margin: 0;
+  padding: 5px 0 0;
+  font-weight: inherit;
+  border-inline-end: var(--consoletable-border);
+  border-bottom: var(--consoletable-border);
+}
+
+.new-consoletable tr:nth-of-type(even) {
+  background-color: var(--table-zebra-background);
+}
+
+.new-consoletable td {
+  padding: 3px 4px;
+  min-width: 100px;
+  -moz-user-focus: normal;
+  color: var(--theme-body-color);
+  border-inline-end: var(--consoletable-border);
+  height: 1.25em;
+  line-height: 1.25em;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/actions/enhancers.js
@@ -0,0 +1,20 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=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";
+
+const { BATCH_ACTIONS } = require("../constants");
+
+function batchActions(batchedActions) {
+  return {
+    type: BATCH_ACTIONS,
+    actions: batchedActions,
+  };
+}
+
+module.exports = {
+  batchActions
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/actions/index.js
@@ -0,0 +1,18 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=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";
+
+const actionModules = [
+  "enhancers",
+  "filters",
+  "messages",
+  "ui",
+].map(filename => require(`./${filename}`));
+
+const actions = Object.assign({}, ...actionModules);
+
+module.exports = actions;
--- a/devtools/client/webconsole/new-console-output/actions/messages.js
+++ b/devtools/client/webconsole/new-console-output/actions/messages.js
@@ -5,43 +5,45 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
   prepareMessage
 } = require("devtools/client/webconsole/new-console-output/utils/messages");
 const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
-
+const { batchActions } = require("devtools/client/webconsole/new-console-output/actions/enhancers");
 const {
   MESSAGE_ADD,
   MESSAGES_CLEAR,
   MESSAGE_OPEN,
   MESSAGE_CLOSE,
   MESSAGE_TYPE,
+  MESSAGE_TABLE_RECEIVE,
 } = require("../constants");
 
 const defaultIdGenerator = new IdGenerator();
 
 function messageAdd(packet, idGenerator = null) {
-  return (dispatch) => {
-    if (idGenerator == null) {
-      idGenerator = defaultIdGenerator;
-    }
-    let message = prepareMessage(packet, idGenerator);
+  if (idGenerator == null) {
+    idGenerator = defaultIdGenerator;
+  }
+  let message = prepareMessage(packet, idGenerator);
+  const addMessageAction = {
+    type: MESSAGE_ADD,
+    message
+  };
 
-    if (message.type === MESSAGE_TYPE.CLEAR) {
-      dispatch(messagesClear());
-    }
-
-    dispatch({
-      type: MESSAGE_ADD,
-      message
-    });
-  };
+  if (message.type === MESSAGE_TYPE.CLEAR) {
+    return batchActions([
+      messagesClear(),
+      addMessageAction,
+    ]);
+  }
+  return addMessageAction;
 }
 
 function messagesClear() {
   return {
     type: MESSAGES_CLEAR
   };
 }
 
@@ -54,12 +56,44 @@ function messageOpen(id) {
 
 function messageClose(id) {
   return {
     type: MESSAGE_CLOSE,
     id
   };
 }
 
-exports.messageAdd = messageAdd;
-exports.messagesClear = messagesClear;
-exports.messageOpen = messageOpen;
-exports.messageClose = messageClose;
+function messageTableDataGet(id, client, dataType) {
+  return (dispatch) => {
+    let fetchObjectActorData;
+    if (["Map", "WeakMap", "Set", "WeakSet"].includes(dataType)) {
+      fetchObjectActorData = (cb) => client.enumEntries(cb);
+    } else {
+      fetchObjectActorData = (cb) => client.enumProperties({
+        ignoreNonIndexedProperties: dataType === "Array"
+      }, cb);
+    }
+
+    fetchObjectActorData(enumResponse => {
+      const {iterator} = enumResponse;
+      iterator.slice(0, iterator.count, sliceResponse => {
+        let {ownProperties} = sliceResponse;
+        dispatch(messageTableDataReceive(id, ownProperties));
+      });
+    });
+  };
+}
+
+function messageTableDataReceive(id, data) {
+  return {
+    type: MESSAGE_TABLE_RECEIVE,
+    id,
+    data
+  };
+}
+
+module.exports = {
+  messageAdd,
+  messagesClear,
+  messageOpen,
+  messageClose,
+  messageTableDataGet,
+};
--- a/devtools/client/webconsole/new-console-output/actions/moz.build
+++ b/devtools/client/webconsole/new-console-output/actions/moz.build
@@ -1,10 +1,12 @@
 # vim: set filetype=python:
 # 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/.
 
 DevToolsModules(
+    'enhancers.js',
     'filters.js',
+    'index.js',
     'messages.js',
     'ui.js',
 )
--- a/devtools/client/webconsole/new-console-output/components/console-output.js
+++ b/devtools/client/webconsole/new-console-output/components/console-output.js
@@ -7,23 +7,23 @@ const {
   createClass,
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 
-const { getAllMessages, getAllMessagesUiById } = require("devtools/client/webconsole/new-console-output/selectors/messages");
+const { getAllMessages, getAllMessagesUiById, getAllMessagesTableDataById } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
 
 const ConsoleOutput = createClass({
 
   propTypes: {
-    jsterm: PropTypes.object.isRequired,
+    hudProxyClient: PropTypes.object.isRequired,
     messages: PropTypes.object.isRequired,
     messagesUi: PropTypes.object.isRequired,
     sourceMapService: PropTypes.object,
     onViewSourceInDebugger: PropTypes.func.isRequired,
     openNetworkPanel: PropTypes.func.isRequired,
     openLink: PropTypes.func.isRequired,
   },
 
@@ -41,35 +41,39 @@ const ConsoleOutput = createClass({
       let node = ReactDOM.findDOMNode(this);
       node.scrollTop = node.scrollHeight;
     }
   },
 
   render() {
     let {
       dispatch,
+      hudProxyClient,
       messages,
       messagesUi,
+      messagesTableData,
       sourceMapService,
       onViewSourceInDebugger,
       openNetworkPanel,
       openLink,
     } = this.props;
 
     let messageNodes = messages.map((message) => {
       return (
         MessageContainer({
           dispatch,
+          hudProxyClient,
           message,
           key: message.id,
           sourceMapService,
           onViewSourceInDebugger,
           openNetworkPanel,
           openLink,
           open: messagesUi.includes(message.id),
+          tableData: messagesTableData.get(message.id),
         })
       );
     });
     return (
       dom.div({className: "webconsole-output"}, messageNodes)
     );
   }
 });
@@ -80,12 +84,13 @@ function isScrolledToBottom(outputNode, 
   return scrollNode.scrollTop + scrollNode.clientHeight >=
          scrollNode.scrollHeight - lastNodeHeight / 2;
 }
 
 function mapStateToProps(state) {
   return {
     messages: getAllMessages(state),
     messagesUi: getAllMessagesUiById(state),
+    messagesTableData: getAllMessagesTableDataById(state),
   };
 }
 
 module.exports = connect(mapStateToProps)(ConsoleOutput);
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/components/console-table.js
@@ -0,0 +1,200 @@
+/* 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 {
+  createClass,
+  createFactory,
+  DOM: dom,
+  PropTypes
+} = require("devtools/client/shared/vendor/react");
+const { ObjectClient } = require("devtools/shared/client/main");
+const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
+const {l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");
+const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
+
+const TABLE_ROW_MAX_ITEMS = 1000;
+const TABLE_COLUMN_MAX_ITEMS = 10;
+
+const ConsoleTable = createClass({
+
+  displayName: "ConsoleTable",
+
+  propTypes: {
+    dispatch: PropTypes.func.isRequired,
+    parameters: PropTypes.array.isRequired,
+    hudProxyClient: PropTypes.object.isRequired,
+    id: PropTypes.string.isRequired,
+  },
+
+  componentWillMount: function () {
+    const {id, dispatch, hudProxyClient, parameters} = this.props;
+
+    if (!Array.isArray(parameters) || parameters.length === 0) {
+      return;
+    }
+
+    const client = new ObjectClient(hudProxyClient, parameters[0]);
+    let dataType = getParametersDataType(parameters);
+
+    // Get all the object properties.
+    dispatch(actions.messageTableDataGet(id, client, dataType));
+  },
+
+  getHeaders: function (columns) {
+    let headerItems = [];
+    columns.forEach((value, key) => headerItems.push(dom.th({}, value)));
+    return headerItems;
+  },
+
+  getRows: function (columns, items) {
+    return items.map(item => {
+      let cells = [];
+      columns.forEach((value, key) => {
+        cells.push(
+          dom.td(
+            {},
+            GripMessageBody({
+              grip: item[key]
+            })
+          )
+        );
+      });
+      return dom.tr({}, cells);
+    });
+  },
+
+  render: function () {
+    const {parameters, tableData} = this.props;
+    const headersGrip = parameters[1];
+    const headers = headersGrip && headersGrip.preview ? headersGrip.preview.items : null;
+
+    // if tableData is nullable, we don't show anything.
+    if (!tableData) {
+      return null;
+    }
+
+    const {columns, items} = getTableItems(
+      tableData,
+      getParametersDataType(parameters),
+      headers
+    );
+
+    return (
+      dom.table({className: "new-consoletable devtools-monospace"},
+        dom.thead({}, this.getHeaders(columns)),
+        dom.tbody({}, this.getRows(columns, items))
+      )
+    );
+  }
+});
+
+function getParametersDataType(parameters = null) {
+  if (!Array.isArray(parameters) || parameters.length === 0) {
+    return null;
+  }
+  return parameters[0].class;
+}
+
+function getTableItems(data = {}, type, headers = null) {
+  const INDEX_NAME = "_index";
+  const VALUE_NAME = "_value";
+  const namedIndexes = {
+    [INDEX_NAME]: (
+      ["Object", "Array"].includes(type) ?
+        l10n.getStr("table.index") : l10n.getStr("table.iterationIndex")
+    ),
+    [VALUE_NAME]: l10n.getStr("table.value"),
+    key: l10n.getStr("table.key")
+  };
+
+  let columns = new Map();
+  let items = [];
+
+  let addItem = function (item) {
+    items.push(item);
+    Object.keys(item).forEach(key => addColumn(key));
+  };
+
+  let addColumn = function (columnIndex) {
+    let columnExists = columns.has(columnIndex);
+    let hasMaxColumns = columns.size == TABLE_COLUMN_MAX_ITEMS;
+    let hasCustomHeaders = Array.isArray(headers);
+
+    if (
+      !columnExists &&
+      !hasMaxColumns && (
+        !hasCustomHeaders ||
+        headers.includes(columnIndex) ||
+        columnIndex === INDEX_NAME
+      )
+    ) {
+      columns.set(columnIndex, namedIndexes[columnIndex] || columnIndex);
+    }
+  };
+
+  for (let index of Object.keys(data)) {
+    if (type !== "Object" && index == parseInt(index, 10)) {
+      index = parseInt(index, 10);
+    }
+
+    let item = {
+      [INDEX_NAME]: index
+    };
+
+    let property = data[index].value;
+
+    if (property.preview) {
+      let {preview} = property;
+      let entries = preview.ownProperties || preview.items;
+      if (entries) {
+        for (let key of Object.keys(entries)) {
+          let entry = entries[key];
+          item[key] = entry.value || entry;
+        }
+      } else {
+        if (preview.key) {
+          item.key = preview.key;
+        }
+
+        item[VALUE_NAME] = preview.value || property;
+      }
+    } else {
+      item[VALUE_NAME] = property;
+    }
+
+    addItem(item);
+
+    if (items.length === TABLE_ROW_MAX_ITEMS) {
+      break;
+    }
+  }
+
+  // Some headers might not be present in the items, so we make sure to
+  // return all the headers set by the user.
+  if (Array.isArray(headers)) {
+    headers.forEach(header => addColumn(header));
+  }
+
+  // We want to always have the index column first
+  if (columns.has(INDEX_NAME)) {
+    let index = columns.get(INDEX_NAME);
+    columns.delete(INDEX_NAME);
+    columns = new Map([[INDEX_NAME, index], ...columns.entries()]);
+  }
+
+  // We want to always have the values column last
+  if (columns.has(VALUE_NAME)) {
+    let index = columns.get(VALUE_NAME);
+    columns.delete(VALUE_NAME);
+    columns.set(VALUE_NAME, index);
+  }
+
+  return {
+    columns,
+    items
+  };
+}
+
+exports.ConsoleTable = ConsoleTable;
--- a/devtools/client/webconsole/new-console-output/components/filter-bar.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-bar.js
@@ -7,19 +7,19 @@ const {
   createFactory,
   createClass,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
 const { getAllUi } = require("devtools/client/webconsole/new-console-output/selectors/ui");
-const { filterTextSet, filtersClear } = require("devtools/client/webconsole/new-console-output/actions/filters");
-const { messagesClear } = require("devtools/client/webconsole/new-console-output/actions/messages");
-const uiActions = require("devtools/client/webconsole/new-console-output/actions/ui");
+const { filterTextSet, filtersClear } = require("devtools/client/webconsole/new-console-output/actions/index");
+const { messagesClear } = require("devtools/client/webconsole/new-console-output/actions/index");
+const uiActions = require("devtools/client/webconsole/new-console-output/actions/index");
 const {
   MESSAGE_LEVEL
 } = require("../constants");
 const FilterButton = createFactory(require("devtools/client/webconsole/new-console-output/components/filter-button").FilterButton);
 
 const FilterBar = createClass({
 
   displayName: "FilterBar",
--- a/devtools/client/webconsole/new-console-output/components/filter-button.js
+++ b/devtools/client/webconsole/new-console-output/components/filter-button.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {
   createClass,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
-const actions = require("devtools/client/webconsole/new-console-output/actions/filters");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 
 const FilterButton = createClass({
 
   displayName: "FilterButton",
 
   propTypes: {
     label: PropTypes.string.isRequired,
     filterKey: PropTypes.string.isRequired,
--- a/devtools/client/webconsole/new-console-output/components/grip-message-body.js
+++ b/devtools/client/webconsole/new-console-output/components/grip-message-body.js
@@ -36,19 +36,21 @@ GripMessageBody.propTypes = {
 function GripMessageBody(props) {
   const { grip } = props;
 
   return (
     // @TODO once there is a longString rep, also turn off quotes for those.
     typeof grip === "string"
       ? StringRep({
         object: grip,
-        useQuotes: false
+        useQuotes: false,
+        mode: props.mode,
       })
       : Rep({
         object: grip,
         objectLink: VariablesViewLink,
-        defaultRep: Grip
+        defaultRep: Grip,
+        mode: props.mode,
       })
   );
 }
 
 module.exports.GripMessageBody = GripMessageBody;
--- a/devtools/client/webconsole/new-console-output/components/message-container.js
+++ b/devtools/client/webconsole/new-console-output/components/message-container.js
@@ -32,49 +32,56 @@ const MessageContainer = createClass({
 
   propTypes: {
     message: PropTypes.object.isRequired,
     sourceMapService: PropTypes.object,
     onViewSourceInDebugger: PropTypes.func.isRequired,
     openNetworkPanel: PropTypes.func.isRequired,
     openLink: PropTypes.func.isRequired,
     open: PropTypes.bool.isRequired,
+    hudProxyClient: PropTypes.object.isRequired,
   },
 
   getDefaultProps: function () {
     return {
       open: false
     };
   },
 
   shouldComponentUpdate(nextProps, nextState) {
-    return this.props.message.repeat !== nextProps.message.repeat
-      || this.props.open !== nextProps.open;
+    const repeatChanged = this.props.message.repeat !== nextProps.message.repeat;
+    const openChanged = this.props.open !== nextProps.open;
+    const tableDataChanged = this.props.tableData !== nextProps.tableData;
+    return repeatChanged || openChanged || tableDataChanged;
   },
 
   render() {
     const {
       dispatch,
       message,
       sourceMapService,
       onViewSourceInDebugger,
       openNetworkPanel,
       openLink,
       open,
+      tableData,
+      hudProxyClient,
     } = this.props;
 
     let MessageComponent = createFactory(getMessageComponent(message));
     return MessageComponent({
       dispatch,
       message,
       sourceMapService,
       onViewSourceInDebugger,
       openNetworkPanel,
       openLink,
       open,
+      tableData,
+      hudProxyClient,
     });
   }
 });
 
 function getMessageComponent(message) {
   switch (message.source) {
     case MESSAGE_SOURCE.CONSOLE_API:
       return componentMap.get("ConsoleApiCall");
--- a/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/console-api-call.js
@@ -13,41 +13,54 @@ const {
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const FrameView = createFactory(require("devtools/client/shared/components/frame"));
 const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
 const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
 const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
 const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
 const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
-const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
+const ConsoleTable = createFactory(require("devtools/client/webconsole/new-console-output/components/console-table").ConsoleTable);
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 
 ConsoleApiCall.displayName = "ConsoleApiCall";
 
 ConsoleApiCall.propTypes = {
   message: PropTypes.object.isRequired,
   sourceMapService: PropTypes.object,
   onViewSourceInDebugger: PropTypes.func.isRequired,
   open: PropTypes.bool,
+  hudProxyClient: PropTypes.object.isRequired,
 };
 
 ConsoleApiCall.defaultProps = {
   open: false
 };
 
 function ConsoleApiCall(props) {
-  const { dispatch, message, sourceMapService, onViewSourceInDebugger, open } = props;
-  const { source, level, stacktrace, type, frame, parameters } = message;
+  const {
+    dispatch,
+    message,
+    sourceMapService,
+    onViewSourceInDebugger,
+    open,
+    hudProxyClient,
+    tableData
+  } = props;
+  const {source, level, stacktrace, type, frame, parameters } = message;
 
   let messageBody;
   if (type === "trace") {
-    messageBody = dom.span({ className: "cm-variable" }, "console.trace()");
+    messageBody = dom.span({className: "cm-variable"}, "console.trace()");
   } else if (type === "assert") {
     let reps = formatReps(parameters);
     messageBody = dom.span({ className: "cm-variable" }, "Assertion failed: ", reps);
+  } else if (type === "table") {
+    // TODO: Chrome does not output anything, see if we want to keep this
+    messageBody = dom.span({className: "cm-variable"}, "console.table()");
   } else if (parameters) {
     messageBody = formatReps(parameters);
   } else {
     messageBody = message.messageText;
   }
 
   const icon = MessageIcon({ level });
   const repeat = MessageRepeat({ repeat: message.repeat });
@@ -78,16 +91,24 @@ function ConsoleApiCall(props) {
       onClick: function () {
         if (open) {
           dispatch(actions.messageClose(message.id));
         } else {
           dispatch(actions.messageOpen(message.id));
         }
       },
     });
+  } else if (type === "table") {
+    attachment = ConsoleTable({
+      dispatch,
+      id: message.id,
+      hudProxyClient,
+      parameters: message.parameters,
+      tableData
+    });
   }
 
   const classes = ["message", "cm-s-mozilla"];
 
   classes.push(source);
   classes.push(type);
   classes.push(level);
 
--- a/devtools/client/webconsole/new-console-output/components/message-types/network-event-message.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/network-event-message.js
@@ -10,17 +10,17 @@
 const {
   createFactory,
   DOM: dom,
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
 const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
 const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
-const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 
 NetworkEventMessage.displayName = "NetworkEventMessage";
 
 NetworkEventMessage.propTypes = {
   message: PropTypes.object.isRequired,
   openNetworkPanel: PropTypes.func.isRequired,
   // @TODO: openLink will be used for mixed-content handling
   openLink: PropTypes.func.isRequired,
@@ -55,33 +55,21 @@ function NetworkEventMessage(props) {
   function onUrlClick() {
     openNetworkPanel(actor);
   }
 
   return dom.div({ className: classes.join(" ") },
     // @TODO add timestamp
     // @TODO add indent if necessary
     MessageIcon({ level }),
-    CollapseButton({
-      open,
-      title: l10n.getStr("messageToggleDetails"),
-      onClick: () => {
-        if (open) {
-          dispatch(actions.messageClose(message.id));
-        } else {
-          dispatch(actions.messageOpen(message.id));
-        }
-      },
-    }),
     dom.span({
       className: "message-body-wrapper message-body devtools-monospace",
       "aria-haspopup": "true"
     },
       dom.span({ className: "method" }, method),
       isXHR ? dom.span({ className: "xhr" }, xhr) : null,
       dom.a({ className: "url", title: url, onClick: onUrlClick },
-        url.replace(/\?.+/, "")),
-      dom.a({ className: "status" }, statusInfo)
+        url.replace(/\?.+/, ""))
     )
   );
 }
 
 module.exports.NetworkEventMessage = NetworkEventMessage;
--- a/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/page-error.js
@@ -13,17 +13,17 @@ const {
   PropTypes
 } = require("devtools/client/shared/vendor/react");
 const FrameView = createFactory(require("devtools/client/shared/components/frame"));
 const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
 const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
 const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
 const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
 
-const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 
 PageError.displayName = "PageError";
 
 PageError.propTypes = {
   message: PropTypes.object.isRequired,
   open: PropTypes.bool,
 };
 
--- a/devtools/client/webconsole/new-console-output/components/moz.build
+++ b/devtools/client/webconsole/new-console-output/components/moz.build
@@ -5,16 +5,17 @@
 
 DIRS += [
     'message-types'
 ]
 
 DevToolsModules(
     'collapse-button.js',
     'console-output.js',
+    'console-table.js',
     'filter-bar.js',
     'filter-button.js',
     'grip-message-body.js',
     'message-container.js',
     'message-icon.js',
     'message-repeat.js',
     'variables-view-link.js'
 )
--- a/devtools/client/webconsole/new-console-output/constants.js
+++ b/devtools/client/webconsole/new-console-output/constants.js
@@ -1,20 +1,22 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=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";
 
 const actionTypes = {
+  BATCH_ACTIONS: "BATCH_ACTIONS",
   MESSAGE_ADD: "MESSAGE_ADD",
   MESSAGES_CLEAR: "MESSAGES_CLEAR",
   MESSAGE_OPEN: "MESSAGE_OPEN",
   MESSAGE_CLOSE: "MESSAGE_CLOSE",
+  MESSAGE_TABLE_RECEIVE: "MESSAGE_TABLE_RECEIVE",
   FILTER_TOGGLE: "FILTER_TOGGLE",
   FILTER_TEXT_SET: "FILTER_TEXT_SET",
   FILTERS_CLEAR: "FILTERS_CLEAR",
   FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
 };
 
 const chromeRDPEnums = {
   MESSAGE_SOURCE: {
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -3,58 +3,72 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // React & Redux
 const React = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 
-const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
 
 const ConsoleOutput = React.createFactory(require("devtools/client/webconsole/new-console-output/components/console-output"));
 const FilterBar = React.createFactory(require("devtools/client/webconsole/new-console-output/components/filter-bar"));
 
 const store = configureStore();
 
 function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner) {
-  const sourceMapService = toolbox ? toolbox._sourceMapService : null;
-  let childComponent = ConsoleOutput({
-    jsterm,
-    sourceMapService,
-    onViewSourceInDebugger: frame => toolbox.viewSourceInDebugger.call(
-      toolbox,
-      frame.url,
-      frame.line
-    ),
-    openNetworkPanel: (requestId) => {
-      return toolbox.selectTool("netmonitor").then(panel => {
-        return panel.panelWin.NetMonitorController.inspectRequest(requestId);
-      });
-    },
-    openLink: (url) => {
-      owner.openLink(url);
-    },
-  });
-  let filterBar = FilterBar({});
-  let provider = React.createElement(
-    Provider,
-    { store },
-    React.DOM.div(
-      {className: "webconsole-output-wrapper"},
-      filterBar,
-      childComponent
-  ));
-  this.body = ReactDOM.render(provider, parentNode);
+  this.parentNode = parentNode;
+  this.jsterm = jsterm;
+  this.toolbox = toolbox;
+  this.owner = owner;
+
+  this.init = this.init.bind(this);
 }
 
 NewConsoleOutputWrapper.prototype = {
+  init: function () {
+    const sourceMapService = this.toolbox ? this.toolbox._sourceMapService : null;
+
+    let childComponent = ConsoleOutput({
+      hudProxyClient: this.jsterm.hud.proxy.client,
+      sourceMapService,
+      onViewSourceInDebugger: frame => this.toolbox.viewSourceInDebugger.call(
+        this.toolbox,
+        frame.url,
+        frame.line
+      ),
+      openNetworkPanel: (requestId) => {
+        return this.toolbox.selectTool("netmonitor").then(panel => {
+          return panel.panelWin.NetMonitorController.inspectRequest(requestId);
+        });
+      },
+      openLink: (url) => {
+        this.owner.openLink(url);
+      },
+    });
+    let filterBar = FilterBar({});
+    let provider = React.createElement(
+      Provider,
+      { store },
+      React.DOM.div(
+        {className: "webconsole-output-wrapper"},
+        filterBar,
+        childComponent
+    ));
+
+    this.body = ReactDOM.render(provider, this.parentNode);
+  },
   dispatchMessageAdd: (message) => {
     store.dispatch(actions.messageAdd(message));
   },
+  dispatchMessagesAdd: (messages) => {
+    const batchedActions = messages.map(message => actions.messageAdd(message));
+    store.dispatch(actions.batchActions(batchedActions));
+  },
   dispatchMessagesClear: () => {
     store.dispatch(actions.messagesClear());
   },
 };
 
 // Exports from this module
 module.exports = NewConsoleOutputWrapper;
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -6,21 +6,23 @@
 "use strict";
 
 const Immutable = require("devtools/client/shared/vendor/immutable");
 const constants = require("devtools/client/webconsole/new-console-output/constants");
 
 const MessageState = Immutable.Record({
   messagesById: Immutable.List(),
   messagesUiById: Immutable.List(),
+  messagesTableDataById: Immutable.Map(),
 });
 
 function messages(state = new MessageState(), action) {
   const messagesById = state.messagesById;
   const messagesUiById = state.messagesUiById;
+  const messagesTableDataById = state.messagesTableDataById;
 
   switch (action.type) {
     case constants.MESSAGE_ADD:
       let newMessage = action.message;
 
       if (newMessage.type === constants.MESSAGE_TYPE.NULL_MESSAGE) {
         return state;
       }
@@ -47,14 +49,17 @@ function messages(state = new MessageSta
         record.set("messagesById", Immutable.List());
         record.set("messagesUiById", Immutable.List());
       });
     case constants.MESSAGE_OPEN:
       return state.set("messagesUiById", messagesUiById.push(action.id));
     case constants.MESSAGE_CLOSE:
       let index = state.messagesUiById.indexOf(action.id);
       return state.deleteIn(["messagesUiById", index]);
+    case constants.MESSAGE_TABLE_RECEIVE:
+      const {id, data} = action;
+      return state.set("messagesTableDataById", messagesTableDataById.set(id, data));
   }
 
   return state;
 }
 
 exports.messages = messages;
--- a/devtools/client/webconsole/new-console-output/selectors/messages.js
+++ b/devtools/client/webconsole/new-console-output/selectors/messages.js
@@ -29,16 +29,20 @@ function getAllMessages(state) {
     logLimit
   );
 }
 
 function getAllMessagesUiById(state) {
   return state.messages.messagesUiById;
 }
 
+function getAllMessagesTableDataById(state) {
+  return state.messages.messagesTableDataById;
+}
+
 function filterLevel(messages, filters) {
   return messages.filter((message) => {
     return filters.get(message.level) === true
       || [MESSAGE_TYPE.COMMAND, MESSAGE_TYPE.RESULT].includes(message.type);
   });
 }
 
 function filterNetwork(messages, filters) {
@@ -109,8 +113,9 @@ function prune(messages, logLimit) {
     return messages.splice(0, messageCount - logLimit);
   }
 
   return messages;
 }
 
 exports.getAllMessages = getAllMessages;
 exports.getAllMessagesUiById = getAllMessagesUiById;
+exports.getAllMessagesTableDataById = getAllMessagesTableDataById;
--- a/devtools/client/webconsole/new-console-output/store.js
+++ b/devtools/client/webconsole/new-console-output/store.js
@@ -1,17 +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/. */
 "use strict";
 
 const {FilterState} = require("devtools/client/webconsole/new-console-output/reducers/filters");
 const {PrefState} = require("devtools/client/webconsole/new-console-output/reducers/prefs");
-const { applyMiddleware, combineReducers, createStore } = require("devtools/client/shared/vendor/redux");
+const {
+  applyMiddleware,
+  combineReducers,
+  compose,
+  createStore
+} = require("devtools/client/shared/vendor/redux");
 const { thunk } = require("devtools/client/shared/redux/middleware/thunk");
+const constants = require("devtools/client/webconsole/new-console-output/constants");
 const { reducers } = require("./reducers/index");
 const Services = require("Services");
 
 function configureStore() {
   const initialState = {
     prefs: new PrefState({
       logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1),
     }),
@@ -23,16 +29,39 @@ function configureStore() {
       network: Services.prefs.getBoolPref("devtools.webconsole.filter.network"),
       netxhr: Services.prefs.getBoolPref("devtools.webconsole.filter.netxhr"),
     })
   };
 
   return createStore(
     combineReducers(reducers),
     initialState,
-    applyMiddleware(thunk)
+    compose(applyMiddleware(thunk), enableBatching())
   );
 }
 
+/**
+ * A enhancer for the store to handle batched actions.
+ */
+function enableBatching() {
+  return next => (reducer, initialState, enhancer) => {
+    function batchingReducer(state, action) {
+      switch (action.type) {
+        case constants.BATCH_ACTIONS:
+          return action.actions.reduce(batchingReducer, state);
+        default:
+          return reducer(state, action);
+      }
+    }
+
+    if (typeof initialState === "function" && typeof enhancer === "undefined") {
+      enhancer = initialState;
+      initialState = undefined;
+    }
+
+    return next(batchingReducer, initialState, enhancer);
+  };
+}
+
 // Provide the store factory for test code so that each test is working with
 // its own instance.
 module.exports.configureStore = configureStore;
 
--- a/devtools/client/webconsole/new-console-output/test/actions/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/actions/filters.test.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-const actions = require("devtools/client/webconsole/new-console-output/actions/filters");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const {
   FILTER_TEXT_SET,
   FILTER_TOGGLE,
   FILTERS_CLEAR,
   MESSAGE_LEVEL
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 const expect = require("expect");
--- a/devtools/client/webconsole/new-console-output/test/actions/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/actions/messages.test.js
@@ -21,47 +21,64 @@ describe("Message actions:", () => {
   });
 
   describe("messageAdd", () => {
     it("dispatches expected action given a packet", () => {
       const packet = stubPackets.get("console.log('foobar', 'test')");
       const store = mockStore({});
       store.dispatch(actions.messageAdd(packet));
 
+      const actualActions = store.getActions();
+      expect(actualActions.length).toEqual(1);
+
+      const addAction = actualActions[0];
+      const {message} = addAction;
+      const expectedAction = {
+        type: constants.MESSAGE_ADD,
+        message: stubPreparedMessages.get("console.log('foobar', 'test')")
+      };
+      expect(message.toJS()).toEqual(expectedAction.message.toJS());
+    });
+
+    it("dispatches expected actions given a console.clear packet", () => {
+      const packet = stubPackets.get("console.clear()");
+      const store = mockStore({});
+      store.dispatch(actions.messageAdd(packet));
+
+      const actualActions = store.getActions();
+      expect(actualActions.length).toEqual(1);
+
+      const [clearAction, addAction] = actualActions[0].actions;
+      expect(clearAction.type).toEqual(constants.MESSAGES_CLEAR);
+
+      const {message} = addAction;
+      const expectedAction = {
+        type: constants.MESSAGE_ADD,
+        message: stubPreparedMessages.get("console.clear()")
+      };
+      expect(addAction.type).toEqual(constants.MESSAGE_ADD);
+      expect(message.toJS()).toEqual(expectedAction.message.toJS());
+    });
+
+    it("dispatches expected action given a console.table packet", () => {
+      const packet = stubPackets.get("console.table(['a', 'b', 'c'])");
+      const store = mockStore({});
+      store.dispatch(actions.messageAdd(packet));
+
       const expectedActions = store.getActions();
       expect(expectedActions.length).toEqual(1);
 
       const addAction = expectedActions[0];
       const {message} = addAction;
       const expected = {
         type: constants.MESSAGE_ADD,
-        message: stubPreparedMessages.get("console.log('foobar', 'test')")
+        message: stubPreparedMessages.get("console.table(['a', 'b', 'c'])")
       };
       expect(message.toJS()).toEqual(expected.message.toJS());
     });
-
-    it("dispatches expected actions given a console.clear packet", () => {
-      const packet = stubPackets.get("console.clear()");
-      const store = mockStore({});
-      store.dispatch(actions.messageAdd(packet));
-
-      const expectedActions = store.getActions();
-      expect(expectedActions.length).toEqual(2);
-
-      const [clearAction, addAction] = expectedActions;
-      expect(clearAction.type).toEqual(constants.MESSAGES_CLEAR);
-
-      const {message} = addAction;
-      const expected = {
-        type: constants.MESSAGE_ADD,
-        message: stubPreparedMessages.get("console.clear()")
-      };
-      expect(addAction.type).toEqual(constants.MESSAGE_ADD);
-      expect(message.toJS()).toEqual(expected.message.toJS());
-    });
   });
 
   describe("messagesClear", () => {
     it("creates expected action", () => {
       const action = actions.messagesClear();
       const expected = {
         type: constants.MESSAGES_CLEAR,
       };
--- a/devtools/client/webconsole/new-console-output/test/actions/ui.test.js
+++ b/devtools/client/webconsole/new-console-output/test/actions/ui.test.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-const actions = require("devtools/client/webconsole/new-console-output/actions/ui");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const {
   FILTER_BAR_TOGGLE
 } = require("devtools/client/webconsole/new-console-output/constants");
 
 const expect = require("expect");
 
 describe("UI actions:", () => {
   describe("filterBarToggle", () => {
--- a/devtools/client/webconsole/new-console-output/test/chrome/test_render_perf.html
+++ b/devtools/client/webconsole/new-console-output/test/chrome/test_render_perf.html
@@ -53,17 +53,17 @@ function timeit(cb) {
     cb();
     let elapsed = performance.now() - start;
     resolve(elapsed / 1000);
   });
 }
 
 window.onload = Task.async(function* () {
   const { configureStore } = browserRequire("devtools/client/webconsole/new-console-output/store");
-  const { filterTextSet, filtersClear } = browserRequire("devtools/client/webconsole/new-console-output/actions/filters");
+  const { filterTextSet, filtersClear } = browserRequire("devtools/client/webconsole/new-console-output/actions/index");
   const NewConsoleOutputWrapper = browserRequire("devtools/client/webconsole/new-console-output/new-console-output-wrapper");
   const wrapper = new NewConsoleOutputWrapper(document.querySelector("#output"), {});
 
   const store = configureStore();
 
   let time = yield timeit(() => {
     testPackets.forEach((message) => {
       wrapper.dispatchMessageAdd(message);
--- a/devtools/client/webconsole/new-console-output/test/components/network-event-message.test.js
+++ b/devtools/client/webconsole/new-console-output/test/components/network-event-message.test.js
@@ -24,42 +24,39 @@ describe("NetworkEventMessage component:
     it("renders as expected", () => {
       const message = stubPreparedMessages.get("GET request");
       const wrapper = render(NetworkEventMessage({ message, onViewSourceInDebugger, openNetworkPanel, openLink }));
 
       expect(wrapper.find(".message-body .method").text()).toBe("GET");
       expect(wrapper.find(".message-body .xhr").length).toBe(0);
       expect(wrapper.find(".message-body .url").length).toBe(1);
       expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
-      expect(wrapper.find(".message-body .status").length).toBe(1);
       expect(wrapper.find("div.message.cm-s-mozilla span.message-body.devtools-monospace").length).toBe(1);
     });
   });
 
   describe("XHR GET request", () => {
     it("renders as expected", () => {
       const message = stubPreparedMessages.get("XHR GET request");
       const wrapper = render(NetworkEventMessage({ message, onViewSourceInDebugger, openNetworkPanel, openLink }));
 
       expect(wrapper.find(".message-body .method").text()).toBe("GET");
       expect(wrapper.find(".message-body .xhr").length).toBe(1);
       expect(wrapper.find(".message-body .xhr").text()).toBe("XHR");
       expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
-      expect(wrapper.find(".message-body .status").length).toBe(1);
       expect(wrapper.find("div.message.cm-s-mozilla span.message-body.devtools-monospace").length).toBe(1);
     });
   });
 
   describe("XHR POST request", () => {
     it("renders as expected", () => {
       const message = stubPreparedMessages.get("XHR POST request");
       const wrapper = render(NetworkEventMessage({ message, onViewSourceInDebugger, openNetworkPanel, openLink }));
 
       expect(wrapper.find(".message-body .method").text()).toBe("POST");
       expect(wrapper.find(".message-body .xhr").length).toBe(1);
       expect(wrapper.find(".message-body .xhr").text()).toBe("XHR");
       expect(wrapper.find(".message-body .url").length).toBe(1);
       expect(wrapper.find(".message-body .url").text()).toBe(EXPECTED_URL);
-      expect(wrapper.find(".message-body .status").length).toBe(1);
       expect(wrapper.find("div.message.cm-s-mozilla span.message-body.devtools-monospace").length).toBe(1);
     });
   });
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient.js
@@ -0,0 +1,9 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+class ObjectClient {
+}
+
+module.exports = ObjectClient;
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js
@@ -41,16 +41,27 @@ foo()
 
 consoleApi.set("console.time('bar')", {
   keys: ["console.time('bar')", "console.timeEnd('bar')"],
   code: `
 console.time("bar");
 console.timeEnd("bar");
 `});
 
+consoleApi.set("console.table('bar')", {
+  keys: ["console.table('bar')"],
+  code: `
+console.table('bar');
+`});
+
+consoleApi.set("console.table(['a', 'b', 'c'])", {
+  keys: ["console.table(['a', 'b', 'c'])"],
+  code: `
+console.table(['a', 'b', 'c']);
+`});
 // Evaluation Result
 
 const evaluationResultCommands = [
   "new Date(0)",
   "asdf()"
 ];
 
 let evaluationResult = new Map(evaluationResultCommands.map(cmd => [cmd, cmd]));
--- a/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
+++ b/devtools/client/webconsole/new-console-output/test/fixtures/stubs/consoleApi.js
@@ -326,28 +326,85 @@ stubPreparedMessages.set("console.time('
 }));
 
 stubPreparedMessages.set("console.timeEnd('bar')", new ConsoleMessage({
 	"id": "1",
 	"allowRepeating": true,
 	"source": "console-api",
 	"type": "timeEnd",
 	"level": "log",
-	"messageText": "bar: 1.63ms",
+	"messageText": "bar: 1.81ms",
 	"parameters": null,
 	"repeat": 1,
-	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 1.63ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)\",\"line\":3,\"column\":1}}",
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"timeEnd\",\"level\":\"log\",\"messageText\":\"bar: 1.81ms\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)\",\"line\":3,\"column\":1}}",
 	"stacktrace": null,
 	"frame": {
 		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.time(%27bar%27)",
 		"line": 3,
 		"column": 1
 	}
 }));
 
+stubPreparedMessages.set("console.table('bar')", new ConsoleMessage({
+	"id": "1",
+	"allowRepeating": true,
+	"source": "console-api",
+	"type": "log",
+	"level": "log",
+	"messageText": null,
+	"parameters": [
+		"bar"
+	],
+	"repeat": 1,
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"bar\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%27bar%27)\",\"line\":2,\"column\":1}}",
+	"stacktrace": null,
+	"frame": {
+		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%27bar%27)",
+		"line": 2,
+		"column": 1
+	}
+}));
+
+stubPreparedMessages.set("console.table(['a', 'b', 'c'])", new ConsoleMessage({
+	"id": "1",
+	"allowRepeating": true,
+	"source": "console-api",
+	"type": "table",
+	"level": "log",
+	"messageText": null,
+	"parameters": [
+		{
+			"type": "object",
+			"actor": "server1.conn14.child1/obj31",
+			"class": "Array",
+			"extensible": true,
+			"frozen": false,
+			"sealed": false,
+			"ownPropertyLength": 4,
+			"preview": {
+				"kind": "ArrayLike",
+				"length": 3,
+				"items": [
+					"a",
+					"b",
+					"c"
+				]
+			}
+		}
+	],
+	"repeat": 1,
+	"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"table\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"object\",\"actor\":\"server1.conn14.child1/obj31\",\"class\":\"Array\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":4,\"preview\":{\"kind\":\"ArrayLike\",\"length\":3,\"items\":[\"a\",\"b\",\"c\"]}}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)\",\"line\":2,\"column\":1}}",
+	"stacktrace": null,
+	"frame": {
+		"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)",
+		"line": 2,
+		"column": 1
+	}
+}));
+
 
 stubPackets.set("console.log('foobar', 'test')", {
 	"from": "server1.conn0.child1/consoleActor2",
 	"type": "consoleAPICall",
 	"message": {
 		"arguments": [
 			"foobar",
 			"test"
@@ -365,17 +422,17 @@ stubPackets.set("console.log('foobar', '
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329261562,
+		"timeStamp": 1474757913492,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log(undefined)", {
 	"from": "server1.conn1.child1/consoleActor2",
@@ -399,17 +456,17 @@ stubPackets.set("console.log(undefined)"
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329262588,
+		"timeStamp": 1474757916196,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.warn('danger, will robinson!')", {
 	"from": "server1.conn2.child1/consoleActor2",
@@ -431,17 +488,17 @@ stubPackets.set("console.warn('danger, w
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329263650,
+		"timeStamp": 1474757918499,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log(NaN)", {
 	"from": "server1.conn3.child1/consoleActor2",
@@ -465,17 +522,17 @@ stubPackets.set("console.log(NaN)", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329264822,
+		"timeStamp": 1474757920577,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log(null)", {
 	"from": "server1.conn4.child1/consoleActor2",
@@ -499,17 +556,17 @@ stubPackets.set("console.log(null)", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329265855,
+		"timeStamp": 1474757922439,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log('鼬')", {
 	"from": "server1.conn5.child1/consoleActor2",
@@ -531,17 +588,17 @@ stubPackets.set("console.log('鼬')", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329266922,
+		"timeStamp": 1474757924400,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.clear()", {
 	"from": "server1.conn6.child1/consoleActor2",
@@ -560,17 +617,17 @@ stubPackets.set("console.clear()", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1474329267971,
+		"timeStamp": 1474757926626,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.count('bar')", {
@@ -595,17 +652,17 @@ stubPackets.set("console.count('bar')", 
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1474329269084,
+		"timeStamp": 1474757929281,
 		"timer": null,
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.assert(false, {message: 'foobar'})", {
@@ -649,17 +706,17 @@ stubPackets.set("console.assert(false, {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329270125,
+		"timeStamp": 1474757931800,
 		"timer": null,
 		"stacktrace": [
 			{
 				"columnNumber": 27,
 				"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.assert(false%2C%20%7Bmessage%3A%20%27foobar%27%7D)",
 				"functionName": "triggerPacket",
 				"language": 2,
 				"lineNumber": 1
@@ -690,17 +747,17 @@ stubPackets.set("console.log('hello \nfr
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329271256,
+		"timeStamp": 1474757936217,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.log('úṇĩçödê țĕșť')", {
 	"from": "server1.conn10.child1/consoleActor2",
@@ -722,17 +779,17 @@ stubPackets.set("console.log('úṇĩçödê țĕșť')", {
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
 		"styles": [],
-		"timeStamp": 1474329272298,
+		"timeStamp": 1474757938480,
 		"timer": null,
 		"workerType": "none",
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.trace()", {
 	"from": "server1.conn11.child1/consoleActor2",
@@ -751,17 +808,17 @@ stubPackets.set("console.trace()", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1474329273375,
+		"timeStamp": 1474757940569,
 		"timer": null,
 		"stacktrace": [
 			{
 				"columnNumber": 3,
 				"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.trace()",
 				"functionName": "testStacktraceFiltering",
 				"language": 2,
 				"lineNumber": 3
@@ -806,20 +863,20 @@ stubPackets.set("console.time('bar')", {
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1474329274410,
+		"timeStamp": 1474757942740,
 		"timer": {
 			"name": "bar",
-			"started": 618.57
+			"started": 1220.705
 		},
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
 stubPackets.set("console.timeEnd('bar')", {
@@ -841,24 +898,105 @@ stubPackets.set("console.timeEnd('bar')"
 			"appId": 0,
 			"firstPartyDomain": "",
 			"inIsolatedMozBrowser": false,
 			"privateBrowsingId": 0,
 			"signedPkg": "",
 			"userContextId": 0
 		},
 		"private": false,
-		"timeStamp": 1474329274411,
+		"timeStamp": 1474757942742,
 		"timer": {
-			"duration": 1.3249999999999318,
+			"duration": 1.8100000000001728,
 			"name": "bar"
 		},
 		"workerType": "none",
 		"styles": [],
 		"category": "webdev"
 	}
 });
 
+stubPackets.set("console.table('bar')", {
+	"from": "server1.conn13.child1/consoleActor2",
+	"type": "consoleAPICall",
+	"message": {
+		"arguments": [
+			"bar"
+		],
+		"columnNumber": 1,
+		"counter": null,
+		"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%27bar%27)",
+		"functionName": "triggerPacket",
+		"groupName": "",
+		"level": "table",
+		"lineNumber": 2,
+		"originAttributes": {
+			"addonId": "",
+			"appId": 0,
+			"firstPartyDomain": "",
+			"inIsolatedMozBrowser": false,
+			"privateBrowsingId": 0,
+			"signedPkg": "",
+			"userContextId": 0
+		},
+		"private": false,
+		"timeStamp": 1474757944789,
+		"timer": null,
+		"workerType": "none",
+		"styles": [],
+		"category": "webdev"
+	}
+});
+
+stubPackets.set("console.table(['a', 'b', 'c'])", {
+	"from": "server1.conn14.child1/consoleActor2",
+	"type": "consoleAPICall",
+	"message": {
+		"arguments": [
+			{
+				"type": "object",
+				"actor": "server1.conn14.child1/obj31",
+				"class": "Array",
+				"extensible": true,
+				"frozen": false,
+				"sealed": false,
+				"ownPropertyLength": 4,
+				"preview": {
+					"kind": "ArrayLike",
+					"length": 3,
+					"items": [
+						"a",
+						"b",
+						"c"
+					]
+				}
+			}
+		],
+		"columnNumber": 1,
+		"counter": null,
+		"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js?key=console.table(%5B%27a%27%2C%20%27b%27%2C%20%27c%27%5D)",
+		"functionName": "triggerPacket",
+		"groupName": "",
+		"level": "table",
+		"lineNumber": 2,
+		"originAttributes": {
+			"addonId": "",
+			"appId": 0,
+			"firstPartyDomain": "",
+			"inIsolatedMozBrowser": false,
+			"privateBrowsingId": 0,
+			"signedPkg": "",
+			"userContextId": 0
+		},
+		"private": false,
+		"timeStamp": 1474757946731,
+		"timer": null,
+		"workerType": "none",
+		"styles": [],
+		"category": "webdev"
+	}
+});
+
 
 module.exports = {
   stubPreparedMessages,
   stubPackets,
 }
\ No newline at end of file
--- a/devtools/client/webconsole/new-console-output/test/helpers.js
+++ b/devtools/client/webconsole/new-console-output/test/helpers.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 let ReactDOM = require("devtools/client/shared/vendor/react-dom");
 let React = require("devtools/client/shared/vendor/react");
 var TestUtils = React.addons.TestUtils;
 
-const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
 const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
 const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 
 /**
  * Prepare actions for use in testing.
  */
 function setupActions() {
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   !/devtools/client/framework/test/shared-head.js
+  test-console-table.html
   test-console.html
 
+[browser_webconsole_console_table.js]
 [browser_webconsole_init.js]
 [browser_webconsole_input_focus.js]
 [browser_webconsole_observer_notifications.js]
 [browser_webconsole_vview_close_on_esc_key.js]
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_table.js
@@ -0,0 +1,173 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check console.table calls with all the test cases shown
+// in the MDN doc (https://developer.mozilla.org/en-US/docs/Web/API/Console/table)
+
+const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console-table.html";
+
+add_task(function* () {
+  let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
+  let hud = toolbox.getCurrentPanel().hud;
+
+  function Person(firstName, lastName) {
+    this.firstName = firstName;
+    this.lastName = lastName;
+  }
+
+  const testCases = [{
+    info: "Testing when data argument is an array",
+    input: ["apples", "oranges", "bananas"],
+    expected: {
+      columns: ["(index)", "Values"],
+      rows: [
+        ["0", "apples"],
+        ["1", "oranges"],
+        ["2", "bananas"],
+      ]
+    }
+  }, {
+    info: "Testing when data argument is an object",
+    input: new Person("John", "Smith"),
+    expected: {
+      columns: ["(index)", "Values"],
+      rows: [
+        ["firstName", "John"],
+        ["lastName", "Smith"],
+      ]
+    }
+  }, {
+    info: "Testing when data argument is an array of arrays",
+    input: [["Jane", "Doe"], ["Emily", "Jones"]],
+    expected: {
+      columns: ["(index)", "0", "1"],
+      rows: [
+        ["0", "Jane", "Doe"],
+        ["1", "Emily", "Jones"],
+      ]
+    }
+  }, {
+    info: "Testing when data argument is an array of objects",
+    input: [
+      new Person("Jack", "Foo"),
+      new Person("Emma", "Bar"),
+      new Person("Michelle", "Rax"),
+    ],
+    expected: {
+      columns: ["(index)", "firstName", "lastName"],
+      rows: [
+        ["0", "Jack", "Foo"],
+        ["1", "Emma", "Bar"],
+        ["2", "Michelle", "Rax"],
+      ]
+    }
+  }, {
+    info: "Testing when data argument is an object whose properties are objects",
+    input: {
+      father: new Person("Darth", "Vader"),
+      daughter: new Person("Leia", "Organa"),
+      son: new Person("Luke", "Skywalker"),
+    },
+    expected: {
+      columns: ["(index)", "firstName", "lastName"],
+      rows: [
+        ["father", "Darth", "Vader"],
+        ["daughter", "Leia", "Organa"],
+        ["son", "Luke", "Skywalker"],
+      ]
+    }
+  }, {
+    info: "Testing when data argument is a Set",
+    input: new Set(["a", "b", "c"]),
+    expected: {
+      columns: ["(iteration index)", "Values"],
+      rows: [
+        ["0", "a"],
+        ["1", "b"],
+        ["2", "c"],
+      ]
+    }
+  }, {
+    info: "Testing when data argument is a Map",
+    input: new Map([["key-a", "value-a"], ["key-b", "value-b"]]),
+    expected: {
+      columns: ["(iteration index)", "Key", "Values"],
+      rows: [
+        ["0", "key-a", "value-a"],
+        ["1", "key-b", "value-b"],
+      ]
+    }
+  }, {
+    info: "Testing restricting the columns displayed",
+    input: [
+      new Person("Sam", "Wright"),
+      new Person("Elena", "Bartz"),
+    ],
+    headers: ["firstName"],
+    expected: {
+      columns: ["(index)", "firstName"],
+      rows: [
+        ["0", "Sam"],
+        ["1", "Elena"],
+      ]
+    }
+  }];
+
+  yield ContentTask.spawn(gBrowser.selectedBrowser, testCases, function (tests) {
+    tests.forEach((test) => {
+      content.wrappedJSObject.doConsoleTable(test.input, test.headers);
+    });
+  });
+
+  let nodes = [];
+  for (let testCase of testCases) {
+    let node = yield waitFor(
+      () => findConsoleTable(hud.ui.experimentalOutputNode, testCases.indexOf(testCase))
+    );
+    nodes.push(node);
+  }
+
+  let consoleTableNodes = hud.ui.experimentalOutputNode.querySelectorAll(
+    ".message .new-consoletable");
+
+  is(consoleTableNodes.length, testCases.length,
+    "console has the expected number of consoleTable items");
+
+  testCases.forEach((testCase, index) => {
+    info(testCase.info);
+
+    let node = nodes[index];
+    let columns = Array.from(node.querySelectorAll("thead th"));
+    let rows = Array.from(node.querySelectorAll("tbody tr"));
+
+    is(
+      JSON.stringify(testCase.expected.columns),
+      JSON.stringify(columns.map(column => column.textContent)),
+      "table has the expected columns"
+    );
+
+    is(testCase.expected.rows.length, rows.length,
+      "table has the expected number of rows");
+
+    testCase.expected.rows.forEach((expectedRow, rowIndex) => {
+      let row = rows[rowIndex];
+      let cells = row.querySelectorAll("td");
+      is(expectedRow.length, cells.length, "row has the expected number of cells");
+
+      expectedRow.forEach((expectedCell, cellIndex) => {
+        let cell = cells[cellIndex];
+        is(expectedCell, cell.textContent, "cell has the expected content");
+      });
+    });
+  });
+});
+
+function findConsoleTable(node, index) {
+  let condition = node.querySelector(
+    `.message:nth-of-type(${index + 1}) .new-consoletable`);
+  return condition;
+}
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_input_focus.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_input_focus.js
@@ -2,17 +2,56 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Tests that the input field is focused when the console is opened.
 
 "use strict";
 
-const TEST_URI = "data:text/html;charset=utf-8,Test input focus";
+const TEST_URI =
+  `data:text/html;charset=utf-8,Test input focused
+  <script>
+    console.log("console message 1");
+  </script>`;
 
 add_task(function* () {
   let hud = yield openNewTabAndConsole(TEST_URI);
   hud.jsterm.clearOutput();
 
   let inputNode = hud.jsterm.inputNode;
-  ok(inputNode.getAttribute("focused"), "input node is focused");
+  ok(inputNode.getAttribute("focused"), "input node is focused after output is cleared");
+
+  ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+    content.wrappedJSObject.console.log("console message 2");
+  });
+  let msg = yield waitFor(() => findMessage(hud, "console message 2"));
+  let outputItem = msg.querySelector(".message-body");
+
+  inputNode = hud.jsterm.inputNode;
+  ok(inputNode.getAttribute("focused"), "input node is focused, first");
+
+  yield waitForBlurredInput(inputNode);
+
+  EventUtils.sendMouseEvent({type: "click"}, hud.outputNode);
+  ok(inputNode.getAttribute("focused"), "input node is focused, second time");
+
+  yield waitForBlurredInput(inputNode);
+
+  info("Setting a text selection and making sure a click does not re-focus");
+  let selection = hud.iframeWindow.getSelection();
+  selection.selectAllChildren(outputItem);
+
+  EventUtils.sendMouseEvent({type: "click"}, hud.outputNode);
+  ok(!inputNode.getAttribute("focused"),
+    "input node focused after text is selected");
 });
+
+function waitForBlurredInput(inputNode) {
+  return new Promise(resolve => {
+    let lostFocus = () => {
+      ok(!inputNode.getAttribute("focused"), "input node is not focused");
+      resolve();
+    };
+    inputNode.addEventListener("blur", lostFocus, { once: true });
+    document.getElementById("urlbar").click();
+  });
+}
--- a/devtools/client/webconsole/new-console-output/test/mochitest/head.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/head.js
@@ -86,17 +86,17 @@ function waitForMessages({ hud, messages
  * @param number interval [optional]
  *        How often the predicate is invoked, in milliseconds.
  * @return object
  *         A promise that is resolved with the result of the condition.
  */
 function* waitFor(condition, message = "waitFor", interval = 100, maxTries = 50) {
   return new Promise(resolve => {
     BrowserTestUtils.waitForCondition(condition, message, interval, maxTries)
-      .then(resolve(condition()));
+      .then(() => resolve(condition()));
   });
 }
 
 /**
  * Find a message in the output.
  *
  * @param object hud
  *        The web console.
@@ -105,10 +105,10 @@ function* waitFor(condition, message = "
  * @param selector [optional]
  *        The selector to use in finding the message.
  */
 function findMessage(hud, text, selector = ".message") {
   const elements = Array.prototype.filter.call(
     hud.ui.experimentalOutputNode.querySelectorAll(selector),
     (el) => el.textContent.includes(text)
   );
-  return elements.pop();
+  return elements.length > 0 ? elements.pop() : false;
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/test-console-table.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <title>Simple webconsole test page</title>
+  </head>
+  <body>
+    <p>console.table() test page</p>
+    <script>
+      function doConsoleTable(data, constrainedHeaders = null) {
+        if (constrainedHeaders) {
+          console.table(data, constrainedHeaders);
+        } else {
+          console.table(data);
+        }
+      }
+    </script>
+  </body>
+</html>
--- a/devtools/client/webconsole/new-console-output/test/requireHelper.js
+++ b/devtools/client/webconsole/new-console-output/test/requireHelper.js
@@ -25,10 +25,12 @@ requireHacker.global_hook("default", pat
   switch (path) {
     case "devtools/client/webconsole/utils":
       return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/WebConsoleUtils")`;
     case "devtools/shared/l10n":
       return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/LocalizationHelper")`;
     case "Services":
     case "Services.default":
       return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/Services")`;
+    case "devtools/shared/client/main":
+      return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/ObjectClient")`;
   }
 });
--- a/devtools/client/webconsole/new-console-output/test/store/filters.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/filters.test.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const expect = require("expect");
 
-const actions = require("devtools/client/webconsole/new-console-output/actions/filters");
-const { messageAdd } = require("devtools/client/webconsole/new-console-output/actions/messages");
+const actions = require("devtools/client/webconsole/new-console-output/actions/index");
+const { messageAdd } = require("devtools/client/webconsole/new-console-output/actions/index");
 const { ConsoleCommand } = require("devtools/client/webconsole/new-console-output/types");
 const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
 const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
 const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
 const { stubPackets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
 
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -6,16 +6,19 @@ const {
   getAllMessages,
   getAllMessagesUiById,
 } = require("devtools/client/webconsole/new-console-output/selectors/messages");
 const {
   setupActions,
   setupStore
 } = require("devtools/client/webconsole/new-console-output/test/helpers");
 const { stubPackets, stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
+const {
+  MESSAGE_TYPE,
+} = require("devtools/client/webconsole/new-console-output/constants");
 
 const expect = require("expect");
 
 describe("Message reducer:", () => {
   let actions;
 
   before(() => {
     actions = setupActions();
@@ -112,16 +115,27 @@ describe("Message reducer:", () => {
       const { dispatch, getState } = setupStore([]);
 
       const message = stubPackets.get("console.time('bar')");
       dispatch(actions.messageAdd(message));
 
       const messages = getAllMessages(getState());
       expect(messages.size).toBe(0);
     });
+
+    it("adds console.table call with unsupported type as console.log", () => {
+      const { dispatch, getState } = setupStore([]);
+
+      const packet = stubPackets.get("console.table('bar')");
+      dispatch(actions.messageAdd(packet));
+
+      const messages = getAllMessages(getState());
+      const tableMessage = messages.last();
+      expect(tableMessage.level).toEqual(MESSAGE_TYPE.LOG);
+    });
   });
 
   describe("messagesUiById", () => {
     it("opens console.trace messages when they are added", () => {
       const { dispatch, getState } = setupStore([]);
 
       const message = stubPackets.get("console.trace()");
       dispatch(actions.messageAdd(message));
--- a/devtools/client/webconsole/new-console-output/utils/messages.js
+++ b/devtools/client/webconsole/new-console-output/utils/messages.js
@@ -76,16 +76,29 @@ function transformPacket(packet) {
             // if corresponding console.time() was called before.
             let duration = Math.round(timer.duration * 100) / 100;
             messageText = l10n.getFormatStr("timeEnd", [timer.name, duration]);
           } else {
             // If the `timer` property does not exists, we don't output anything.
             type = MESSAGE_TYPE.NULL_MESSAGE;
           }
           break;
+        case "table":
+          const supportedClasses = [
+            "Array", "Object", "Map", "Set", "WeakMap", "WeakSet"];
+          if (
+            !Array.isArray(parameters) ||
+            parameters.length === 0 ||
+            !supportedClasses.includes(parameters[0].class)
+          ) {
+            // If the class of the first parameter is not supported,
+            // we handle the call as a simple console.log
+            type = "log";
+          }
+          break;
       }
 
       const frame = message.filename ? {
         source: message.filename,
         line: message.lineNumber,
         column: message.columnNumber,
       } : null;
 
--- a/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
+++ b/devtools/client/webconsole/test/browser_webconsole_block_mixedcontent_securityerrors.js
@@ -55,17 +55,19 @@ add_task(function* () {
   yield testClickOpenNewTab(hud, results2[0]);
 });
 
 function pushPrefEnv() {
   let deferred = promise.defer();
   let options = {
     "set": [
       ["security.mixed_content.block_active_content", true],
-      ["security.mixed_content.block_display_content", true]
+      ["security.mixed_content.block_display_content", true],
+      ["security.mixed_content.use_hsts", false],
+      ["security.mixed_content.send_hsts_priming", false],
     ]
   };
   SpecialPowers.pushPrefEnv(options, deferred.resolve);
   return deferred.promise;
 }
 
 function mixedContentOverrideTest2(hud, browser) {
   let deferred = promise.defer();
--- a/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
+++ b/devtools/client/webconsole/test/browser_webconsole_bug_632817.js
@@ -89,16 +89,23 @@ function testXhrGet() {
 
 function testXhrWarn() {
   // Start the XMLHttpRequest() warn test.
   ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
     content.wrappedJSObject.testXhrWarn();
   });
 
   let lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
+  if (lastRequest.request.method == "HEAD") {
+    // in non-e10s, we get the HEAD request that priming sends, so make sure
+    // a priming request should be sent, and then get the actual request
+    is(Services.prefs.getBoolPref("security.mixed_content.send_hsts_priming"),
+        true, "Found HSTS Priming Request");
+    lastRequest = yield waitForFinishedRequest(XHR_WARN_REQUEST_PREDICATE);
+  }
 
   ok(lastRequest, "testXhrWarn() was logged");
   is(lastRequest.request.method, "GET", "Method is correct");
   ok(lastRequest.isXHR, "It's an XHR request");
   is(lastRequest.securityInfo, "insecure", "It's an insecure request");
 }
 
 function testXhrPost() {
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -489,16 +489,20 @@ WebConsoleFrame.prototype = {
     // the returned promise because the console panel needs to be attached
     // to the toolbox before the web-console-created event is receieved.
     let notifyObservers = () => {
       let id = WebConsoleUtils.supportsString(this.hudId);
       Services.obs.notifyObservers(id, "web-console-created", null);
     };
     allReady.then(notifyObservers, notifyObservers);
 
+    if (this.NEW_CONSOLE_OUTPUT_ENABLED) {
+      allReady.then(this.newConsoleOutput.init);
+    }
+
     return allReady;
   },
 
   /**
    * Connect to the server using the remote debugging protocol.
    *
    * @private
    * @return object
@@ -3268,16 +3272,23 @@ WebConsoleConnectionProxy.prototype = {
     let messageNodes = this.webConsoleFrame.experimentalOutputNode.querySelectorAll(".message");
     this.webConsoleFrame.emit("new-messages", {
       response: packet,
       node: messageNodes[messageNodes.length - 1],
     });
   },
 
   /**
+   * Batched dispatch of messages.
+   */
+  dispatchMessagesAdd: function(packets) {
+    this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
+  },
+
+  /**
    * The "cachedMessages" response handler.
    *
    * @private
    * @param object response
    *        The JSON response object received from the server.
    */
   _onCachedMessages: function (response) {
     if (response.error) {
@@ -3296,19 +3307,17 @@ WebConsoleConnectionProxy.prototype = {
     let messages =
       response.messages.concat(...this.webConsoleClient.getNetworkEvents());
     messages.sort((a, b) => a.timeStamp - b.timeStamp);
 
     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
       // Filter out CSS page errors.
       messages = messages.filter(message => !(message._type == "PageError"
           && Utils.categoryForScriptError(message) === CATEGORY_CSS));
-      for (let packet of messages) {
-        this.dispatchMessageAdd(packet);
-      }
+      this.dispatchMessagesAdd(messages);
     } else {
       this.webConsoleFrame.displayCachedMessages(messages);
       if (!this._hasNativeConsoleAPI) {
         this.webConsoleFrame.logWarningAboutReplacedAPI();
       }
     }
 
     this.connected = true;
--- a/devtools/client/webide/test/test_manifestUpdate.html
+++ b/devtools/client/webide/test/test_manifestUpdate.html
@@ -27,18 +27,21 @@
 
             function isProjectMarkedAsValid() {
               let details = win.frames[1];
               return !details.document.body.classList.contains("error");
             }
 
             let packagedAppLocation = getTestFilePath("app");
 
+            let onValidated = waitForUpdate(win, "project-validated");
+            let onDetails = waitForUpdate(win, "details");
             yield winProject.projectList.importPackagedApp(packagedAppLocation);
-            yield waitForUpdate(win, "details");
+            yield onValidated;
+            yield onDetails;
 
             let project = win.AppManager.selectedProject;
 
             ok("name" in project.manifest, "manifest includes name");
             is(project.name, project.manifest.name, "Display name uses manifest name");
             ok(isProjectMarkedAsValid(), "project is marked as valid");
 
             // Change the name
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -375,17 +375,18 @@ exports.defineLazyGetter(this, "NetworkH
  *        An object with the following optional properties:
  *        - loadFromCache: if false, will bypass the cache and
  *          always load fresh from the network (default: true)
  *        - policy: the nsIContentPolicy type to apply when fetching the URL
  *                  (only works when loading from system principal)
  *        - window: the window to get the loadGroup from
  *        - charset: the charset to use if the channel doesn't provide one
  *        - principal: the principal to use, if omitted, the request is loaded
- *                     with the system principal
+ *                     with a codebase principal corresponding to the url being
+ *                     loaded, using the origin attributes of the window, if any.
  *        - cacheKey: when loading from cache, use this key to retrieve a cache
  *                    specific to a given SHEntry. (Allows loading POST
  *                    requests from cache)
  * @returns Promise that resolves with an object with the following members on
  *          success:
  *           - content: the document at that URL, as a string,
  *           - contentType: the content type of the document
  *
@@ -521,61 +522,54 @@ function mainThreadFetch(aURL, aOptions 
  * Opens a channel for given URL. Tries a bit harder than NetUtil.newChannel.
  *
  * @param {String} url - The URL to open a channel for.
  * @param {Object} options - The options object passed to @method fetch.
  * @return {nsIChannel} - The newly created channel. Throws on failure.
  */
 function newChannelForURL(url, { policy, window, principal }) {
   var securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
-  if (window) {
-    // Respect private browsing.
-    var req = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                    .getInterface(Ci.nsIWebNavigation)
-                    .QueryInterface(Ci.nsIDocumentLoader)
-                    .loadGroup;
-    if (req) {
-      var nc = req.notificationCallbacks;
-      if (nc) {
-        try {
-          var lc = nc.getInterface(Ci.nsILoadContext);
-          if (lc) {
-            if (lc.usePrivateBrowsing) {
-              securityFlags |= Ci.nsILoadInfo.SEC_FORCE_PRIVATE_BROWSING;
-            }
-          }
-        } catch (ex) {}
-      }
-    }
+
+  let uri;
+  try {
+    uri = Services.io.newURI(url, null, null);
+  } catch (e) {
+    // In the xpcshell tests, the script url is the absolute path of the test
+    // file, which will make a malformed URI error be thrown. Add the file
+    // scheme to see if it helps.
+    uri = Services.io.newURI("file://" + url, null, null);
   }
-
   let channelOptions = {
     contentPolicyType: policy,
     securityFlags: securityFlags,
-    uri: url
+    uri: uri
   };
-  if (principal) {
-    // contentPolicyType is required when loading with a custom principal
-    if (!channelOptions.contentPolicyType) {
-      channelOptions.contentPolicyType = Ci.nsIContentPolicy.TYPE_OTHER;
+  let prin = principal;
+  if (!prin) {
+    let oa = {};
+    if (window) {
+      oa = window.document.nodePrincipal.originAttributes;
     }
-    channelOptions.loadingPrincipal = principal;
-  } else {
-    channelOptions.loadUsingSystemPrincipal = true;
+    prin = Services.scriptSecurityManager
+                   .createCodebasePrincipal(uri, oa);
   }
+  // contentPolicyType is required when specifying a principal
+  if (!channelOptions.contentPolicyType) {
+    channelOptions.contentPolicyType = Ci.nsIContentPolicy.TYPE_OTHER;
+  }
+  channelOptions.loadingPrincipal = prin;
 
   try {
     return NetUtil.newChannel(channelOptions);
   } catch (e) {
-    // In the xpcshell tests, the script url is the absolute path of the test
-    // file, which will make a malformed URI error be thrown. Add the file
-    // scheme to see if it helps.
-    channelOptions.uri = "file://" + url;
-
-    return NetUtil.newChannel(channelOptions);
+    // In xpcshell tests on Windows, nsExternalProtocolHandler::NewChannel()
+    // can throw NS_ERROR_UNKNOWN_PROTOCOL if the external protocol isn't
+    // supported by Windows, so we also need to handle the exception here if
+    // parsing the URL above doesn't throw.
+    return newChannelForURL("file://" + url, { policy, window, principal });
   }
 }
 
 // Fetch is defined differently depending on whether we are on the main thread
 // or a worker thread.
 if (!this.isWorker) {
   exports.fetch = mainThreadFetch;
 } else {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4981,20 +4981,20 @@ nsDocShell::DisplayLoadError(nsresult aE
           UsePrivateBrowsing() ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
         bool isStsHost = false;
         bool isPinnedHost = false;
         if (XRE_IsParentProcess()) {
           nsCOMPtr<nsISiteSecurityService> sss =
             do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
           NS_ENSURE_SUCCESS(rv, rv);
           rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI,
-                                flags, &isStsHost);
+                                flags, nullptr, &isStsHost);
           NS_ENSURE_SUCCESS(rv, rv);
           rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, aURI,
-                                flags, &isPinnedHost);
+                                flags, nullptr, &isPinnedHost);
           NS_ENSURE_SUCCESS(rv, rv);
         } else {
           mozilla::dom::ContentChild* cc =
             mozilla::dom::ContentChild::GetSingleton();
           mozilla::ipc::URIParams uri;
           SerializeURI(aURI, uri);
           cc->SendIsSecureURI(nsISiteSecurityService::HEADER_HSTS, uri, flags,
                               &isStsHost);
@@ -9857,16 +9857,35 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
     if (NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) {
       return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
     }
 
     return NS_ERROR_CONTENT_BLOCKED;
   }
 
+  // If HSTS priming was set by nsMixedContentBlocker::ShouldLoad, and we
+  // would block due to mixed content, go ahead and block here. If we try to
+  // proceed with priming, we will error out later on.
+  nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(context);
+  NS_ENSURE_TRUE(docShell, NS_OK);
+  if (docShell) {
+    nsIDocument* document = docShell->GetDocument();
+    NS_ENSURE_TRUE(document, NS_OK);
+
+    HSTSPrimingState state = document->GetHSTSPrimingStateForLocation(aURI);
+    if (state == HSTSPrimingState::eHSTS_PRIMING_BLOCK) {
+      // HSTS Priming currently disabled for InternalLoad, so we need to clear
+      // the location that was added by nsMixedContentBlocker::ShouldLoad
+      // Bug 1269815 will address images loaded via InternalLoad
+      document->ClearHSTSPrimingLocation(aURI);
+      return NS_ERROR_CONTENT_BLOCKED;
+    }
+  }
+
   nsCOMPtr<nsIPrincipal> principalToInherit = aPrincipalToInherit;
   //
   // Get a principal from the current document if necessary.  Note that we only
   // do this for URIs that inherit a security context and local file URIs;
   // in particular we do NOT do this for about:blank.  This way, random
   // about:blank loads that have no principal (which basically means they were
   // done by someone from chrome manually messing with our nsIWebNavigation
   // or by C++ setting document.location) don't get a funky principal.  If
@@ -10833,20 +10852,16 @@ nsDocShell::DoURILoad(nsIURI* aURI,
 
   if (inherit) {
     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
   if (isSandBoxed) {
     securityFlags |= nsILoadInfo::SEC_SANDBOXED;
   }
 
-  if (UsePrivateBrowsing()) {
-    securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
-  }
-
   nsCOMPtr<nsILoadInfo> loadInfo =
     (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) ?
       new LoadInfo(loadingWindow, triggeringPrincipal,
                    securityFlags) :
       new LoadInfo(loadingPrincipal, triggeringPrincipal, loadingNode,
                    securityFlags, aContentPolicyType);
 
   if (aPrincipalToInherit) {
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -16,17 +16,18 @@
 #include "nsIDocumentObserver.h"         // for typedef (nsUpdateType)
 #include "nsILoadGroup.h"                // for member (in nsCOMPtr)
 #include "nsINode.h"                     // for base class
 #include "nsIScriptGlobalObject.h"       // for member (in nsCOMPtr)
 #include "nsIServiceManager.h"
 #include "nsIUUIDGenerator.h"
 #include "nsPIDOMWindow.h"               // for use in inline functions
 #include "nsPropertyTable.h"             // for member
-#include "nsTHashtable.h"                // for member
+#include "nsDataHashtable.h"             // for member
+#include "nsURIHashKey.h"                // for member
 #include "mozilla/net/ReferrerPolicy.h"  // for member
 #include "nsWeakReference.h"
 #include "mozilla/UseCounter.h"
 #include "mozilla/WeakPtr.h"
 #include "Units.h"
 #include "nsContentListDeclarations.h"
 #include "nsExpirationTracker.h"
 #include "nsClassHashtable.h"
@@ -170,16 +171,23 @@ typedef CallbackObjectHolder<NodeFilter,
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
   DocumentFlavorSVG, // SVGDocument
   DocumentFlavorPlain, // Just a Document
 };
 
+// Enum for HSTS priming states
+enum class HSTSPrimingState {
+  eNO_HSTS_PRIMING = 0,    // don't do HSTS Priming
+  eHSTS_PRIMING_ALLOW = 1, // if HSTS priming fails, allow the load to proceed
+  eHSTS_PRIMING_BLOCK = 2  // if HSTS priming fails, block the load
+};
+
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
 // Window activation status
 #define NS_DOCUMENT_STATE_WINDOW_INACTIVE         NS_DEFINE_EVENT_STATE_MACRO(1)
 
 // Some function forward-declarations
@@ -360,16 +368,44 @@ public:
     return mUpgradeInsecureRequests;
   }
 
   void SetReferrer(const nsACString& aReferrer) {
     mReferrer = aReferrer;
   }
 
   /**
+   * Check to see if a subresource we want to load requires HSTS priming
+   * to be done.
+   */
+  HSTSPrimingState GetHSTSPrimingStateForLocation(nsIURI* aContentLocation) const
+  {
+    HSTSPrimingState state;
+    if (mHSTSPrimingURIList.Get(aContentLocation, &state)) {
+      return state;
+    }
+    return HSTSPrimingState::eNO_HSTS_PRIMING;
+  }
+
+  /**
+   * Add a subresource to the HSTS priming list. If this URI is
+   * not in the HSTS cache, it will trigger an HSTS priming request
+   * when we try to load it.
+   */
+  void AddHSTSPrimingLocation(nsIURI* aContentLocation, HSTSPrimingState aState)
+  {
+    mHSTSPrimingURIList.Put(aContentLocation, aState);
+  }
+
+  void ClearHSTSPrimingLocation(nsIURI* aContentLocation)
+  {
+    mHSTSPrimingURIList.Remove(aContentLocation);
+  }
+
+  /**
    * Set the principal responsible for this document.
    */
   virtual void SetPrincipal(nsIPrincipal *aPrincipal) = 0;
 
   /**
    * Return the LoadGroup for the document. May return null.
    */
   already_AddRefed<nsILoadGroup> GetDocumentLoadGroup() const
@@ -2763,26 +2799,16 @@ public:
   bool HasScriptsBlockedBySandbox();
 
   void ReportHasScrollLinkedEffect();
   bool HasScrollLinkedEffect() const
   {
     return mHasScrollLinkedEffect;
   }
 
-  bool MayHavePluginFramesForPrinting()
-  {
-    return mMayHavePluginFramesForPrinting;
-  }
-
-  void SetMayHavePluginFramesForPrinting()
-  {
-    mMayHavePluginFramesForPrinting = true;
-  }
-
 protected:
   bool GetUseCounter(mozilla::UseCounter aUseCounter)
   {
     return mUseCounters[aUseCounter];
   }
 
   void SetChildDocumentUseCounter(mozilla::UseCounter aUseCounter)
   {
@@ -2861,16 +2887,21 @@ protected:
   bool mReferrerPolicySet;
   ReferrerPolicyEnum mReferrerPolicy;
 
   bool mBlockAllMixedContent;
   bool mBlockAllMixedContentPreloads;
   bool mUpgradeInsecureRequests;
   bool mUpgradeInsecurePreloads;
 
+  // if nsMixedContentBlocker requires sending an HSTS priming request,
+  // temporarily store that in the document so that it can be propogated to the
+  // LoadInfo and eventually the HTTP Channel
+  nsDataHashtable<nsURIHashKey, HSTSPrimingState> mHSTSPrimingURIList;
+
   mozilla::WeakPtr<nsDocShell> mDocumentContainer;
 
   nsCString mCharacterSet;
   int32_t mCharacterSetSource;
 
   // This is just a weak pointer; the parent document owns its children.
   nsIDocument* mParentDocument;
 
@@ -3069,20 +3100,16 @@ protected:
   bool mGetUserFontSetCalled : 1;
 
   // Do we currently have an event posted to call FlushUserFontSet?
   bool mPostedFlushUserFontSet : 1;
 
   // True is document has ever been in a foreground window.
   bool mEverInForeground : 1;
 
-  // True if this document is a static clone for printing and may
-  // have elements referring to plugins in the original document.
-  bool mMayHavePluginFramesForPrinting : 1;
-
   enum Type {
     eUnknown, // should never be used
     eHTML,
     eXHTML,
     eGenericXML,
     eSVG,
     eXUL
   };
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -1760,17 +1760,18 @@ void
 nsINode::Before(const Sequence<OwningNodeOrString>& aNodes,
                 ErrorResult& aRv)
 {
   nsCOMPtr<nsINode> parent = GetParentNode();
   if (!parent) {
     return;
   }
 
-  nsINode* viablePreviousSibling = FindViablePreviousSibling(*this, aNodes);
+  nsCOMPtr<nsINode> viablePreviousSibling =
+    FindViablePreviousSibling(*this, aNodes);
 
   nsCOMPtr<nsINode> node =
     ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
   if (aRv.Failed()) {
     return;
   }
 
   viablePreviousSibling = viablePreviousSibling ?
@@ -1783,17 +1784,17 @@ void
 nsINode::After(const Sequence<OwningNodeOrString>& aNodes,
                ErrorResult& aRv)
 {
   nsCOMPtr<nsINode> parent = GetParentNode();
   if (!parent) {
     return;
   }
 
-  nsINode* viableNextSibling = FindViableNextSibling(*this, aNodes);
+  nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
 
   nsCOMPtr<nsINode> node =
     ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
   if (aRv.Failed()) {
     return;
   }
 
   parent->InsertBefore(*node, viableNextSibling, aRv);
@@ -1803,17 +1804,17 @@ void
 nsINode::ReplaceWith(const Sequence<OwningNodeOrString>& aNodes,
                      ErrorResult& aRv)
 {
   nsCOMPtr<nsINode> parent = GetParentNode();
   if (!parent) {
     return;
   }
 
-  nsINode* viableNextSibling = FindViableNextSibling(*this, aNodes);
+  nsCOMPtr<nsINode> viableNextSibling = FindViableNextSibling(*this, aNodes);
 
   nsCOMPtr<nsINode> node =
     ConvertNodesOrStringsIntoNode(aNodes, OwnerDoc(), aRv);
   if (aRv.Failed()) {
     return;
   }
 
   if (parent == GetParentNode()) {
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -2902,23 +2902,19 @@ nsObjectLoadingContent::CreateStaticClon
   aDest->mType = mType;
   nsObjectLoadingContent* thisObj = const_cast<nsObjectLoadingContent*>(this);
   if (thisObj->mPrintFrame.IsAlive()) {
     aDest->mPrintFrame = thisObj->mPrintFrame;
   } else {
     aDest->mPrintFrame = const_cast<nsObjectLoadingContent*>(this)->GetExistingFrame();
   }
 
-  nsCOMPtr<nsIContent> content =
-    do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
-  if (aDest->mPrintFrame) {
-    content->OwnerDoc()->SetMayHavePluginFramesForPrinting();
-  }
-
   if (mFrameLoader) {
+    nsCOMPtr<nsIContent> content =
+      do_QueryInterface(static_cast<nsIImageLoadingContent*>(aDest));
     nsFrameLoader* fl = nsFrameLoader::Create(content->AsElement(), false);
     if (fl) {
       aDest->mFrameLoader = fl;
       mFrameLoader->CreateStaticClone(fl);
     }
   }
 }
 
--- a/dom/base/test/bug704320.sjs
+++ b/dom/base/test/bug704320.sjs
@@ -189,16 +189,22 @@ function createPolicyTest(policy, option
                     onload="incrementLoad2(\'img\', 2);">\n\
             <img src="http://example.com/tests/dom/base/test/bug704320_counter.sjs?type=img"\n\
                     onload="incrementLoad2(\'img\', 2);">\n\
           </body>\n\
           </html>';
 }
 
 function handleRequest(request, response) {
+  if (request.method == 'HEAD') {
+    // respond to a HEAD request with a 418 so that we can easily distinguish
+    // HSTS priming responses and ignore them
+    response.setStatusLine('1.1', 418, "I'm a teapot");
+    return;
+  }
   var sharedKey = 'bug704320.sjs';
   var params = request.queryString.split('&');
   var action = params[0].split('=')[1];
 
   if (action === 'create-1st-level-iframe') {
     // ?action=create-1st-level-iframe&scheme-from=http&scheme-to=https&policy=origin
     var schemeFrom = params[1].split('=')[1];
     var schemeTo = params[2].split('=')[1];
--- a/dom/base/test/referrerHelper.js
+++ b/dom/base/test/referrerHelper.js
@@ -20,16 +20,19 @@ window.addEventListener("message", funct
 /**
  * helper to perform an XHR.
  */
 function doXHR(url, onSuccess, onFail) {
   var xhr = new XMLHttpRequest();
   xhr.onload = function () {
     if (xhr.status == 200) {
       onSuccess(xhr);
+    } else if (xhr.status == 418) {
+      // Ignore HSTS priming responses
+      return;
     } else {
       onFail(xhr);
     }
   };
   xhr.open('GET', url, true);
   xhr.send(null);
 }
 
--- a/dom/bindings/Errors.msg
+++ b/dom/bindings/Errors.msg
@@ -89,17 +89,17 @@ MSG_DEF(MSG_PROMISE_CAPABILITY_HAS_SOMET
 MSG_DEF(MSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
 MSG_DEF(MSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE, 0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
 MSG_DEF(MSG_PROMISE_ARG_NOT_ITERABLE, 1, JSEXN_TYPEERR, "{0} is not iterable")
 MSG_DEF(MSG_IS_NOT_PROMISE, 1, JSEXN_TYPEERR, "{0} is not a Promise")
 MSG_DEF(MSG_SW_INSTALL_ERROR, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} encountered an error during installation.")
 MSG_DEF(MSG_SW_SCRIPT_THREW, 2, JSEXN_TYPEERR, "ServiceWorker script at {0} for scope {1} threw an exception during script evaluation.")
 MSG_DEF(MSG_TYPEDARRAY_IS_SHARED, 1, JSEXN_TYPEERR, "{0} can't be a typed array on SharedArrayBuffer")
 MSG_DEF(MSG_CACHE_ADD_FAILED_RESPONSE, 3, JSEXN_TYPEERR, "Cache got {0} response with bad status {1} while trying to add request {2}")
-MSG_DEF(MSG_SW_UPDATE_BAD_REGISTRATION, 2, JSEXN_TYPEERR, "Failed to update the ServiceWorker for scope {0] because the registration has been {1} since the update was scheduled.")
+MSG_DEF(MSG_SW_UPDATE_BAD_REGISTRATION, 2, JSEXN_TYPEERR, "Failed to update the ServiceWorker for scope {0} because the registration has been {1} since the update was scheduled.")
 MSG_DEF(MSG_INVALID_DURATION_ERROR, 1, JSEXN_TYPEERR, "Invalid duration '{0}'.")
 MSG_DEF(MSG_INVALID_EASING_ERROR, 1, JSEXN_TYPEERR, "Invalid easing '{0}'.")
 MSG_DEF(MSG_INVALID_SPACING_MODE_ERROR, 1, JSEXN_TYPEERR, "Invalid spacing '{0}'.")
 MSG_DEF(MSG_USELESS_SETTIMEOUT, 1, JSEXN_TYPEERR, "Useless {0} call (missing quotes around argument?)")
 MSG_DEF(MSG_TOKENLIST_NO_SUPPORTED_TOKENS, 2, JSEXN_TYPEERR, "{0} attribute of <{1}> does not define any supported tokens")
 MSG_DEF(MSG_CACHE_STREAM_CLOSED, 0, JSEXN_TYPEERR, "Response body is a cache file stream that has already been closed.")
 MSG_DEF(MSG_TIME_VALUE_OUT_OF_RANGE, 1, JSEXN_TYPEERR, "{0} is outside the supported range for time values.")
 MSG_DEF(MSG_ONLY_IF_CACHED_WITHOUT_SAME_ORIGIN, 1, JSEXN_TYPEERR, "Request mode '{0}' was used, but request cache mode 'only-if-cached' can only be used with request mode 'same-origin'.")
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -5761,16 +5761,18 @@ CanvasRenderingContext2D::PutImageData_e
     return NS_ERROR_FAILURE;
   }
 
   uint32_t copyX = dirtyRect.x - aX;
   uint32_t copyY = dirtyRect.y - aY;
   //uint8_t *src = aArray->Data();
   uint8_t *dst = imgsurf->Data();
   uint8_t* srcLine = aArray->Data() + copyY * (aW * 4) + copyX * 4;
+  // For opaque canvases, we must still premultiply the RGB components, but write the alpha as opaque.
+  uint8_t alphaMask = mOpaque ? 255 : 0;
 #if 0
   printf("PutImageData_explicit: dirty x=%d y=%d w=%d h=%d copy x=%d y=%d w=%d h=%d ext x=%d y=%d w=%d h=%d\n",
        dirtyRect.x, dirtyRect.y, copyWidth, copyHeight,
        copyX, copyY, copyWidth, copyHeight,
        x, y, w, h);
 #endif
   for (uint32_t j = 0; j < copyHeight; j++) {
     uint8_t *src = srcLine;
@@ -5780,19 +5782,19 @@ CanvasRenderingContext2D::PutImageData_e
       uint8_t b = *src++;
       uint8_t a = *src++;
 
       // Convert to premultiplied color (losslessly if the input came from getImageData)
 #if MOZ_LITTLE_ENDIAN
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + b];
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + g];
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + r];
-      *dst++ = a;
+      *dst++ = a | alphaMask;
 #else
-      *dst++ = a;
+      *dst++ = a | alphaMask;
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + r];
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + g];
       *dst++ = gfxUtils::sPremultiplyTable[a * 256 + b];
 #endif
     }
     srcLine += aW * 4;
   }
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -564,17 +564,17 @@ BaseCaps(const WebGLContextOptions& opti
         if (!layerManager)
             break;
 
         // XXX we really want "AsSurfaceAllocator" here for generality
         layers::ShadowLayerForwarder* forwarder = layerManager->AsShadowForwarder();
         if (!forwarder)
             break;
 
-        baseCaps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
+        baseCaps.surfaceAllocator = forwarder->GetTextureForwarder();
     } while (false);
 #endif
 
     // Done with baseCaps construction.
 
     if (!gfxPrefs::WebGLForceMSAA()) {
         const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
 
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1305312-1.html
@@ -0,0 +1,5 @@
+<canvas id='cid'></canvas>
+<script>
+var x=document.getElementById('cid').getContext('2d',{alpha: false});
+x.putImageData(x.createImageData(250,27434.63),Number.MAX_SAFE_INTEGER,23);
+</script>
\ No newline at end of file
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -32,9 +32,10 @@ load 1284356-1.html
 load 1284578-1.html
 skip-if(d2d) load 1287515-1.html
 load 1287652-1.html
 load 1288872-1.html
 load 1290628-1.html
 load 1283113-1.html
 load 1286458-1.html
 load 1299062-1.html
+load 1305312-1.html
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3437,17 +3437,17 @@ HTMLInputElement::Focus(ErrorResult& aEr
         break;
       }
     }
   }
 
   return;
 }
 
-#if defined(XP_WIN) || defined(XP_LINUX)
+#if !defined(ANDROID) && !defined(XP_MACOSX)
 bool
 HTMLInputElement::IsNodeApzAwareInternal() const
 {
   // Tell APZC we may handle mouse wheel event and do preventDefault when input
   // type is number.
   return (mType == NS_FORM_INPUT_NUMBER) || (mType == NS_FORM_INPUT_RANGE) ||
          nsINode::IsNodeApzAwareInternal();
 }
@@ -4533,17 +4533,17 @@ HTMLInputElement::PostHandleEvent(EventC
               // aggressive about stopping the spin. (And don't set
               // nsEventStatus_eConsumeNoDefault after doing so, since that
               // might prevent, say, the context menu from opening.)
               StopNumberControlSpinnerSpin();
             }
           }
           break;
         }
-#if defined(XP_WIN) || defined(XP_LINUX)
+#if !defined(ANDROID) && !defined(XP_MACOSX)
         case eWheel: {
           // Handle wheel events as increasing / decreasing the input element's
           // value when it's focused and it's type is number or range.
           WidgetWheelEvent* wheelEvent = aVisitor.mEvent->AsWheelEvent();
           if (!aVisitor.mEvent->DefaultPrevented() &&
               aVisitor.mEvent->IsTrusted() && IsMutable() && wheelEvent &&
               wheelEvent->mDeltaY != 0 &&
               wheelEvent->mDeltaMode != nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
@@ -6198,17 +6198,17 @@ FireEventForAccessibility(nsIDOMHTMLInpu
 
   return NS_OK;
 }
 #endif
 
 void
 HTMLInputElement::UpdateApzAwareFlag()
 {
-#if defined(XP_WIN) || defined(XP_LINUX)
+#if !defined(ANDROID) && !defined(XP_MACOSX)
   if ((mType == NS_FORM_INPUT_NUMBER) || (mType == NS_FORM_INPUT_RANGE)) {
     SetMayBeApzAware();
   }
 #endif
 }
 
 nsresult
 HTMLInputElement::SetDefaultValueAsValue()
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -130,17 +130,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual int32_t TabIndexDefault() override;
   using nsGenericHTMLElement::Focus;
   virtual void Blur(ErrorResult& aError) override;
   virtual void Focus(ErrorResult& aError) override;
 
   // nsINode
-#if defined(XP_WIN) || defined(XP_LINUX)
+#if !defined(ANDROID) && !defined(XP_MACOSX)
   virtual bool IsNodeApzAwareInternal() const override;
 #endif
 
   // Element
   virtual bool IsInteractiveHTMLContent(bool aIgnoreTabindex) const override;
 
   // nsIDOMHTMLInputElement
   NS_DECL_NSIDOMHTMLINPUTELEMENT
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -558,26 +558,16 @@ public:
       securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
     }
 
     MOZ_ASSERT(aElement->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
     nsContentPolicyType contentPolicyType = aElement->IsHTMLElement(nsGkAtoms::audio)
       ? nsIContentPolicy::TYPE_INTERNAL_AUDIO :
         nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
-    nsCOMPtr<nsIDocShell> docShell = aElement->OwnerDoc()->GetDocShell();
-    if (docShell) {
-      nsDocShell* docShellPtr = nsDocShell::Cast(docShell);
-      bool privateBrowsing;
-      docShellPtr->GetUsePrivateBrowsing(&privateBrowsing);
-      if (privateBrowsing) {
-        securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
-      }
-    }
-
     nsCOMPtr<nsILoadGroup> loadGroup = aElement->GetDocumentLoadGroup();
     nsCOMPtr<nsIChannel> channel;
     nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                                 aElement->mLoadingSrc,
                                 static_cast<Element*>(aElement),
                                 securityFlags,
                                 contentPolicyType,
                                 loadGroup,
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -617,19 +617,19 @@ skip-if = buildapp == 'b2g' # bug 112901
 [test_bug1166138.html]
 [test_bug1230665.html]
 [test_filepicker_default_directory.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug1233598.html]
 [test_bug1250401.html]
 [test_bug1260664.html]
 [test_bug1261673.html]
-skip-if = (os != 'win' && os != 'linux')
+skip-if = (os == 'android' || os == 'mac')
 [test_bug1261674-1.html]
-skip-if = (os != 'win' && os != 'linux')
+skip-if = (os == 'android' || os == 'mac')
 [test_bug1261674-2.html]
-skip-if = (os != 'win' && os != 'linux')
+skip-if = (os == 'android' || os == 'mac')
 [test_bug1260704.html]
 [test_allowMedia.html]
 [test_bug1292522_same_domain_with_different_port_number.html]
 [test_bug1295719_event_sequence_for_arrow_keys.html]
 skip-if = os == "android" || appname == "b2g" # up/down arrow keys not supported on android/b2g
 [test_bug1295719_event_sequence_for_number_keys.html]
--- a/dom/html/test/test_anchor_ping.html
+++ b/dom/html/test/test_anchor_ping.html
@@ -36,21 +36,24 @@ addLoadEvent(function () {
 
 let tests = [
 
   // Ensure that sending pings is enabled.
   function* setup() {
     Services.prefs.setBoolPref("browser.send_pings", true);
     Services.prefs.setIntPref("browser.send_pings.max_per_link", -1);
     Services.prefs.setBoolPref("security.mixed_content.block_active_content", false);
+    // The server we create can't handle the priming HEAD requests
+    Services.prefs.setBoolPref("security.mixed_content.send_hsts_priming", false);
 
     SimpleTest.registerCleanupFunction(() => {
       Services.prefs.clearUserPref("browser.send_pings");
       Services.prefs.clearUserPref("browser.send_pings.max_per_link");
       Services.prefs.clearUserPref("security.mixed_content.block_active_content");
+      Services.prefs.clearUserPref("security.mixed_content.send_hsts_priming");
     });
   },
 
   // If both the address of the document containing the hyperlink being audited
   // and ping URL have the same origin then the request must include a Ping-From
   // HTTP header with, as its value, the address of the document containing the
   // hyperlink, and a Ping-To HTTP header with, as its value, the target URL.
   // The request must not include a Referer (sic) HTTP header.
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -5309,17 +5309,17 @@ private:
   struct DatabasesCompleteCallback;
   class FinishCallbackWrapper;
   class IdleConnectionRunnable;
   struct IdleDatabaseInfo;
   struct IdleResource;
   struct IdleThreadInfo;
   struct ThreadInfo;
   class ThreadRunnable;
-  struct TransactionInfo;
+  class TransactionInfo;
   struct TransactionInfoPair;
 
   // This mutex guards mDatabases, see below.
   Mutex mDatabasesMutex;
 
   nsTArray<IdleThreadInfo> mIdleThreads;
   nsTArray<IdleDatabaseInfo> mIdleDatabases;
   nsTArray<DatabaseInfo*> mDatabasesPerformingIdleMaintenance;
@@ -5708,28 +5708,31 @@ public:
   }
 
 private:
   ~ThreadRunnable();
 
   NS_DECL_NSIRUNNABLE
 };
 
-struct ConnectionPool::TransactionInfo final
+class ConnectionPool::TransactionInfo final
 {
   friend class nsAutoPtr<TransactionInfo>;
 
+  nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlocking;
+  nsTArray<TransactionInfo*> mBlockingOrdered;
+
+public:
   DatabaseInfo* mDatabaseInfo;
   const nsID mBackgroundChildLoggingId;
   const nsCString mDatabaseId;
   const uint64_t mTransactionId;
   const int64_t mLoggingSerialNumber;
   const nsTArray<nsString> mObjectStoreNames;
   nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlockedOn;
-  nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlocking;
   nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
   const bool mIsWriteTransaction;
   bool mRunning;
 
 #ifdef DEBUG
   bool mFinished;
 #endif
 
@@ -5738,20 +5741,26 @@ struct ConnectionPool::TransactionInfo f
                   const nsACString& aDatabaseId,
                   uint64_t aTransactionId,
                   int64_t aLoggingSerialNumber,
                   const nsTArray<nsString>& aObjectStoreNames,
                   bool aIsWriteTransaction,
                   TransactionDatabaseOperationBase* aTransactionOp);
 
   void
-  Schedule();
+  AddBlockingTransaction(TransactionInfo* aTransactionInfo);
+
+  void
+  RemoveBlockingTransactions();
 
 private:
   ~TransactionInfo();
+
+  void
+  MaybeUnblock(TransactionInfo* aTransactionInfo);
 };
 
 struct ConnectionPool::TransactionInfoPair final
 {
   friend class nsAutoPtr<TransactionInfoPair>;
 
   // Multiple reading transactions can block future writes.
   nsTArray<TransactionInfo*> mLastBlockingWrites;
@@ -11740,28 +11749,28 @@ ConnectionPool::Start(const nsID& aBackg
     if (!blockInfo) {
       blockInfo = new TransactionInfoPair();
       blockingTransactions.Put(objectStoreName, blockInfo);
     }
 
     // Mark what we are blocking on.
     if (TransactionInfo* blockingRead = blockInfo->mLastBlockingReads) {
       transactionInfo->mBlockedOn.PutEntry(blockingRead);
-      blockingRead->mBlocking.PutEntry(transactionInfo);
+      blockingRead->AddBlockingTransaction(transactionInfo);
     }
 
     if (aIsWriteTransaction) {
       if (const uint32_t writeCount = blockInfo->mLastBlockingWrites.Length()) {
         for (uint32_t writeIndex = 0; writeIndex < writeCount; writeIndex++) {
           TransactionInfo* blockingWrite =
             blockInfo->mLastBlockingWrites[writeIndex];
           MOZ_ASSERT(blockingWrite);
 
           transactionInfo->mBlockedOn.PutEntry(blockingWrite);
-          blockingWrite->mBlocking.PutEntry(transactionInfo);
+          blockingWrite->AddBlockingTransaction(transactionInfo);
         }
       }
 
       blockInfo->mLastBlockingReads = transactionInfo;
       blockInfo->mLastBlockingWrites.Clear();
     } else {
       blockInfo->mLastBlockingWrites.AppendElement(transactionInfo);
     }
@@ -12283,28 +12292,17 @@ ConnectionPool::NoteFinishedTransaction(
     if (transactionInfo->mIsWriteTransaction &&
         blockInfo->mLastBlockingReads == transactionInfo) {
       blockInfo->mLastBlockingReads = nullptr;
     }
 
     blockInfo->mLastBlockingWrites.RemoveElement(transactionInfo);
   }
 
-  for (auto iter = transactionInfo->mBlocking.Iter();
-       !iter.Done();
-       iter.Next()) {
-    TransactionInfo* blockedInfo = iter.Get()->GetKey();
-    MOZ_ASSERT(blockedInfo);
-    MOZ_ASSERT(blockedInfo->mBlockedOn.Contains(transactionInfo));
-
-    blockedInfo->mBlockedOn.RemoveEntry(transactionInfo);
-    if (!blockedInfo->mBlockedOn.Count()) {
-      blockedInfo->Schedule();
-    }
-  }
+  transactionInfo->RemoveBlockingTransactions();
 
   if (transactionInfo->mIsWriteTransaction) {
     MOZ_ASSERT(dbInfo->mWriteTransactionCount);
     dbInfo->mWriteTransactionCount--;
   } else {
     MOZ_ASSERT(dbInfo->mReadTransactionCount);
     dbInfo->mReadTransactionCount--;
   }
@@ -13063,28 +13061,65 @@ TransactionInfo::~TransactionInfo()
   MOZ_ASSERT(!mRunning);
   MOZ_ASSERT(mFinished);
 
   MOZ_COUNT_DTOR(ConnectionPool::TransactionInfo);
 }
 
 void
 ConnectionPool::
-TransactionInfo::Schedule()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mDatabaseInfo);
-
-  ConnectionPool* connectionPool = mDatabaseInfo->mConnectionPool;
-  MOZ_ASSERT(connectionPool);
-  connectionPool->AssertIsOnOwningThread();
-
-  Unused <<
-    connectionPool->ScheduleTransaction(this,
-                                        /* aFromQueuedTransactions */ false);
+TransactionInfo::AddBlockingTransaction(TransactionInfo* aTransactionInfo)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aTransactionInfo);
+
+  if (!mBlocking.Contains(aTransactionInfo)) {
+    mBlocking.PutEntry(aTransactionInfo);
+    mBlockingOrdered.AppendElement(aTransactionInfo);
+  }
+}
+
+void
+ConnectionPool::
+TransactionInfo::RemoveBlockingTransactions()
+{
+  AssertIsOnBackgroundThread();
+
+  for (uint32_t index = 0, count = mBlockingOrdered.Length();
+       index < count;
+       index++) {
+    TransactionInfo* blockedInfo = mBlockingOrdered[index];
+    MOZ_ASSERT(blockedInfo);
+
+    blockedInfo->MaybeUnblock(this);
+  }
+
+  mBlocking.Clear();
+  mBlockingOrdered.Clear();
+}
+
+void
+ConnectionPool::
+TransactionInfo::MaybeUnblock(TransactionInfo* aTransactionInfo)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mBlockedOn.Contains(aTransactionInfo));
+
+  mBlockedOn.RemoveEntry(aTransactionInfo);
+  if (!mBlockedOn.Count()) {
+    MOZ_ASSERT(mDatabaseInfo);
+
+    ConnectionPool* connectionPool = mDatabaseInfo->mConnectionPool;
+    MOZ_ASSERT(connectionPool);
+    connectionPool->AssertIsOnOwningThread();
+
+    Unused <<
+      connectionPool->ScheduleTransaction(this,
+                                          /* aFromQueuedTransactions */ false);
+  }
 }
 
 ConnectionPool::
 TransactionInfoPair::TransactionInfoPair()
   : mLastBlockingReads(nullptr)
 {
   AssertIsOnBackgroundThread();
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1549,25 +1549,27 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObject
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedKeyPath)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransaction)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexes)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeletedIndexes)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 
   // Don't unlink mTransaction!
 
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexes)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeletedIndexes)
 
   tmp->mCachedKeyPath.setUndefined();
 
   if (tmp->mRooted) {
     mozilla::DropJSObjects(tmp);
     tmp->mRooted = false;
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@@ -1769,23 +1771,21 @@ IDBObjectStore::CreateIndex(const nsAStr
 
   if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
       mDeletedSpec) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return nullptr;
   }
 
   IDBTransaction* transaction = IDBTransaction::GetCurrent();
-  if (!transaction || transaction != mTransaction) {
+  if (!transaction || transaction != mTransaction || !transaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return nullptr;
   }
 
-  MOZ_ASSERT(transaction->IsOpen());
-
   auto& indexes = const_cast<nsTArray<IndexMetadata>&>(mSpec->indexes());
   for (uint32_t count = indexes.Length(), index = 0;
        index < count;
        index++) {
     if (aName == indexes[index].name()) {
       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR);
       return nullptr;
     }
@@ -1883,23 +1883,21 @@ IDBObjectStore::DeleteIndex(const nsAStr
 
   if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
       mDeletedSpec) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
     return;
   }
 
   IDBTransaction* transaction = IDBTransaction::GetCurrent();
-  if (!transaction || transaction != mTransaction) {
+  if (!transaction || transaction != mTransaction || !transaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return;
   }
 
-  MOZ_ASSERT(transaction->IsOpen());
-
   auto& metadataArray = const_cast<nsTArray<IndexMetadata>&>(mSpec->indexes());
 
   int64_t foundId = 0;
 
   for (uint32_t metadataCount = metadataArray.Length(), metadataIndex = 0;
        metadataIndex < metadataCount;
        metadataIndex++) {
     const IndexMetadata& metadata = metadataArray[metadataIndex];
@@ -1911,16 +1909,21 @@ IDBObjectStore::DeleteIndex(const nsAStr
       // Must do this before altering the metadata array!
       for (uint32_t indexCount = mIndexes.Length(), indexIndex = 0;
            indexIndex < indexCount;
            indexIndex++) {
         RefPtr<IDBIndex>& index = mIndexes[indexIndex];
 
         if (index->Id() == foundId) {
           index->NoteDeletion();
+
+          RefPtr<IDBIndex>* deletedIndex =
+            mDeletedIndexes.AppendElement();
+          deletedIndex->swap(mIndexes[indexIndex]);
+
           mIndexes.RemoveElementAt(indexIndex);
           break;
         }
       }
 
       metadataArray.RemoveElementAt(metadataIndex);
 
       RefreshSpec(/* aMayDelete */ false);
@@ -2125,16 +2128,22 @@ IDBObjectStore::RefreshSpec(bool aMayDel
       mSpec = &objSpec;
 
       for (uint32_t idxCount = mIndexes.Length(), idxIndex = 0;
            idxIndex < idxCount;
            idxIndex++) {
         mIndexes[idxIndex]->RefreshMetadata(aMayDelete);
       }
 
+      for (uint32_t idxCount = mDeletedIndexes.Length(), idxIndex = 0;
+           idxIndex < idxCount;
+           idxIndex++) {
+        mDeletedIndexes[idxIndex]->RefreshMetadata(false);
+      }
+
       found = true;
       break;
     }
   }
 
   MOZ_ASSERT_IF(!aMayDelete && !mDeletedSpec, found);
 
   if (found) {
@@ -2197,23 +2206,21 @@ IDBObjectStore::SetName(const nsAString&
 
   if (mTransaction->GetMode() != IDBTransaction::VERSION_CHANGE ||
       mDeletedSpec) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   IDBTransaction* transaction = IDBTransaction::GetCurrent();
-  if (!transaction || transaction != mTransaction) {
+  if (!transaction || transaction != mTransaction || !transaction->IsOpen()) {
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
     return;
   }
 
-  MOZ_ASSERT(transaction->IsOpen());
-
   if (aName == mSpec->metadata().name()) {
     return;
   }
 
   // Cache logging string of this object store before renaming.
   const LoggingString loggingOldObjectStore(this);
 
   nsresult rv =
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -62,16 +62,17 @@ class IDBObjectStore final
   // This normally points to the ObjectStoreSpec owned by the parent IDBDatabase
   // object. However, if this objectStore is part of a versionchange transaction
   // and it gets deleted then the spec is copied into mDeletedSpec and mSpec is
   // set to point at mDeletedSpec.
   const ObjectStoreSpec* mSpec;
   nsAutoPtr<ObjectStoreSpec> mDeletedSpec;
 
   nsTArray<RefPtr<IDBIndex>> mIndexes;
+  nsTArray<RefPtr<IDBIndex>> mDeletedIndexes;
 
   const int64_t mId;
   bool mRooted;
 
 public:
   struct StructuredCloneWriteInfo;
 
   static already_AddRefed<IDBObjectStore>
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -648,16 +648,22 @@ IDBTransaction::AbortInternal(nsresult a
   if (isVersionChange) {
     // If a version change transaction is aborted, we must revert the world
     // back to its previous state unless we're being invalidated after the
     // transaction already completed.
     if (!isInvalidated) {
       mDatabase->RevertToPreviousState();
     }
 
+    // We do the reversion only for the mObjectStores/mDeletedObjectStores but
+    // not for the mIndexes/mDeletedIndexes of each IDBObjectStore because it's
+    // time-consuming(O(m*n)) and mIndexes/mDeletedIndexes won't be used anymore
+    // in IDBObjectStore::(Create|Delete)Index() and IDBObjectStore::Index() in
+    // which all the executions are returned earlier by !transaction->IsOpen().
+
     const nsTArray<ObjectStoreSpec>& specArray =
       mDatabase->Spec()->objectStores();
 
     if (specArray.IsEmpty()) {
       mObjectStores.Clear();
       mDeletedObjectStores.Clear();
     } else {
       nsTHashtable<nsUint64HashKey> validIds(specArray.Length());
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -14,16 +14,18 @@ support-files =
   file_app_isolation.html
   file_app_isolation.js
   helpers.js
   leaving_page_iframe.html
   service_worker.js
   service_worker_client.html
   third_party_iframe1.html
   third_party_iframe2.html
+  unit/test_abort_deleted_index.js
+  unit/test_abort_deleted_objectStore.js
   unit/test_add_put.js
   unit/test_add_twice_failure.js
   unit/test_advance.js
   unit/test_autoIncrement.js
   unit/test_autoIncrement_indexes.js
   unit/test_blob_file_backed.js
   unit/test_blocked_order.js
   unit/test_clear.js
@@ -110,16 +112,20 @@ support-files =
   unit/test_transaction_lifetimes_nested.js
   unit/test_transaction_ordering.js
   unit/test_unique_index_update.js
   unit/test_writer_starvation.js
   webapp_clearBrowserData.js
   webapp_clearBrowserData_appFrame.html
   webapp_clearBrowserData_browserFrame.html
 
+[test_abort_deleted_index.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
+[test_abort_deleted_objectStore.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_add_put.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_add_twice_failure.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_advance.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_app_isolation_inproc.html]
 # The app isolation tests are only supposed to run in the main process.
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_abort_deleted_index.html
@@ -0,0 +1,19 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Abort Deleted Index Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_abort_deleted_index.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/test_abort_deleted_objectStore.html
@@ -0,0 +1,19 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+  <title>Indexed Database Abort Deleted ObjectStore Test</title>
+
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+  <script type="text/javascript;version=1.7" src="unit/test_abort_deleted_objectStore.js"></script>
+  <script type="text/javascript;version=1.7" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_abort_deleted_index.js
@@ -0,0 +1,78 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = this.window ? window.location.pathname : "Splendid Test";
+  const storeName = "test store";
+  const indexName_ToBeDeleted = "test index to be deleted";
+
+  info("Create index in v1.");
+  let request = indexedDB.open(name, 1);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = unexpectedSuccessHandler;
+  let event = yield undefined;
+
+  let db = event.target.result;
+  let txn = event.target.transaction;
+
+  is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
+
+  let objectStore = db.createObjectStore(storeName, { keyPath: "foo" });
+  is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
+  is(db.objectStoreNames.item(0), objectStore.name, "Correct object store name");
+
+  // create index to be deleted later in v2.
+  objectStore.createIndex(indexName_ToBeDeleted, "foo");
+  ok(objectStore.index(indexName_ToBeDeleted), "Index created.");
+
+  txn.oncomplete = continueToNextStepSync;
+  yield undefined;
+  request.onsuccess = continueToNextStep;
+  yield undefined;
+  db.close();
+
+  info("Delete index in v2.");
+  request = indexedDB.open(name, 2);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = unexpectedSuccessHandler;
+  event = yield undefined;
+
+  db = event.target.result;
+  txn = event.target.transaction;
+
+  objectStore = txn.objectStore(storeName);
+  let index = objectStore.index(indexName_ToBeDeleted);
+  ok(index, "index is valid.");
+  objectStore.deleteIndex(indexName_ToBeDeleted);
+
+  // Aborting the transaction.
+  request.onerror = expectedErrorHandler("AbortError");
+  txn.abort();
+  try {
+    index.get('foo');
+    ok(false, "TransactionInactiveError shall be thrown right after a deletion of an index is aborted.");
+  } catch (e) {
+    ok(e instanceof DOMException, "got a database exception");
+    is(e.name, "TransactionInactiveError", "TransactionInactiveError shall be thrown right after a deletion of an index is aborted.");
+  }
+
+  yield undefined;
+
+  try {
+    index.get('foo');
+    ok(false, "TransactionInactiveError shall be thrown after the transaction is inactive.");
+  } catch (e) {
+    ok(e instanceof DOMException, "got a database exception");
+    is(e.name, "TransactionInactiveError", "TransactionInactiveError shall be thrown after the transaction is inactive.");
+  }
+
+  finishTest();
+  yield undefined;
+}
new file mode 100644
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_abort_deleted_objectStore.js
@@ -0,0 +1,74 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var testGenerator = testSteps();
+
+function testSteps()
+{
+  const name = this.window ? window.location.pathname : "Splendid Test";
+  const storeName_ToBeDeleted = "test store to be deleted";
+
+  info("Create objectStore in v1.");
+  let request = indexedDB.open(name, 1);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = unexpectedSuccessHandler;
+  let event = yield undefined;
+
+  let db = event.target.result;
+  let txn = event.target.transaction;
+
+  is(db.objectStoreNames.length, 0, "Correct objectStoreNames list");
+
+  // create objectstore to be deleted later in v2.
+  db.createObjectStore(storeName_ToBeDeleted, { keyPath: "foo" });
+  is(db.objectStoreNames.length, 1, "Correct objectStoreNames list");
+  ok(db.objectStoreNames.contains(storeName_ToBeDeleted), "Correct name");
+
+  txn.oncomplete = continueToNextStepSync;
+  yield undefined;
+  request.onsuccess = continueToNextStep;
+  yield undefined;
+  db.close();
+
+  info("Delete objectStore in v2.");
+  request = indexedDB.open(name, 2);
+  request.onerror = errorHandler;
+  request.onupgradeneeded = grabEventAndContinueHandler;
+  request.onsuccess = unexpectedSuccessHandler;
+  event = yield undefined;
+
+  db = event.target.result;
+  txn = event.target.transaction;
+
+  let objectStore = txn.objectStore(storeName_ToBeDeleted);
+  ok(objectStore, "objectStore is available");
+
+  db.deleteObjectStore(storeName_ToBeDeleted);
+
+  // Aborting the transaction.
+  request.onerror = expectedErrorHandler("AbortError");
+  txn.abort();
+  try {
+    objectStore.get('foo');
+    ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive.");
+  } catch (e) {
+    ok(e instanceof DOMException, "got a database exception");
+    is(e.name, "TransactionInactiveError", "correct error");
+  }
+
+  yield undefined;
+
+  try {
+    objectStore.get('foo');
+    ok(false, "TransactionInactiveError shall be thrown if the transaction is inactive.");
+  } catch (e) {
+    ok(e instanceof DOMException, "got a database exception");
+    is(e.name, "TransactionInactiveError", "correct error");
+  }
+
+  finishTest();
+  yield undefined;
+}
--- a/dom/indexedDB/test/unit/xpcshell-shared.ini
+++ b/dom/indexedDB/test/unit/xpcshell-shared.ini
@@ -1,12 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+[test_abort_deleted_index.js]
+[test_abort_deleted_objectStore.js]
 [test_add_put.js]
 [test_add_twice_failure.js]
 [test_advance.js]
 [test_autoIncrement.js]
 [test_autoIncrement_indexes.js]
 [test_blocked_order.js]
 [test_clear.js]
 [test_complex_keyPaths.js]
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -3569,20 +3569,17 @@ BlobChild::SetMysteryBlobInfo(const nsSt
 
 bool
 BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mBlobImpl);
   MOZ_ASSERT(mRemoteBlobImpl);
 
-  nsString voidString;
-  voidString.SetIsVoid(true);
-
-  mBlobImpl->SetLazyData(voidString, aContentType, aLength, INT64_MAX);
+  mBlobImpl->SetLazyData(NullString(), aContentType, aLength, INT64_MAX);
 
   NormalBlobConstructorParams params(aContentType,
                                      aLength,
                                      void_t() /* optionalBlobData */);
   return SendResolveMystery(params);
 }
 
 void
@@ -4395,20 +4392,17 @@ BlobParent::RecvResolveMystery(const Res
       const NormalBlobConstructorParams& params =
         aParams.get_NormalBlobConstructorParams();
 
       if (NS_WARN_IF(params.length() == UINT64_MAX)) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
 
-      nsString voidString;
-      voidString.SetIsVoid(true);
-
-      mBlobImpl->SetLazyData(voidString,
+      mBlobImpl->SetLazyData(NullString(),
                              params.contentType(),
                              params.length(),
                              INT64_MAX);
       return true;
     }
 
     case ResolveMysteryParams::TFileBlobConstructorParams: {
       const FileBlobConstructorParams& params =
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -817,24 +817,25 @@ ContentChild::ProvideWindowCommon(TabChi
     return NS_ERROR_ABORT;
   }
 
   if (layersId == 0) { // if renderFrame is invalid.
     PRenderFrameChild::Send__delete__(renderFrame);
     renderFrame = nullptr;
   }
 
-  ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0);
+  ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0, 0);
   auto* opener = nsPIDOMWindowOuter::From(aParent);
   nsIDocShell* openerShell;
   if (opener && (openerShell = opener->GetDocShell())) {
     nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
     showInfo = ShowInfo(EmptyString(), false,
                         context->UsePrivateBrowsing(), true, false,
-                        aTabOpener->mDPI, aTabOpener->mDefaultScale);
+                        aTabOpener->mDPI, aTabOpener->mRounding,
+                        aTabOpener->mDefaultScale);
   }
 
   // Unfortunately we don't get a window unless we've shown the frame.  That's
   // pretty bogus; see bug 763602.
   newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame,
                        showInfo);
 
   for (size_t i = 0; i < frameScripts.Length(); i++) {
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -3889,28 +3889,28 @@ ContentParent::RecvIsSecureURI(const uin
   nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
   if (!sss) {
     return false;
   }
   nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
   if (!ourURI) {
     return false;
   }
-  nsresult rv = sss->IsSecureURI(type, ourURI, flags, isSecureURI);
+  nsresult rv = sss->IsSecureURI(type, ourURI, flags, nullptr, isSecureURI);
   return NS_SUCCEEDED(rv);
 }
 
 bool
-ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive)
+ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming)
 {
   nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI);
   if (!ourURI) {
     return false;
   }
-  nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive);
+  nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming);
   return true;
 }
 
 bool
 ContentParent::RecvLoadURIExternal(const URIParams& uri,
                                    PBrowserParent* windowContext)
 {
   nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -784,17 +784,18 @@ private:
                                    nsTArray<uint8_t>&& aChallenge,
                                    nsTArray<uint8_t>&& aKeyHandle,
                                    nsTArray<uint8_t>* aSignature) override;
 
   virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
                                const uint32_t& aFlags, bool* aIsSecureURI) override;
 
   virtual bool RecvAccumulateMixedContentHSTS(const URIParams& aURI,
-                                              const bool& aActive) override;
+                                              const bool& aActive,
+                                              const bool& aHSTSPriming) override;
 
   virtual bool DeallocPHalParent(PHalParent*) override;
 
   virtual bool
   DeallocPHeapSnapshotTempFileHelperParent(PHeapSnapshotTempFileHelperParent*) override;
 
   virtual PIccParent* AllocPIccParent(const uint32_t& aServiceId) override;
 
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -96,16 +96,17 @@ union MaybeNativeKeyBinding
 struct ShowInfo
 {
   nsString name;
   bool fullscreenAllowed;
   bool isPrivate;
   bool fakeShowInfo;
   bool isTransparent;
   float dpi;
+  int32_t widgetRounding;
   double defaultScale;
 };
 
 union OptionalShmem
 {
   void_t;
   Shmem;
 };
@@ -371,16 +372,21 @@ parent:
     sync GetDPI() returns (float value);
 
     /**
      * Gets the default scaling factor of the screen corresponding to this browser.
      */
     sync GetDefaultScale() returns (double value);
 
     /**
+     * Gets the rounding of coordinates in the widget.
+     */
+    sync GetWidgetRounding() returns (int32_t value);
+
+    /**
      * Gets maximum of touch points at current device.
      */
     sync GetMaxTouchPoints() returns (uint32_t value);
 
     /**
      * Set the native cursor.
      * @param value
      *   The widget cursor to set.
@@ -788,17 +794,17 @@ child:
      * Tell the child that the UI resolution changed for the containing
      * window.
      * To avoid some sync messages from child to parent, we also send the dpi
      * and default scale with the notification.
      * If we don't know the dpi and default scale, we just pass in a negative
      * value (-1) but in the majority of the cases this saves us from two
      * sync requests from the child to the parent.
      */
-    async UIResolutionChanged(float dpi, double scale);
+    async UIResolutionChanged(float dpi, int32_t rounding, double scale);
 
     /**
      * Tell the child that the system theme has changed, and that a repaint
      * is necessary.
      */
     async ThemeChanged(LookAndFeelInt[] lookAndFeelIntCache);
 
     /**
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -833,17 +833,17 @@ parent:
      */
     sync NSSU2FTokenSign(uint8_t[] application, uint8_t[] challenge,
                          uint8_t[] keyHandle)
         returns (uint8_t[] signature);
 
     sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
         returns (bool isSecureURI);
 
-    async AccumulateMixedContentHSTS(URIParams uri, bool active);
+    async AccumulateMixedContentHSTS(URIParams uri, bool active, bool hasHSTSPriming);
 
     sync GetLookAndFeelCache()
         returns (LookAndFeelInt[] lookAndFeelIntCache);
 
     prio(urgent) async PHal();
 
     async PHeapSnapshotTempFileHelper();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -536,16 +536,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mIgnoreKeyPressEvent(false)
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
+  , mRounding(0)
   , mDefaultScale(0)
   , mIsTransparent(false)
   , mIPCOpen(false)
   , mParentIsActive(false)
   , mDidSetRealShowInfo(false)
   , mDidLoadURLInit(false)
   , mAPZChild(nullptr)
   , mLayerObserverEpoch(0)
@@ -1544,16 +1545,17 @@ TabChild::ApplyShowInfo(const ShowInfo& 
           DocShellOriginAttributes attrs(nsDocShell::Cast(docShell)->GetOriginAttributes());
           attrs.SyncAttributesWithPrivateBrowsing(true);
           nsDocShell::Cast(docShell)->SetOriginAttributes(attrs);
         }
       }
     }
   }
   mDPI = aInfo.dpi();
+  mRounding = aInfo.widgetRounding();
   mDefaultScale = aInfo.defaultScale();
   mIsTransparent = aInfo.isTransparent();
 }
 
 #ifdef MOZ_WIDGET_GONK
 void
 TabChild::MaybeRequestPreinitCamera()
 {
@@ -2938,16 +2940,32 @@ TabChild::GetDefaultScale(double* aScale
       return;
     }
 
     // Fallback to a sync call if needed.
     SendGetDefaultScale(aScale);
 }
 
 void
+TabChild::GetWidgetRounding(int32_t* aRounding)
+{
+  *aRounding = 1;
+  if (!mRemoteFrame) {
+    return;
+  }
+  if (mRounding > 0) {
+    *aRounding = mRounding;
+    return;
+  }
+
+  // Fallback to a sync call if needed.
+  SendGetWidgetRounding(aRounding);
+}
+
+void
 TabChild::GetMaxTouchPoints(uint32_t* aTouchPoints)
 {
   // Fallback to a sync call.
   SendGetMaxTouchPoints(aTouchPoints);
 }
 
 void
 TabChild::NotifyPainted()
@@ -3280,22 +3298,25 @@ TabChild::RecvRequestNotifyAfterRemotePa
   // Tell the CompositorBridgeChild that, when it gets a RemotePaintIsReady
   // message that it should forward it us so that we can bounce it to our
   // RenderFrameParent.
   compositor->RequestNotifyAfterRemotePaint(this);
   return true;
 }
 
 bool
-TabChild::RecvUIResolutionChanged(const float& aDpi, const double& aScale)
+TabChild::RecvUIResolutionChanged(const float& aDpi,
+                                  const int32_t& aRounding,
+                                  const double& aScale)
 {
   ScreenIntSize oldScreenSize = GetInnerSize();
   mDPI = 0;
+  mRounding = 0;
   mDefaultScale = 0;
-  static_cast<PuppetWidget*>(mPuppetWidget.get())->UpdateBackingScaleCache(aDpi, aScale);
+  static_cast<PuppetWidget*>(mPuppetWidget.get())->UpdateBackingScaleCache(aDpi, aRounding, aScale);
   nsCOMPtr<nsIDocument> document(GetDocument());
   nsCOMPtr<nsIPresShell> presShell = document->GetShell();
   if (presShell) {
     RefPtr<nsPresContext> presContext = presShell->GetPresContext();
     if (presContext) {
       presContext->UIResolutionChangedSync();
     }
   }
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -479,16 +479,18 @@ public:
 
   virtual PuppetWidget* WebWidget() override { return mPuppetWidget; }
 
   /** Return the DPI of the widget this TabChild draws to. */
   void GetDPI(float* aDPI);
 
   void GetDefaultScale(double *aScale);
 
+  void GetWidgetRounding(int32_t* aRounding);
+
   bool IsTransparent() const { return mIsTransparent; }
 
   void GetMaxTouchPoints(uint32_t* aTouchPoints);
 
   ScreenOrientationInternal GetOrientation() const { return mOrientation; }
 
   void SetBackgroundColor(const nscolor& aColor);
 
@@ -567,16 +569,17 @@ public:
   static inline TabChild* GetFrom(nsIDOMWindow* aWindow)
   {
     nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
     nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
     return GetFrom(docShell);
   }
 
   virtual bool RecvUIResolutionChanged(const float& aDpi,
+                                       const int32_t& aRounding,
                                        const double& aScale) override;
 
   virtual bool
   RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override;
 
   virtual bool RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
                                    nsTArray<uint32_t>&& aCharCodes,
                                    const int32_t& aModifierMask) override;
@@ -772,16 +775,17 @@ private:
   // Position of client area relative to the outer window
   LayoutDeviceIntPoint mClientOffset;
   // Position of tab, relative to parent widget (typically the window)
   LayoutDeviceIntPoint mChromeDisp;
   TabId mUniqueId;
 
   friend class ContentChild;
   float mDPI;
+  int32_t mRounding;
   double mDefaultScale;
 
   bool mIsTransparent;
 
   bool mIPCOpen;
   bool mParentIsActive;
   bool mAsyncPanZoomEnabled;
   CSSSize mUnscaledInnerSize;
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -272,16 +272,17 @@ TabParent::TabParent(nsIContentParent* a
                      const TabContext& aContext,
                      uint32_t aChromeFlags)
   : TabContext(aContext)
   , mFrameElement(nullptr)
   , mRect(0, 0, 0, 0)
   , mDimensions(0, 0)
   , mOrientation(0)
   , mDPI(0)
+  , mRounding(0)
   , mDefaultScale(0)
   , mUpdatedDimensions(false)
   , mSizeMode(nsSizeMode_Normal)
   , mManager(aManager)
   , mDocShellIsActive(false)
   , mMarkedDestroying(false)
   , mIsDestroyed(false)
   , mIsDetached(true)
@@ -1025,17 +1026,18 @@ TabParent::UIResolutionChanged()
     // TryCacheDPIAndScale()'s cache is keyed off of
     // mDPI being greater than 0, so this invalidates it.
     mDPI = -1;
     TryCacheDPIAndScale();
     // If mDPI was set to -1 to invalidate it and then TryCacheDPIAndScale
     // fails to cache the values, then mDefaultScale.scale might be invalid.
     // We don't want to send that value to content. Just send -1 for it too in
     // that case.
-    Unused << SendUIResolutionChanged(mDPI, mDPI < 0 ? -1.0 : mDefaultScale.scale);
+    Unused << SendUIResolutionChanged(mDPI, mRounding,
+                                      mDPI < 0 ? -1.0 : mDefaultScale.scale);
   }
 }
 
 void
 TabParent::ThemeChanged()
 {
   if (!mIsDestroyed) {
     // The theme has changed, and any cached values we had sent down
@@ -2495,16 +2497,27 @@ TabParent::RecvGetDefaultScale(double* a
 
   MOZ_ASSERT(mDefaultScale.scale > 0,
              "Must not ask for scale before OwnerElement is received!");
   *aValue = mDefaultScale.scale;
   return true;
 }
 
 bool
+TabParent::RecvGetWidgetRounding(int32_t* aValue)
+{
+  TryCacheDPIAndScale();
+
+  MOZ_ASSERT(mRounding > 0,
+             "Must not ask for rounding before OwnerElement is received!");
+  *aValue = mRounding;
+  return true;
+}
+
+bool
 TabParent::RecvGetMaxTouchPoints(uint32_t* aTouchPoints)
 {
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (widget) {
     *aTouchPoints = widget->GetMaxTouchPoints();
   } else {
     *aTouchPoints = 0;
   }
@@ -2761,16 +2774,17 @@ TabParent::TryCacheDPIAndScale()
   if (mDPI > 0) {
     return;
   }
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
 
   if (widget) {
     mDPI = widget->GetDPI();
+    mRounding = widget->RoundsWidgetCoordinatesTo();
     mDefaultScale = widget->GetDefaultScale();
   }
 }
 
 already_AddRefed<nsIWidget>
 TabParent::GetWidget() const
 {
   if (!mFrameElement) {
@@ -3369,21 +3383,21 @@ TabParent::GetShowInfo()
     bool allowFullscreen =
       mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
       mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen);
     bool isPrivate = mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
     bool isTransparent =
       nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) &&
       mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
     return ShowInfo(name, allowFullscreen, isPrivate, false,
-                    isTransparent, mDPI, mDefaultScale.scale);
+                    isTransparent, mDPI, mRounding, mDefaultScale.scale);
   }
 
   return ShowInfo(EmptyString(), false, false, false,
-                  false, mDPI, mDefaultScale.scale);
+                  false, mDPI, mRounding, mDefaultScale.scale);
 }
 
 void
 TabParent::AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow,
                                           AudioChannel aAudioChannel,
                                           float aVolume,
                                           bool aMuted)
 {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -316,16 +316,18 @@ public:
                                const nsString& aDirection) override;
 
   virtual bool RecvHideTooltip() override;
 
   virtual bool RecvGetDPI(float* aValue) override;
 
   virtual bool RecvGetDefaultScale(double* aValue) override;
 
+  virtual bool RecvGetWidgetRounding(int32_t* aValue) override;
+
   virtual bool RecvGetMaxTouchPoints(uint32_t* aTouchPoints) override;
 
   virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue) override;
 
   virtual bool RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override;
 
   virtual bool RecvDispatchFocusToTopLevelWindow() override;
 
@@ -630,16 +632,17 @@ protected:
                                                     const bool& aActive) override;
 
   ContentCacheInParent mContentCache;
 
   nsIntRect mRect;
   ScreenIntSize mDimensions;
   ScreenOrientationInternal mOrientation;
   float mDPI;
+  int32_t mRounding;
   CSSToLayoutDeviceScale mDefaultScale;
   bool mUpdatedDimensions;
   nsSizeMode mSizeMode;
   LayoutDeviceIntPoint mClientOffset;
   LayoutDeviceIntPoint mChromeOffset;
 
 private:
   void DestroyInternal();
--- a/dom/json/nsJSON.cpp
+++ b/dom/json/nsJSON.cpp
@@ -65,30 +65,28 @@ nsJSON::Encode(JS::Handle<JS::Value> aVa
                nsAString &aJSON)
 {
   // This function should only be called from JS.
   nsresult rv = WarnDeprecatedMethod(EncodeWarning);
   if (NS_FAILED(rv))
     return rv;
 
   if (aArgc == 0) {
-    aJSON.Truncate();
     aJSON.SetIsVoid(true);
     return NS_OK;
   }
 
   nsJSONWriter writer;
   rv = EncodeInternal(cx, aValue, &writer);
 
   // FIXME: bug 408838. Get exception types sorted out
   if (NS_SUCCEEDED(rv) || rv == NS_ERROR_INVALID_ARG) {
     rv = NS_OK;
     // if we didn't consume anything, it's not JSON, so return null
     if (!writer.DidWrite()) {
-      aJSON.Truncate();
       aJSON.SetIsVoid(true);
     } else {
       writer.FlushBuffer();
       aJSON.Append(writer.mOutputString);
     }
   }
 
   return rv;
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -360,17 +360,18 @@ AudioStream::Init(uint32_t aNumChannels,
 nsresult
 AudioStream::OpenCubeb(cubeb* aContext, cubeb_stream_params& aParams,
                        TimeStamp aStartTime, bool aIsFirst)
 {
   MOZ_ASSERT(aContext);
 
   cubeb_stream* stream = nullptr;
   /* Convert from milliseconds to frames. */
-  uint32_t latency_frames = CubebUtils::GetCubebLatency() * aParams.rate / 1000;
+  uint32_t latency_frames =
+    CubebUtils::GetCubebPlaybackLatencyInMilliseconds() * aParams.rate / 1000;
   if (cubeb_stream_init(aContext, &stream, "AudioStream",
                         nullptr, nullptr, nullptr, &aParams,
                         latency_frames,
                         DataCallback_S, StateCallback_S, this) == CUBEB_OK) {
     mCubebStream.reset(stream);
     CubebUtils::ReportCubebBackendUsed();
   } else {
     NS_WARNING(nsPrintfCString("AudioStream::OpenCubeb() %p failed to init cubeb", this).get());
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -14,33 +14,36 @@
 #include "mozilla/StaticPtr.h"
 #include "mozilla/Telemetry.h"
 #include "nsThreadUtils.h"
 #include "CubebUtils.h"
 #include "nsAutoRef.h"
 #include "prdtoa.h"
 
 #define PREF_VOLUME_SCALE "media.volume_scale"
-#define PREF_CUBEB_LATENCY "media.cubeb_latency_ms"
+#define PREF_CUBEB_LATENCY_PLAYBACK "media.cubeb_latency_playback_ms"
+#define PREF_CUBEB_LATENCY_MSG "media.cubeb_latency_msg_frames"
 
 namespace mozilla {
 
 namespace {
 
 // This mutex protects the variables below.
 StaticMutex sMutex;
 enum class CubebState {
   Uninitialized = 0,
   Initialized,
   Shutdown
 } sCubebState = CubebState::Uninitialized;
 cubeb* sCubebContext;
 double sVolumeScale;
-uint32_t sCubebLatency;
-bool sCubebLatencyPrefSet;
+uint32_t sCubebPlaybackLatencyInMilliseconds;
+uint32_t sCubebMSGLatencyInFrames;
+bool sCubebPlaybackLatencyPrefSet;
+bool sCubebMSGLatencyPrefSet;
 bool sAudioStreamInitEverSucceeded = false;
 StaticAutoPtr<char> sBrandName;
 
 const char kBrandBundleURL[]      = "chrome://branding/locale/brand.properties";
 
 const char* AUDIOSTREAM_BACKEND_ID_STR[] = {
   "jack",
   "pulse",
@@ -76,38 +79,49 @@ const int CUBEB_BACKEND_UNKNOWN = CUBEB_
 // visible on the querying thread/CPU.
 uint32_t sPreferredSampleRate;
 
 } // namespace
 
 extern LazyLogModule gAudioStreamLog;
 
 static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
+// Consevative default that can work on all platforms.
+static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
 
 namespace CubebUtils {
 
 void PrefChanged(const char* aPref, void* aClosure)
 {
   if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
     nsAdoptingString value = Preferences::GetString(aPref);
     StaticMutexAutoLock lock(sMutex);
     if (value.IsEmpty()) {
       sVolumeScale = 1.0;
     } else {
       NS_ConvertUTF16toUTF8 utf8(value);
       sVolumeScale = std::max<double>(0, PR_strtod(utf8.get(), nullptr));
     }
-  } else if (strcmp(aPref, PREF_CUBEB_LATENCY) == 0) {
+  } else if (strcmp(aPref, PREF_CUBEB_LATENCY_PLAYBACK) == 0) {
     // Arbitrary default stream latency of 100ms.  The higher this
     // value, the longer stream volume changes will take to become
     // audible.
-    sCubebLatencyPrefSet = Preferences::HasUserValue(aPref);
+    sCubebPlaybackLatencyPrefSet = Preferences::HasUserValue(aPref);
     uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_MS);
     StaticMutexAutoLock lock(sMutex);
-    sCubebLatency = std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
+    sCubebPlaybackLatencyInMilliseconds = std::min<uint32_t>(std::max<uint32_t>(value, 1), 1000);
+  } else if (strcmp(aPref, PREF_CUBEB_LATENCY_MSG) == 0) {
+    sCubebMSGLatencyPrefSet = Preferences::HasUserValue(aPref);
+    uint32_t value = Preferences::GetUint(aPref, CUBEB_NORMAL_LATENCY_FRAMES);
+    StaticMutexAutoLock lock(sMutex);
+    // 128 is the block size for the Web Audio API, which limits how low the
+    // latency can be here.
+    // We don't want to limit the upper limit too much, so that people can
+    // experiment.
+    sCubebMSGLatencyInFrames = std::min<uint32_t>(std::max<uint32_t>(value, 128), 1e6);
   }
 }
 
 bool GetFirstStream()
 {
   static bool sFirstStream = true;
 
   StaticMutexAutoLock lock(sMutex);
@@ -233,43 +247,62 @@ void ReportCubebStreamInitFailure(bool a
     // failures to open multiple streams in a process over time.
     return;
   }
   Telemetry::Accumulate(Telemetry::AUDIOSTREAM_BACKEND_USED,
                         aIsFirst ? CUBEB_BACKEND_INIT_FAILURE_FIRST
                                  : CUBEB_BACKEND_INIT_FAILURE_OTHER);
 }
 
-uint32_t GetCubebLatency()
+uint32_t GetCubebPlaybackLatencyInMilliseconds()
+{
+  StaticMutexAutoLock lock(sMutex);
+  return sCubebPlaybackLatencyInMilliseconds;
+}
+
+bool CubebPlaybackLatencyPrefSet()
 {
   StaticMutexAutoLock lock(sMutex);
-  return sCubebLatency;
+  return sCubebPlaybackLatencyPrefSet;
+}
+
+bool CubebMSGLatencyPrefSet()
+{
+  StaticMutexAutoLock lock(sMutex);
+  return sCubebMSGLatencyPrefSet;
 }
 
-bool CubebLatencyPrefSet()
+Maybe<uint32_t> GetCubebMSGLatencyInFrames()
 {
   StaticMutexAutoLock lock(sMutex);
-  return sCubebLatencyPrefSet;
+  if (!sCubebMSGLatencyPrefSet) {
+    return Maybe<uint32_t>();
+  }
+  MOZ_ASSERT(sCubebMSGLatencyInFrames > 0);
+  return Some(sCubebMSGLatencyInFrames);
 }
 
 void InitLibrary()
 {
   PrefChanged(PREF_VOLUME_SCALE, nullptr);
   Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE);
-  PrefChanged(PREF_CUBEB_LATENCY, nullptr);
-  Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
+  PrefChanged(PREF_CUBEB_LATENCY_PLAYBACK, nullptr);
+  PrefChanged(PREF_CUBEB_LATENCY_MSG, nullptr);
+  Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
+  Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
 #ifndef MOZ_WIDGET_ANDROID
   NS_DispatchToMainThread(NS_NewRunnableFunction(&InitBrandName));
 #endif
 }
 
 void ShutdownLibrary()
 {
   Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
-  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY);
+  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
+  Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
 
   StaticMutexAutoLock lock(sMutex);
   if (sCubebContext) {
     cubeb_destroy(sCubebContext);
     sCubebContext = nullptr;
   }
   sBrandName = nullptr;
   // This will ensure we don't try to re-create a context.
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -4,16 +4,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/. */
 
 #if !defined(CubebUtils_h_)
 #define CubebUtils_h_
 
 #include "cubeb/cubeb.h"
 #include "mozilla/dom/AudioChannelBinding.h"
+#include "mozilla/Maybe.h"
 
 namespace mozilla {
 namespace CubebUtils {
 
 typedef cubeb_devid AudioDeviceID;
 
 // Initialize Audio Library. Some Audio backends require initializing the
 // library before using it.
@@ -31,17 +32,18 @@ uint32_t PreferredSampleRate();
 
 void PrefChanged(const char* aPref, void* aClosure);
 double GetVolumeScale();
 bool GetFirstStream();
 cubeb* GetCubebContext();
 cubeb* GetCubebContextUnlocked();
 void ReportCubebStreamInitFailure(bool aIsFirstStream);
 void ReportCubebBackendUsed();
-uint32_t GetCubebLatency();
+uint32_t GetCubebPlaybackLatencyInMilliseconds();
+Maybe<uint32_t> GetCubebMSGLatencyInFrames();
 bool CubebLatencyPrefSet();
 #if defined(__ANDROID__) && defined(MOZ_B2G)
 cubeb_stream_type ConvertChannelToCubebType(dom::AudioChannel aChannel);
 #endif
 void GetCurrentBackend(nsAString& aBackend);
 
 } // namespace CubebUtils
 } // namespace mozilla
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -623,19 +623,24 @@ AudioCallbackDriver::Init()
 
   output.channels = mGraphImpl->AudioChannelCount();
   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
     output.format = CUBEB_SAMPLE_S16NE;
   } else {
     output.format = CUBEB_SAMPLE_FLOAT32NE;
   }
 
-  if (cubeb_get_min_latency(cubebContext, output, &latency_frames) != CUBEB_OK) {
-    NS_WARNING("Could not get minimal latency from cubeb.");
-    return;
+  Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
+  if (latencyPref) {
+    latency_frames = latencyPref.value();
+  } else {
+    if (cubeb_get_min_latency(cubebContext, output, &latency_frames) != CUBEB_OK) {
+      NS_WARNING("Could not get minimal latency from cubeb.");
+      return;
+    }
   }
 
   input = output;
   input.channels = mInputChannels; // change to support optional stereo capture
 
   cubeb_stream* stream = nullptr;
   CubebUtils::AudioDeviceID input_id = nullptr, output_id = nullptr;
   // We have to translate the deviceID values to cubeb devid's since those can be
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -64,17 +64,16 @@ MediaFormatReader::MediaFormatReader(Abs
   , mVideo(this, MediaData::VIDEO_DATA,
            Preferences::GetUint("media.video-max-decode-error", 2))
   , mDemuxer(aDemuxer)
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
   , mPreviousDecodedKeyframeTime_us(sNoPreviousDecodedKeyframe)
   , mLayersBackendType(aLayersBackend)
   , mInitDone(false)
-  , mIsEncrypted(false)
   , mTrackDemuxersMayBlock(false)
   , mDemuxOnly(false)
   , mSeekScheduled(false)
   , mVideoFrameContainer(aVideoFrameContainer)
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
 }
@@ -323,19 +322,16 @@ MediaFormatReader::OnDemuxerInitDone(nsr
       mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock();
     } else {
       mAudio.mTrackDemuxer->BreakCycles();
       mAudio.mTrackDemuxer = nullptr;
     }
   }
 
   UniquePtr<EncryptionInfo> crypto = mDemuxer->GetCrypto();
-
-  mIsEncrypted = crypto && crypto->IsEncrypted();
-
   if (mDecoder && crypto && crypto->IsEncrypted()) {
     // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
     for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) {
       NS_DispatchToMainThread(
         new DispatchKeyNeededEvent(mDecoder, crypto->mInitDatas[i].mInitData, crypto->mInitDatas[i].mType));
     }
     mInfo.mCrypto = *crypto;
   }
@@ -359,16 +355,23 @@ MediaFormatReader::OnDemuxerInitDone(nsr
 
   mInitDone = true;
   RefPtr<MetadataHolder> metadata = new MetadataHolder();
   metadata->mInfo = mInfo;
   metadata->mTags = tags->Count() ? tags.release() : nullptr;
   mMetadataPromise.Resolve(metadata, __func__);
 }
 
+bool
+MediaFormatReader::IsEncrypted() const
+{
+  return (HasAudio() && mInfo.mAudio.mCrypto.mValid) ||
+         (HasVideo() && mInfo.mVideo.mCrypto.mValid);
+}
+
 void
 MediaFormatReader::OnDemuxerInitFailed(const MediaResult& aError)
 {
   mDemuxerInitRequest.Complete();
   mMetadataPromise.Reject(aError, __func__);
 }
 
 MediaResult
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -98,18 +98,18 @@ public:
   // Returns a string describing the state of the decoder data.
   // Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
   void SetVideoBlankDecode(bool aIsBlankDecode) override;
 
 private:
 
-  bool HasVideo() { return mVideo.mTrackDemuxer; }
-  bool HasAudio() { return mAudio.mTrackDemuxer; }
+  bool HasVideo() const { return mVideo.mTrackDemuxer; }
+  bool HasAudio() const { return mAudio.mTrackDemuxer; }
 
   bool IsWaitingOnCDMResource();
 
   bool InitDemuxer();
   // Notify the demuxer that new data has been received.
   // The next queued task calling GetBuffered() is guaranteed to have up to date
   // buffered ranges.
   void NotifyDemuxer();
@@ -521,21 +521,17 @@ private:
   static const int64_t sNoPreviousDecodedKeyframe = INT64_MAX;
 
   layers::LayersBackend mLayersBackendType;
 
   // Metadata objects
   // True if we've read the streams' metadata.
   bool mInitDone;
   MozPromiseHolder<MetadataPromise> mMetadataPromise;
-  bool IsEncrypted()
-  {
-    return mIsEncrypted;
-  }
-  bool mIsEncrypted;
+  bool IsEncrypted() const;
 
   // Set to true if any of our track buffers may be blocking.
   bool mTrackDemuxersMayBlock;
 
   // Set the demuxed-only flag.
   Atomic<bool> mDemuxOnly;
 
   // Seeking objects.
--- a/dom/media/eme/EMEUtils.cpp
+++ b/dom/media/eme/EMEUtils.cpp
@@ -149,9 +149,22 @@ KeySystemToGMPName(const nsAString& aKey
 }
 
 bool
 IsClearkeyKeySystem(const nsAString& aKeySystem)
 {
   return !CompareUTF8toUTF16(kEMEKeySystemClearkey, aKeySystem);
 }
 
+CDMType
+ToCDMTypeTelemetryEnum(const nsString& aKeySystem)
+{
+  if (!CompareUTF8toUTF16(kEMEKeySystemWidevine, aKeySystem)) {
+    return CDMType::eWidevine;
+  } else if (!CompareUTF8toUTF16(kEMEKeySystemClearkey, aKeySystem)) {
+    return CDMType::eClearKey;
+  } else if (!CompareUTF8toUTF16(kEMEKeySystemPrimetime, aKeySystem)) {
+    return CDMType::ePrimetime;
+  }
+  return CDMType::eUnknown;
+}
+
 } // namespace mozilla
--- a/dom/media/eme/EMEUtils.h
+++ b/dom/media/eme/EMEUtils.h
@@ -101,11 +101,21 @@ ArrayData
 GetArrayBufferViewOrArrayBufferData(const dom::ArrayBufferViewOrArrayBuffer& aBufferOrView);
 
 nsString
 KeySystemToGMPName(const nsAString& aKeySystem);
 
 bool
 IsClearkeyKeySystem(const nsAString& aKeySystem);
 
+enum CDMType {
+  eClearKey = 0,
+  ePrimetime = 1,
+  eWidevine = 2,
+  eUnknown = 3
+};
+
+CDMType
+ToCDMTypeTelemetryEnum(const nsString& aKeySystem);
+
 } // namespace mozilla
 
 #endif // EME_LOG_H_
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -194,16 +194,19 @@ MediaKeySession::GenerateRequest(const n
   if (data.IsEmpty()) {
     promise->MaybeReject(NS_ERROR_DOM_TYPE_ERR,
       NS_LITERAL_CSTRING("Empty initData passed to MediaKeySession.generateRequest()"));
     EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() failed, empty initData",
       this, NS_ConvertUTF16toUTF8(mSessionId).get());
     return promise.forget();
   }
 
+  Telemetry::Accumulate(Telemetry::VIDEO_CDM_GENERATE_REQUEST_CALLED,
+                        ToCDMTypeTelemetryEnum(mKeySystem));
+
   // Convert initData to base64 for easier logging.
   // Note: CreateSession() Move()s the data out of the array, so we have
   // to copy it here.
   nsAutoCString base64InitData(ToBase64(data));
   PromiseId pid = mKeys->StorePromise(promise);
   mKeys->GetCDMProxy()->CreateSession(Token(),
                                       mSessionType,
                                       pid,
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -396,36 +396,16 @@ MediaKeys::Init(ErrorResult& aRv)
                NS_ConvertUTF8toUTF16(origin),
                NS_ConvertUTF8toUTF16(topLevelOrigin),
                KeySystemToGMPName(mKeySystem),
                inPrivateBrowsing);
 
   return promise.forget();
 }
 
-enum CDMCreatedType {
-  eClearKey = 0,
-  ePrimetime = 1,
-  eWidevine = 2,
-  eUnknown = 3
-};
-
-static CDMCreatedType
-ToCDMCreatedTelemetryEnum(const nsString& aKeySystem)
-{
-  if (!CompareUTF8toUTF16(kEMEKeySystemWidevine, aKeySystem)) {
-    return CDMCreatedType::eWidevine;
-  } else if (!CompareUTF8toUTF16(kEMEKeySystemClearkey, aKeySystem)) {
-    return CDMCreatedType::eClearKey;
-  } else if (!CompareUTF8toUTF16(kEMEKeySystemPrimetime, aKeySystem)) {
-    return CDMCreatedType::ePrimetime;
-  }
-  return CDMCreatedType::eUnknown;
-}
-
 void
 MediaKeys::OnCDMCreated(PromiseId aId, const nsACString& aNodeId, const uint32_t aPluginId)
 {
   RefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
   mNodeId = aNodeId;
@@ -435,17 +415,17 @@ MediaKeys::OnCDMCreated(PromiseId aId, c
   if (mCreatePromiseId == aId) {
     Release();
   }
 
   MediaKeySystemAccess::NotifyObservers(mParent,
                                         mKeySystem,
                                         MediaKeySystemStatus::Cdm_created);
 
-  Telemetry::Accumulate(Telemetry::VIDEO_CDM_CREATED, ToCDMCreatedTelemetryEnum(mKeySystem));
+  Telemetry::Accumulate(Telemetry::VIDEO_CDM_CREATED, ToCDMTypeTelemetryEnum(mKeySystem));
 }
 
 already_AddRefed<MediaKeySession>
 MediaKeys::CreateSession(JSContext* aCx,
                          MediaKeySessionType aSessionType,
                          ErrorResult& aRv)
 {
   if (!mProxy) {
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1066,17 +1066,16 @@ TrackBuffersManager::OnDemuxerInitDone(n
       NS_DispatchToMainThread(
         new DispatchKeyNeededEvent(mParentDecoder, crypto->mInitDatas[i].mInitData,
                                    crypto->mInitDatas[i].mType));
     }
     info.mCrypto = *crypto;
     // We clear our crypto init data array, so the MediaFormatReader will
     // not emit an encrypted event for the same init data again.
     info.mCrypto.mInitDatas.Clear();
-    mEncrypted = true;
   }
 
   {
     MonitorAutoLock mon(mMonitor);
     mInfo = info;
   }
 
   // We now have a valid init data ; we can store it for later use.
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -235,17 +235,16 @@ private:
   // Length already processed in current media segment.
   uint32_t mProcessedInput;
   Maybe<media::TimeUnit> mLastParsedEndTime;
 
   void OnDemuxerInitDone(nsresult);
   void OnDemuxerInitFailed(const MediaResult& aFailure);
   void OnDemuxerResetDone(nsresult);
   MozPromiseRequestHolder<MediaDataDemuxer::InitPromise> mDemuxerInitRequest;
-  bool mEncrypted;
 
   void OnDemuxFailed(TrackType aTrack, const MediaResult& aError);
   void DoDemuxVideo();
   void OnVideoDemuxCompleted(RefPtr<MediaTrackDemuxer::SamplesHolder> aSamples);
   void OnVideoDemuxFailed(const MediaResult& aError)
   {
     mVideoTracks.mDemuxRequest.Complete();
     OnDemuxFailed(TrackType::kVideoTrack, aError);
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -24,43 +24,16 @@
     mozilla::LogLevel::Debug, ("AndroidDecoderModule(%p)::%s: " arg, \
       this, __func__, ##__VA_ARGS__))
 
 using namespace mozilla;
 using namespace mozilla::gl;
 using namespace mozilla::java::sdk;
 using media::TimeUnit;
 
-namespace {
-  template<class T>
-  mozilla::jni::ByteArray::LocalRef
-  CreateAndInitJByteArray(const T& data, jsize length)
-  {
-    JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
-    jbyteArray result = jenv->NewByteArray(length);
-    MOZ_CATCH_JNI_EXCEPTION(jenv);
-    jenv->SetByteArrayRegion(result, 0, length, reinterpret_cast<const jbyte*>(const_cast<T>(data)));
-    MOZ_CATCH_JNI_EXCEPTION(jenv);
-    return mozilla::jni::ByteArray::LocalRef::Adopt(jenv, result);
-  }
-
-  template<class T>
-  mozilla::jni::IntArray::LocalRef
-  CreateAndInitJIntArray(const T& data, jsize length)
-  {
-    JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
-    jintArray result = jenv->NewIntArray(length);
-    MOZ_CATCH_JNI_EXCEPTION(jenv);
-    jenv->SetIntArrayRegion(result, 0, length, reinterpret_cast<const jint*>(const_cast<T>(data)));
-    MOZ_CATCH_JNI_EXCEPTION(jenv);
-    return mozilla::jni::IntArray::LocalRef::Adopt(jenv, result);
-  }
-}
-
-
 namespace mozilla {
 
 mozilla::LazyLogModule sAndroidDecoderModuleLog("AndroidDecoderModule");
 
 static const char*
 TranslateMimeType(const nsACString& aMimeType)
 {
   if (VPXDecoder::IsVPX(aMimeType, VPXDecoder::VP8)) {
@@ -120,21 +93,27 @@ GetCryptoInfoFromSample(const MediaRawDa
   auto tempIV(cryptoObj.mIV);
   auto tempIVLength = tempIV.Length();
   MOZ_ASSERT(tempIVLength <= kExpectedIVLength);
   for (size_t i = tempIVLength; i < kExpectedIVLength; i++) {
     // Padding with 0
     tempIV.AppendElement(0);
   }
 
-  auto numBytesOfPlainData = CreateAndInitJIntArray(&plainSizes[0], plainSizes.Length());
-  auto numBytesOfEncryptedData = CreateAndInitJIntArray(&cryptoObj.mEncryptedSizes[0],
-                                                        cryptoObj.mEncryptedSizes.Length());
-  auto iv = CreateAndInitJByteArray(&tempIV[0], tempIV.Length());
-  auto keyId = CreateAndInitJByteArray(&cryptoObj.mKeyId[0], cryptoObj.mKeyId.Length());
+  auto numBytesOfPlainData = mozilla::jni::IntArray::New(
+                              reinterpret_cast<int32_t*>(&plainSizes[0]),
+                              plainSizes.Length());
+
+  auto numBytesOfEncryptedData =
+    mozilla::jni::IntArray::New(reinterpret_cast<const int32_t*>(&cryptoObj.mEncryptedSizes[0]),
+                                cryptoObj.mEncryptedSizes.Length());
+  auto iv = mozilla::jni::ByteArray::New(reinterpret_cast<int8_t*>(&tempIV[0]),
+                                        tempIV.Length());
+  auto keyId = mozilla::jni::ByteArray::New(reinterpret_cast<const int8_t*>(&cryptoObj.mKeyId[0]),
+                                            cryptoObj.mKeyId.Length());
   cryptoInfo->Set(numSubSamples,
                   numBytesOfPlainData,
                   numBytesOfEncryptedData,
                   keyId,
                   iv,
                   MediaCodec::CRYPTO_MODE_AES_CTR);
 
   return cryptoInfo;
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -47,34 +47,34 @@ public:
     : ITextureClientAllocationHelper(gfx::SurfaceFormat::UNKNOWN,
                                      aSize,
                                      BackendSelector::Content,
                                      TextureFlags::DEALLOCATE_CLIENT,
                                      ALLOC_DISALLOW_BUFFERTEXTURECLIENT)
     , mGrallocFormat(aGrallocFormat)
   {}
 
-  already_AddRefed<TextureClient> Allocate(TextureForwarder* aAllocator) override
+  already_AddRefed<TextureClient> Allocate(KnowsCompositor* aAllocator) override
   {
     uint32_t usage = android::GraphicBuffer::USAGE_SW_READ_OFTEN |
                      android::GraphicBuffer::USAGE_SW_WRITE_OFTEN |
                      android::GraphicBuffer::USAGE_HW_TEXTURE;
 
     GrallocTextureData* texData = GrallocTextureData::Create(mSize, mGrallocFormat,
                                                              gfx::BackendType::NONE,
-                                                             usage, aAllocator);
+                                                             usage, aAllocator->GetTextureForwarder());
     if (!texData) {
       return nullptr;
     }
     sp<GraphicBuffer> graphicBuffer = texData->GetGraphicBuffer();
     if (!graphicBuffer.get()) {
       return nullptr;
     }
     RefPtr<TextureClient> textureClient =
-      TextureClient::CreateWithData(texData, TextureFlags::DEALLOCATE_CLIENT, aAllocator);
+      TextureClient::CreateWithData(texData, TextureFlags::DEALLOCATE_CLIENT, aAllocator->GetTextureForwarder());
     return textureClient.forget();
   }
 
   bool IsCompatible(TextureClient* aTextureClient) override
   {
     if (!aTextureClient) {
       return false;
     }
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -925,8 +925,9 @@ tags = webvtt
 [test_background_video_suspend.html]
 tags = suspend
 [test_background_video_suspend_ends.html]
 tags = suspend
 [test_background_video_no_suspend_short_vid.html]
 tags = suspend
 [test_background_video_no_suspend_disabled.html]
 tags = suspend
+[test_temporary_file_blob_video_plays.html]
copy from dom/media/test/test_mediarecorder_record_canvas_captureStream.html
copy to dom/media/test/test_temporary_file_blob_video_plays.html
--- a/dom/media/test/test_mediarecorder_record_canvas_captureStream.html
+++ b/dom/media/test/test_temporary_file_blob_video_plays.html
@@ -60,14 +60,14 @@ function startTest() {
       .then(SimpleTest.finish);
   };
 
   mediaRecorder.start();
   is(mediaRecorder.state, "recording", "Media recorder should be recording");
 }
 
 SimpleTest.waitForExplicitFinish();
-startTest();
+SpecialPowers.pushPrefEnv({set:[["media.recorder.max_memory", 1]]}, startTest);
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -248,19 +248,17 @@ inline bool IsDrawingModelDirect(int16_t
 }
 
 // in NPAPI, char* == nullptr is sometimes meaningful.  the following is
 // helper code for dealing with nullable nsCString's
 inline nsCString
 NullableString(const char* aString)
 {
     if (!aString) {
-        nsCString str;
-        str.SetIsVoid(true);
-        return str;
+        return NullCString();
     }
     return nsCString(aString);
 }
 
 inline const char*
 NullableStringGet(const nsCString& str)
 {
   if (str.IsVoid())
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -15,16 +15,17 @@ EXPORTS.mozilla.dom += [
     'nsMixedContentBlocker.h',
     'SRICheck.h',
     'SRILogHelper.h',
     'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
+    'nsMixedContentBlocker.h',
 ]
 
 UNIFIED_SOURCES += [
     'ContentVerifier.cpp',
     'nsContentSecurityManager.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
--- a/dom/security/nsContentSecurityManager.cpp
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -1,16 +1,18 @@
 #include "nsContentSecurityManager.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIStreamListener.h"
 #include "nsILoadInfo.h"
 #include "nsContentUtils.h"
 #include "nsCORSListenerProxy.h"
 #include "nsIStreamListener.h"
+#include "nsIDocument.h"
+#include "nsMixedContentBlocker.h"
 
 #include "mozilla/dom/Element.h"
 
 NS_IMPL_ISUPPORTS(nsContentSecurityManager,
                   nsIContentSecurityManager,
                   nsIChannelEventSink)
 
 static nsresult
@@ -376,16 +378,24 @@ DoContentSecurityChecks(nsIChannel* aCha
                                  nullptr,        //extra,
                                  &shouldLoad,
                                  nsContentUtils::GetContentPolicy(),
                                  nsContentUtils::GetSecurityManager());
   NS_ENSURE_SUCCESS(rv, rv);
   if (NS_CP_REJECTED(shouldLoad)) {
     return NS_ERROR_CONTENT_BLOCKED;
   }
+
+  if (nsMixedContentBlocker::sSendHSTSPriming) {
+    rv = nsMixedContentBlocker::MarkLoadInfoForPriming(uri,
+                                                       requestingContext,
+                                                       aLoadInfo);
+    return rv;
+  }
+
   return NS_OK;
 }
 
 /*
  * Based on the security flags provided in the loadInfo of the channel,
  * doContentSecurityCheck() performs the following content security checks
  * before opening the channel:
  *
@@ -484,17 +494,17 @@ nsContentSecurityManager::AsyncOnChannel
       nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
       nsIScriptSecurityManager::DISALLOW_SCRIPT;
   nsresult rv = nsContentUtils::GetSecurityManager()->
     CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
   if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
       rv = nsContentUtils::GetSecurityManager()->
         CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
   }
-  NS_ENSURE_SUCCESS(rv, rv);  
+  NS_ENSURE_SUCCESS(rv, rv);
 
   aCb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 static void
 AddLoadFlags(nsIRequest *aRequest, nsLoadFlags aNewFlags)
 {
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -49,16 +49,21 @@ enum nsMixedContentBlockerMessageType {
 
 // Is mixed script blocking (fonts, plugin content, scripts, stylesheets,
 // iframes, websockets, XHR) enabled?
 bool nsMixedContentBlocker::sBlockMixedScript = false;
 
 // Is mixed display content blocking (images, audio, video, <a ping>) enabled?
 bool nsMixedContentBlocker::sBlockMixedDisplay = false;
 
+// Do we move HSTS before mixed-content
+bool nsMixedContentBlocker::sUseHSTS = false;
+// Do we send an HSTS priming request
+bool nsMixedContentBlocker::sSendHSTSPriming = false;
+
 // Fired at the document that attempted to load mixed content.  The UI could
 // handle this event, for example, by displaying an info bar that offers the
 // choice to reload the page with mixed content permitted.
 class nsMixedContentEvent : public Runnable
 {
 public:
   nsMixedContentEvent(nsISupports *aContext, MixedContentTypes aType, bool aRootHasSecureConnection)
     : mContext(aContext), mType(aType), mRootHasSecureConnection(aRootHasSecureConnection)
@@ -190,16 +195,24 @@ nsMixedContentBlocker::nsMixedContentBlo
 {
   // Cache the pref for mixed script blocking
   Preferences::AddBoolVarCache(&sBlockMixedScript,
                                "security.mixed_content.block_active_content");
 
   // Cache the pref for mixed display blocking
   Preferences::AddBoolVarCache(&sBlockMixedDisplay,
                                "security.mixed_content.block_display_content");
+
+  // Cache the pref for HSTS
+  Preferences::AddBoolVarCache(&sUseHSTS,
+                               "security.mixed_content.use_hsts");
+
+  // Cache the pref for sending HSTS priming
+  Preferences::AddBoolVarCache(&sSendHSTSPriming,
+                               "security.mixed_content.send_hsts_priming");
 }
 
 nsMixedContentBlocker::~nsMixedContentBlocker()
 {
 }
 
 NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy, nsIChannelEventSink)
 
@@ -233,18 +246,16 @@ LogMixedContentMessage(MixedContentTypes
 
   NS_ConvertUTF8toUTF16 locationSpecUTF16(aContentLocation->GetSpecOrDefault());
   const char16_t* strings[] = { locationSpecUTF16.get() };
   nsContentUtils::ReportToConsole(severityFlag, messageCategory, aRootDoc,
                                   nsContentUtils::eSECURITY_PROPERTIES,
                                   messageLookupKey.get(), strings, ArrayLength(strings));
 }
 
-
-
 /* nsIChannelEventSink implementation
  * This code is called when a request is redirected.
  * We check the channel associated with the new uri is allowed to load
  * in the current context
  */
 NS_IMETHODIMP
 nsMixedContentBlocker::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
                                               nsIChannel* aNewChannel,
@@ -304,27 +315,45 @@ nsMixedContentBlocker::AsyncOnChannelRed
     if (nsContentUtils::IsSystemPrincipal(requestingPrincipal)) {
       return NS_OK;
     }
     // We set the requestingLocation from the RequestingPrincipal.
     rv = requestingPrincipal->GetURI(getter_AddRefs(requestingLocation));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  nsCOMPtr<nsISupports> requestingContext = loadInfo->LoadingNode();
+
   int16_t decision = REJECT_REQUEST;
   rv = ShouldLoad(contentPolicyType,
                   newUri,
                   requestingLocation,
-                  loadInfo->LoadingNode(),
+                  requestingContext,
                   EmptyCString(),       // aMimeGuess
                   nullptr,              // aExtra
                   requestingPrincipal,
                   &decision);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (nsMixedContentBlocker::sSendHSTSPriming) {
+    // The LoadInfo passed in is for the original channel, HSTS priming needs to
+    // be set on the new channel, if required. If the redirect changes
+    // http->https, or vice-versa, the need for priming may change.
+    nsCOMPtr<nsILoadInfo> newLoadInfo;
+    rv = aNewChannel->GetLoadInfo(getter_AddRefs(newLoadInfo));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = nsMixedContentBlocker::MarkLoadInfoForPriming(newUri,
+                                                       requestingContext,
+                                                       newLoadInfo);
+    if (NS_FAILED(rv)) {
+      decision = REJECT_REQUEST;
+      newLoadInfo->ClearHSTSPriming();
+    }
+  }
+
   // If the channel is about to load mixed content, abort the channel
   if (!NS_CP_ACCEPTED(decision)) {
     autoCallback.DontCallback();
     return NS_BINDING_FAILED;
   }
 
   return NS_OK;
 }
@@ -458,17 +487,16 @@ nsMixedContentBlocker::ShouldLoad(bool a
       return NS_OK;
     // Creating insecure websocket connections in a secure page is blocked already
     // in the websocket constructor. We don't need to check the blocking here
     // and we don't want to un-block
     case TYPE_WEBSOCKET:
       *aDecision = ACCEPT;
       return NS_OK;
 
-
     // Static display content is considered moderate risk for mixed content so
     // these will be blocked according to the mixed display preference
     case TYPE_IMAGE:
     case TYPE_MEDIA:
     case TYPE_OBJECT_SUBREQUEST:
       classification = eMixedDisplay;
       break;
 
@@ -492,17 +520,16 @@ nsMixedContentBlocker::ShouldLoad(bool a
     case TYPE_XSLT:
     case TYPE_OTHER:
       break;
 
 
     // This content policy works as a whitelist.
     default:
       MOZ_ASSERT(false, "Mixed content of unknown type");
-      break;
   }
 
   // Make sure to get the URI the load started with. No need to check
   // outer schemes because all the wrapping pseudo protocols inherit the
   // security properties of the actual network request represented
   // by the innerMost URL.
   nsCOMPtr<nsIURI> innerContentLocation = NS_GetInnermostURI(aContentLocation);
   if (!innerContentLocation) {
@@ -672,43 +699,45 @@ nsMixedContentBlocker::ShouldLoad(bool a
   // http: and ws: (for websockets). Websockets are not subject to mixed content
   // blocking since insecure websockets are not allowed within secure pages. Hence,
   // we only have to check against http: here. Skip mixed content blocking if the
   // subresource load uses http: and the CSP directive 'upgrade-insecure-requests'
   // is present on the page.
   bool isHttpScheme = false;
   rv = innerContentLocation->SchemeIs("http", &isHttpScheme);
   NS_ENSURE_SUCCESS(rv, rv);
-  if (isHttpScheme && docShell->GetDocument()->GetUpgradeInsecureRequests(isPreload)) {
+  nsIDocument* document = docShell->GetDocument();
+  MOZ_ASSERT(document, "Expected a document");
+  if (isHttpScheme && document->GetUpgradeInsecureRequests(isPreload)) {
     *aDecision = ACCEPT;
     return NS_OK;
   }
 
   // The page might have set the CSP directive 'block-all-mixed-content' which
   // should block not only active mixed content loads but in fact all mixed content
   // loads, see https://www.w3.org/TR/mixed-content/#strict-checking
   // Block all non secure loads in case the CSP directive is present. Please note
   // that at this point we already know, based on |schemeSecure| that the load is
   // not secure, so we can bail out early at this point.
-  if (docShell->GetDocument()->GetBlockAllMixedContent(isPreload)) {
+  if (document->GetBlockAllMixedContent(isPreload)) {
     // log a message to the console before returning.
     nsAutoCString spec;
     rv = aContentLocation->GetSpec(spec);
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ConvertUTF8toUTF16 reportSpec(spec);
 
     const char16_t* params[] = { reportSpec.get()};
     CSP_LogLocalizedStr(u"blockAllMixedContent",
                         params, ArrayLength(params),
                         EmptyString(), // aSourceFile
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::errorFlag, "CSP",
-                        docShell->GetDocument()->InnerWindowID());
+                        document->InnerWindowID());
     *aDecision = REJECT_REQUEST;
     return NS_OK;
   }
 
   // Determine if the rootDoc is https and if the user decided to allow Mixed Content
   bool rootHasSecureConnection = false;
   bool allowMixedContent = false;
   bool isRootDocShell = false;
@@ -791,39 +820,67 @@ nsMixedContentBlocker::ShouldLoad(bool a
   // If there is no securityUI, document doesn't have a security state.
   // Allow load and return early.
   if (!securityUI) {
     *aDecision = nsIContentPolicy::ACCEPT;
     return NS_OK;
   }
   nsresult stateRV = securityUI->GetState(&state);
 
+  bool doHSTSPriming = false;
+  if (isHttpScheme) {
+    bool hsts = false;
+    bool cached = false;
+    nsCOMPtr<nsISiteSecurityService> sss =
+      do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aContentLocation,
+        0, &cached, &hsts);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    if (hsts && sUseHSTS) {
+      // assume we will be upgraded later
+      *aDecision = ACCEPT;
+      return NS_OK;
+    }
+
+    // Send a priming request if the result is not already cached and priming
+    // requests are allowed
+    if (!cached && sSendHSTSPriming) {
+      // add this URI as a priming location
+      doHSTSPriming = true;
+      document->AddHSTSPrimingLocation(innerContentLocation,
+          HSTSPrimingState::eHSTS_PRIMING_ALLOW);
+      *aDecision = ACCEPT;
+    }
+  }
+
   // At this point we know that the request is mixed content, and the only
   // question is whether we block it.  Record telemetry at this point as to
   // whether HSTS would have fixed things by making the content location
   // into an HTTPS URL.
   //
   // Note that we count this for redirects as well as primary requests. This
   // will cause some degree of double-counting, especially when mixed content
   // is not blocked (e.g., for images).  For more detail, see:
   //   https://bugzilla.mozilla.org/show_bug.cgi?id=1198572#c19
   //
   // We do not count requests aHadInsecureImageRedirect=true, since these are
   // just an artifact of the image caching system.
   bool active = (classification == eMixedScript);
   if (!aHadInsecureImageRedirect) {
     if (XRE_IsParentProcess()) {
-      AccumulateMixedContentHSTS(innerContentLocation, active);
+      AccumulateMixedContentHSTS(innerContentLocation, active, doHSTSPriming);
     } else {
       // Ask the parent process to do the same call
       mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton();
       if (cc) {
         mozilla::ipc::URIParams uri;
         SerializeURI(innerContentLocation, uri);
-        cc->SendAccumulateMixedContentHSTS(uri, active);
+        cc->SendAccumulateMixedContentHSTS(uri, active, doHSTSPriming);
       }
     }
   }
 
   // set hasMixedContentObjectSubrequest on this object if necessary
   if (aContentType == TYPE_OBJECT_SUBREQUEST) {
     rootDoc->SetHasMixedContentObjectSubrequest(true);
   }
@@ -856,17 +913,23 @@ nsMixedContentBlocker::ShouldLoad(bool a
       } else {
         // User has overriden the pref and the root is not https;
         // mixed display content was allowed on an https subframe.
         if (NS_SUCCEEDED(stateRV)) {
           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT));
         }
       }
     } else {
-      *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      if (doHSTSPriming) {
+        document->AddHSTSPrimingLocation(innerContentLocation,
+            HSTSPrimingState::eHSTS_PRIMING_BLOCK);
+        *aDecision = nsIContentPolicy::ACCEPT;
+      } else {
+        *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      }
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
       if (!rootDoc->GetHasMixedDisplayContentBlocked() && NS_SUCCEEDED(stateRV)) {
         rootDoc->SetHasMixedDisplayContentBlocked(true);
         eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT));
       }
     }
     return NS_OK;
 
@@ -902,32 +965,37 @@ nsMixedContentBlocker::ShouldLoad(bool a
         // mixed active content was allowed on an https subframe.
         if (NS_SUCCEEDED(stateRV)) {
           eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT));
         }
         return NS_OK;
       }
     } else {
       //User has not overriden the pref by Disabling protection. Reject the request and update the security state.
-      *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      if (doHSTSPriming) {
+        document->AddHSTSPrimingLocation(innerContentLocation,
+            HSTSPrimingState::eHSTS_PRIMING_BLOCK);
+        *aDecision = nsIContentPolicy::ACCEPT;
+      } else {
+        *aDecision = nsIContentPolicy::REJECT_REQUEST;
+      }
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eBlocked);
       // See if the pref will change here. If it will, only then do we need to call OnSecurityChange() to update the UI.
       if (rootDoc->GetHasMixedActiveContentBlocked()) {
         return NS_OK;
       }
       rootDoc->SetHasMixedActiveContentBlocked(true);
 
       // The user has not overriden the pref, so make sure they still have an option by calling eventSink
       // which will invoke the doorhanger
       if (NS_SUCCEEDED(stateRV)) {
          eventSink->OnSecurityChange(aRequestingContext, (state | nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT));
       }
       return NS_OK;
     }
-
   } else {
     // The content is not blocked by the mixed content prefs.
 
     // Log a message that we are loading mixed content.
     LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
 
     // Fire the event from a script runner as it is unsafe to run script
     // from within ShouldLoad
@@ -968,51 +1036,150 @@ nsMixedContentBlocker::ShouldProcess(uin
 
 enum MixedContentHSTSState {
   MCB_HSTS_PASSIVE_NO_HSTS   = 0,
   MCB_HSTS_PASSIVE_WITH_HSTS = 1,
   MCB_HSTS_ACTIVE_NO_HSTS    = 2,
   MCB_HSTS_ACTIVE_WITH_HSTS  = 3
 };
 
+// Similar to the existing mixed-content HSTS, except MCB_HSTS_*_NO_HSTS is
+// broken into two distinct states, indicating whether we plan to send a priming
+// request or not. If we decided not go send a priming request, it could be
+// because it is a type we do not support, or because we cached a previous
+// negative response.
+enum MixedContentHSTSPrimingState {
+  eMCB_HSTS_PASSIVE_WITH_HSTS  = 0,
+  eMCB_HSTS_ACTIVE_WITH_HSTS   = 1,
+  eMCB_HSTS_PASSIVE_NO_PRIMING = 2,
+  eMCB_HSTS_PASSIVE_DO_PRIMING = 3,
+  eMCB_HSTS_ACTIVE_NO_PRIMING  = 4,
+  eMCB_HSTS_ACTIVE_DO_PRIMING  = 5
+};
+
 // Record information on when HSTS would have made mixed content not mixed
 // content (regardless of whether it was actually blocked)
 void
-nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive)
+nsMixedContentBlocker::AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive, bool aHasHSTSPriming)
 {
   // This method must only be called in the parent, because
   // nsSiteSecurityService is only available in the parent
   if (!XRE_IsParentProcess()) {
     MOZ_ASSERT(false);
     return;
   }
 
   bool hsts;
   nsresult rv;
   nsCOMPtr<nsISiteSecurityService> sss = do_GetService(NS_SSSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return;
   }
-  rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0, &hsts);
+  rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, aURI, 0, nullptr, &hsts);
   if (NS_FAILED(rv)) {
     return;
   }
 
+  // states: would upgrade, would prime, hsts info cached
+  // active, passive
+  //
   if (!aActive) {
     if (!hsts) {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_PASSIVE_NO_HSTS);
+      if (aHasHSTSPriming) {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_PASSIVE_DO_PRIMING);
+      } else {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_PASSIVE_NO_PRIMING);
+      }
     }
     else {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_PASSIVE_WITH_HSTS);
+      Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                            eMCB_HSTS_PASSIVE_WITH_HSTS);
     }
   } else {
     if (!hsts) {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_ACTIVE_NO_HSTS);
+      if (aHasHSTSPriming) {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_ACTIVE_DO_PRIMING);
+      } else {
+        Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                              eMCB_HSTS_ACTIVE_NO_PRIMING);
+      }
     }
     else {
       Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS,
                             MCB_HSTS_ACTIVE_WITH_HSTS);
+      Telemetry::Accumulate(Telemetry::MIXED_CONTENT_HSTS_PRIMING,
+                            eMCB_HSTS_ACTIVE_WITH_HSTS);
     }
   }
 }
+
+//static
+nsresult
+nsMixedContentBlocker::MarkLoadInfoForPriming(nsIURI* aURI,
+                                              nsISupports* aRequestingContext,
+                                              nsILoadInfo* aLoadInfo)
+{
+  nsresult rv;
+  bool sendPriming = false;
+  bool mixedContentWouldBlock = false;
+  rv = GetHSTSPrimingFromRequestingContext(aURI,
+                                           aRequestingContext,
+                                           &sendPriming,
+                                           &mixedContentWouldBlock);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (sendPriming) {
+    aLoadInfo->SetHSTSPriming(mixedContentWouldBlock);
+  }
+
+  return NS_OK;
+}
+
+//static
+nsresult
+nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(nsIURI* aURI,
+    nsISupports* aRequestingContext,
+    bool* aSendPrimingRequest,
+    bool* aMixedContentWouldBlock)
+{
+  *aSendPrimingRequest = false;
+  *aMixedContentWouldBlock = false;
+  // If we marked for priming, we used the innermost URI, so get that
+  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(aURI);
+  if (!innerURI) {
+    NS_ERROR("Can't get innerURI from aContentLocation");
+    return NS_ERROR_CONTENT_BLOCKED;
+  }
+
+  bool isHttp = false;
+  innerURI->SchemeIs("http", &isHttp);
+  if (!isHttp) {
+    // there is nothign to do
+    return NS_OK;
+  }
+
+  // If the DocShell was marked for HSTS priming, propagate that to the LoadInfo
+  nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(aRequestingContext);
+  if (!docShell) {
+    return NS_OK;
+  }
+  nsCOMPtr<nsIDocument> document = docShell->GetDocument();
+  if (!document) {
+    return NS_OK;
+  }
+
+  HSTSPrimingState status = document->GetHSTSPrimingStateForLocation(innerURI);
+  if (status != HSTSPrimingState::eNO_HSTS_PRIMING) {
+    *aSendPrimingRequest = (status != HSTSPrimingState::eNO_HSTS_PRIMING);
+    *aMixedContentWouldBlock = (status == HSTSPrimingState::eHSTS_PRIMING_BLOCK);
+  }
+
+  return NS_OK;
+}
--- a/dom/security/nsMixedContentBlocker.h
+++ b/dom/security/nsMixedContentBlocker.h
@@ -23,16 +23,18 @@ enum MixedContentTypes {
   eMixedDisplay
 };
 
 #include "nsIContentPolicy.h"
 #include "nsIChannel.h"
 #include "nsIChannelEventSink.h"
 #include "imgRequest.h"
 
+class nsILoadInfo; // forward declaration
+
 class nsMixedContentBlocker : public nsIContentPolicy,
                               public nsIChannelEventSink
 {
 private:
   virtual ~nsMixedContentBlocker();
 
 public:
   NS_DECL_ISUPPORTS
@@ -54,14 +56,46 @@ public:
                              uint32_t aContentType,
                              nsIURI* aContentLocation,
                              nsIURI* aRequestingLocation,
                              nsISupports* aRequestingContext,
                              const nsACString& aMimeGuess,
                              nsISupports* aExtra,
                              nsIPrincipal* aRequestPrincipal,
                              int16_t* aDecision);
-  static void AccumulateMixedContentHSTS(nsIURI* aURI, bool aActive);
+  static void AccumulateMixedContentHSTS(nsIURI* aURI,
+                                         bool aActive,
+                                         bool aHasHSTSPriming);
+  /* If the document associated with aRequestingContext requires priming for
+   * aURI, propagate that to the LoadInfo so the HttpChannel will find out about
+   * it.
+   *
+   * @param aURI The URI associated with the load
+   * @param aRequestingContext the requesting context passed to ShouldLoad
+   * @param aLoadInfo the LoadInfo for the load
+   */
+  static nsresult MarkLoadInfoForPriming(nsIURI* aURI,
+                                         nsISupports* aRequestingContext,
+                                         nsILoadInfo* aLoadInfo);
+
+  /* Given a context, return whether HSTS was marked on the document associated
+   * with the load for the given URI. This is used by MarkLoadInfoForPriming and
+   * directly by the image loader to determine whether to allow a load to occur
+   * from the cache.
+   *
+   * @param aURI The URI associated with the load
+   * @param aRequestingContext the requesting context passed to ShouldLoad
+   * @param aSendPrimingRequest out true if priming is required on the channel
+   * @param aMixedContentWouldBlock out true if mixed content would block
+   */
+  static nsresult GetHSTSPrimingFromRequestingContext(nsIURI* aURI,
+                                                      nsISupports* aRequestingContext,
+                                                      bool* aSendPrimingRequest,
+                                                      bool* aMixedContentWouldBlock);
+
+
   static bool sBlockMixedScript;
   static bool sBlockMixedDisplay;
+  static bool sUseHSTS;
+  static bool sSendHSTSPriming;
 };
 
 #endif /* nsMixedContentBlocker_h___ */
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/browser.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+support-files =
+  file_priming-top.html
+  file_testserver.sjs
+  file_1x1.png
+  file_priming.js
+  file_stylesheet.css
+
+[browser_hsts-priming_main.js]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/browser_hsts-priming_main.js
@@ -0,0 +1,296 @@
+/*
+ * Description of the test:
+ *   Check that HSTS priming occurs correctly with mixed content
+ *
+ *   This test uses three hostnames, each of which treats an HSTS priming
+ *   request differently.
+ *   * no-ssl never returns an ssl response
+ *   * reject-upgrade returns an ssl response, but with no STS header
+ *   * prime-hsts returns an ssl response with the appropriate STS header
+ *
+ *   For each server, test that it response appropriately when the we allow
+ *   or block active or display content, as well as when we send an hsts priming
+ *   request, but do not change the order of mixed-content and HSTS.
+ *
+ *   This test uses http-on-examine-response, so must be run in browser context.
+ */
+'use strict';
+
+var TOP_URI = "https://example.com/browser/dom/security/test/hsts/file_priming-top.html";
+
+var test_servers = {
+  // a test server that does not support TLS
+  'no-ssl': {
+    host: 'example.co.jp',
+    response: false,
+    id: 'no-ssl',
+  },
+  // a test server which does not support STS upgrade
+  'reject-upgrade': {
+    host: 'example.org',
+    response: true,
+    id: 'reject-upgrade',
+  },
+  // a test server when sends an STS header when priming
+  'prime-hsts': {
+    host: 'test1.example.com',
+    response: true,
+    id: 'prime-hsts'
+  },
+};
+// The number of priming responses we expect to see
+var priming_count = 2;
+
+var test_settings = {
+  // mixed active content is allowed, priming will upgrade
+  allow_active: {
+    block_active: false,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'script',
+    result: {
+      'no-ssl': 'insecure',
+      'reject-upgrade': 'insecure',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed active content is blocked, priming will upgrade
+  block_active: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'script',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+  // keep the original order of mixed-content and HSTS, but send
+  // priming requests
+  hsts_after_mixed: {
+    block_active: true,
+    block_display: false,
+    use_hsts: false,
+    send_hsts_priming: true,
+    type: 'script',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'blocked',
+    },
+  },
+  // mixed display content is allowed, priming will upgrade
+  allow_display: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'img',
+    result: {
+      'no-ssl': 'insecure',
+      'reject-upgrade': 'insecure',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed display content is blocked, priming will upgrade
+  block_display: {
+    block_active: true,
+    block_display: true,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'img',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed active content is blocked, priming will upgrade (css)
+  block_active_css: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'css',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+  // mixed active content is blocked, priming will upgrade
+  // redirect to the same host
+  block_active_with_redir_same: {
+    block_active: true,
+    block_display: false,
+    use_hsts: true,
+    send_hsts_priming: true,
+    type: 'script',
+    redir: 'same',
+    result: {
+      'no-ssl': 'blocked',
+      'reject-upgrade': 'blocked',
+      'prime-hsts': 'secure',
+    },
+  },
+}
+// track which test we are on
+var which_test = "";
+
+const Observer = {
+  observe: function (subject, topic, data) {
+    switch (topic) {
+      case 'console-api-log-event':
+        return Observer.console_api_log_event(subject, topic, data);
+      case 'http-on-examine-response':
+        return Observer.http_on_examine_response(subject, topic, data);
+    }
+    throw "Can't handle topic "+topic;
+  },
+  // When a load is blocked which results in an error event within a page, the
+  // test logs to the console.
+  console_api_log_event: function (subject, topic, data) {
+    var message = subject.wrappedJSObject.arguments[0];
+    // when we are blocked, this will match the message we sent to the console,
+    // ignore everything else.
+    var re = RegExp(/^HSTS_PRIMING: Blocked ([-\w]+).*$/);
+    if (!re.test(message)) {
+      return;
+    }
+
+    let id = message.replace(re, '$1');
+    let curTest =test_servers[id];
+
+    if (!curTest) {
+      ok(false, "HSTS priming got a console message blocked, "+
+          "but doesn't match expectations "+id+" (msg="+message);
+      return;
+    }
+
+    is("blocked", test_settings[which_test].result[curTest.id], "HSTS priming "+
+        which_test+":"+curTest.id+" expected "+
+        test_settings[which_test].result[curTest.id]+", got blocked");
+    test_settings[which_test].finished[curTest.id] = "blocked";
+  },
+  // When we see a response come back, peek at the response and test it is secure
+  // or insecure as needed. Addtionally, watch the response for priming requests.
+  http_on_examine_response: function (subject, topic, data) {
+    let curTest = null;
+    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+    for (let item in test_servers) {
+      let re = RegExp('https?://'+test_servers[item].host);
+      if (re.test(channel.URI.asciiSpec)) {
+        curTest = test_servers[item];
+        break;
+      }
+    }
+
+    if (!curTest) {
+      return;
+    }
+
+    let result = (channel.URI.asciiSpec.startsWith('https:')) ? "secure" : "insecure";
+
+    // This is a priming request, go ahead and validate we were supposed to see
+    // a response from the server
+    if (channel.requestMethod == 'HEAD') {
+      is(true, curTest.response, "HSTS priming response found " + curTest.id);
+      test_settings[which_test].priming[curTest.id] = true;
+      return;
+    }
+
+    // This is the response to our query, make sure it matches
+    is(result, test_settings[which_test].result[curTest.id],
+        "HSTS priming result " + which_test + ":" + curTest.id);
+    test_settings[which_test].finished[curTest.id] = result;
+  },
+};
+
+// opens `uri' in a new tab and focuses it.
+// returns the newly opened tab
+function openTab(uri) {
+  let tab = gBrowser.addTab(uri);
+
+  // select tab and make sure its browser is focused
+  gBrowser.selectedTab = tab;
+  tab.ownerDocument.defaultView.focus();
+
+  return tab;
+}
+
+function clear_sts_data() {
+  for (let test in test_servers) {
+    SpecialPowers.cleanUpSTSData('http://'+test_servers[test].host);
+  }
+}
+
+function do_cleanup() {
+  clear_sts_data();
+
+  Services.obs.removeObserver(Observer, "console-api-log-event");
+  Services.obs.removeObserver(Observer, "http-on-examine-response");
+}
+
+function SetupPrefTestEnvironment(which) {
+  which_test = which;
+  clear_sts_data();
+
+  var settings = test_settings[which];
+  // priming counts how many priming requests we saw
+  settings.priming = {};
+  // priming counts how many tests were finished
+  settings.finished= {};
+
+  SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.block_active_content",
+                                      settings.block_active],
+                                     ["security.mixed_content.block_display_content",
+                                      settings.block_display],
+                                     ["security.mixed_content.use_hsts",
+                                      settings.use_hsts],
+                                     ["security.mixed_content.send_hsts_priming",
+                                      settings.send_hsts_priming]]});
+}
+
+// make the top-level test uri
+function build_test_uri(base_uri, host, test_id, type) {
+  return base_uri +
+          "?host=" + escape(host) +
+          "&id=" + escape(test_id) +
+          "&type=" + escape(type);
+}
+
+// open a new tab, load the test, and wait for it to finish
+function execute_test(test, mimetype) {
+  var src = build_test_uri(TOP_URI, test_servers[test].host,
+      test, test_settings[which_test].type);
+
+  let tab = openTab(src);
+  test_servers[test]['tab'] = tab;
+
+  let browser = gBrowser.getBrowserForTab(tab);
+  yield BrowserTestUtils.browserLoaded(browser);
+
+  yield BrowserTestUtils.removeTab(tab);
+}
+
+//jscs:disable
+add_task(function*() {
+  //jscs:enable
+  Services.obs.addObserver(Observer, "console-api-log-event", false);
+  Services.obs.addObserver(Observer, "http-on-examine-response", false);
+  registerCleanupFunction(do_cleanup);
+  requestLongerTimeout(4);
+
+  for (let which of Object.keys(test_settings)) {
+    SetupPrefTestEnvironment(which);
+
+    for (let server of Object.keys(test_servers)) {
+      yield execute_test(server, test_settings[which].mimetype);
+    }
+
+    SpecialPowers.popPrefEnv();
+  }
+});
new file mode 100644
index 0000000000000000000000000000000000000000..1ba31ba1a62313908f41f844a9fb2e74663a4cd2
GIT binary patch
literal 17811
zc%1E<cT`i^x5p2?D5xkXWh4d_P*NxfNrWH}qzI@q6GR~-7l@Jsk_e%rG72K%fQq7G
zd4pg<aR57D0THEIU_d|_3quhEM7sQf=!k-|T<iVbKX2W$*3He`=Y01kcb~ma&dpkh
zTkB}8Ag?A508p^Cp*V@oV&jLbwCGh+xhPI_k_)h*@d21Icl;0osYe$8Ab*QxX}NZ-
zA6LNT`*8yhww9KN03O$e<qrWMtj{{=>*8Fuuz0M$>Al73*y#OLE1V?7Wn&zHRI8WP
z;{7XQ^%rOUsT?R-vVFI#n_#x&?MuL5cdXR26VzK|^M&$l1nO+%1O3~qg}pVyJvE_Y
zd7ak_^J<C19sI5og_U8qQtXN|rC08ypyL(0eYKY^$%(UD31sGB#a0v`*0;@9Ru%);
z?vKOaZPJq9#RXH(Wy*KQ@TqrlJ;Z>L6gYFo!(0*Y<N-_B2I2vGMQ}R)wG$DzNdtSD
zx03;IlL2LW0yz@Er;YGj0-V;vlK@Z$i0XlGnl$Ls0@=Nisyv`J7icW8_)RieMHY~^
z+ZW7^b^{=YNSyI2$bAXC4h(V0s!_f6t|2{xs>>D@&H!MaxYac${V3g$LT&5whX{8i
zf#2ajs{DYL7~*N&jJvLSw#Sr-w~>o(N%}6cc)66iPko=sZHq{+0m<cmyc}722y>)i
zc~Y;pHuW;r{WWGz<?gkE8Qm^N530{Iyxn@rt4XrO_uf+bfHa-R8p#%)l9)9Y<kq$t
zSmiF`U)Q5<Ejijy>M#EB6|JZ2&#Kayac52L<laIYK*u>}r92pQO>+u-!i)@zwW}<T
zU-$OFLvPnm6*IlfkF@RjA`r8;BgB!>3V|<XcD|5A&z485*?^P*vQ(Xo%jQDN2X&E7
zX9x-9$+8+9e^%^u<bXKa(vEJ=k!CRjh|ac2uIKnhpO6O>!kNG{@h7$*_qsMNM-dFk
zu56dtW^vH?LNfsGR*G%)+KyhZU6;Del(0ixC^mmPVkUBtN|F~@7BQE%QaYYaJipCC
zXJ=OS+y`_sjqRHW(%L8FD(*;K6*qo?Z`f6GA|_I(VEImN)f~x1NI}fDzmcnAiQe%9
zEsY`>ISb@Izr}L4<_5FnPOUts)xOXBcS&7qMI`S2#rvwS#CZy>77LO2_Ypk{2X}kx
zC_tyAGo?rhuTMn!5!B)<EDfuy{K)RnoK+92>~a;^6wC?Zs^Ltfhy+DO^g4YNpFMR9
z4LuNFlypg8zcZ_7cec}crE?|j*@_+L(?v~Qo&k~?5+(QQUfv8id6jS!T#>z^bVY9c
z-gEbGf1#T@{|XJXys9ZAZPMh%_ZRiPiyN3Z;vK%IV4ml%W(kFR3KP3ZqL8y?^|mj$
zeQdU!4U#$M;2iv%4@zpX>=?9#CjF4j;x;9ATvL$^^0<z+ic;|0_SlxV;Mmck%Eik{
zV_(_JFI|$n`1icuogS@oTHdPIx`eg=yg6PwEJ>@>qQ|L6rH9U&F{pCwxQSi$?^LIo
zx|jCrJy5e#O;6(;?uqHyW|yA2)-*Hc46<$WX4O7I-?F@vp<hp1<(!nNdh=*rZh5X&
zF23HfUWRmxW8U&8xs^BM6RI$Z99137QL`*JPsmv6vLW5=WKCv}_1i2Gt<j@#O+-Ox
zLFkzw+rABLT%-GHDr#bC;Ry%Sl+}EVLXCFfR8t*Oe@i8%9x+PXyy>w<6>>ApPl_d%
zC!edx+W4f6;Wg8F6|lTTEg_U$E&JP7>Ci#@g@*C8GLw59Ohwif)|%SAwVO_DJQ?A*
zq4W+_MQ`Eividuu+igA($Gow{T)X=H3C?X94TlX*8M^K|mwej!_s1vro-OCl8;os?
zHv}KyBzHQS9!*PV^3N|09e$5)&p-U&XdBKIo9EXScP8=ux(M}Azfovt*L!Z;EcFFy
zF$-)Ig48{XgpB(Juhe{0JF)&a4I}RbgA3yJ1T8pb99mrCGkmGI!{Eb`5Bn|^1ZG~k
zGjJj>qdPM$-S%jyd$M$L#_4&^ds)|86(0vZUSz11bJ*u$`R4M4hm*^RZw|k?QMRJY
zT2Bw9q=%;((PmQHs6BeUs4b}D)atD8EESrodt=U0D*t>=R=@l6%jRqkwgG#YKd0{0
zWzJ>fv;4~&gM=$)t%%#0zA@3Xys460Sx{3@yY2Pt=Iol2rOj`Ra#*F0FFUj!`f#`U
z>ADW#pWT;|4>})u?4F^L(O-eD5O0xh2}27~yi#1z<>;={&m7krDc}xCtl!ygU#oE8
z?S<!!4UJ4rA7@9WPp58YSVI`W-f^Dq+?KO4O?RI&4wTRmF=?DMUgg}hsF1oaDD0O$
zwL^5G7W-baYDCl0rtq#EO?H}>*X*to(@ej33G@8QqkdxT{^hUS0zHRl!_eT<!Ti49
z_bG$NKWNImn@OEhr4lQ(Q&L4LNSY;8EOkJ}bOv6oLiU2fLis=in#v=U-Ln*CHLBF#
zHGCl_#N9lJ?ax-Gr`%yR>o>n{uGI1uRS?Rs`Y_$_N@cjg!|;PlH|NEa9^@5ka>5^b
z+~@T@^S{%PS*M)iNm*fqN$iZJ7wN?k)p~Vn%z{>Tyd<0>Sb2oq3>|40NFDes=2lFS
zB=lEqk#d}I()%QeZFy<YtAST%Uo{iW`9T$}$J<F)0yzC94?|YJ*?8olv8TycgLt3R
zh?KVTJ6$<lYv+P)t-%8$%`ThT(w;0bkM>G=k+*Mo!y*m)UdQFOVTU`9ZAv2VYd&_#
zl!1M0oMY6RptI5i9h#z?nrF6U@d|@sn-7Nw<_-HSAJ~=>wW^bttI;SUkE=|2$`dzt
z9anUZM=rm5pPq-?f%sPy57`F|Upoyt{CdDU|CDj1_C;h@?_UGO+9`hM-}J6sd*ai2
zFCEjnqc@7SbHh?<O+kZjF!go*@)0);kGS*p8|w-l2wZQU{rGgzxpTE^YnT@YBeGuB
zC*^6@E;+wN7pp_vSapTWj9{O1KY2decSr5cQM2YqrO_wXs-c&i31+)vtZL(biC-OG
z$X4>d{xRoqC1jS!c6{D&t(g6=AS)&-DXZ~W_!ZX_zk{z*-m{Ni%MX+Zv##EoxMF_C
zO*c}$U<~~LU5tik6k0DQr*T7Y)bdKbLeFN;>TK(%%6q0-VZVlTb-e66_nezuC>*Pm
zI3+Plai@m=`P@uBQwm{!@ZZJF19yKZ42V2)=Fz!j7ltA!JPPfXjS*+=<)40&`LP<6
z=a+RVD4WA+e9PEYlG^^ZeHK1jZFl0`z~b)pg{MdABakDMVcUw*A?G(4ACCGQMQ^64
zrQj8D5MK94a_WtWm}4w%_h#bgk-J>$3bo@|gp3Hz>gDNqC$@Hke;jPavz{H9p*nx4
zetxc5!69LHQ+izr3A4+yAau#VgJQ+Py}j%6FQ(<c33*$#wW9B|I>%Uzd%ekI^hSGE
z#)?LgI``W1%>{1*Z=`QowZ8qA=PTxSN;l>65`(+$jnevWTz`38^L^Rdb>!LP1wx(3
zfzEYJ|5IIZ?)Z!2^Gu%#-G}VQXs<PEqKtGJ_qT*fgmKA<As?@<sp=onyf!va<N0p;
z$5Zdqs~@I{wQS4TaeK!V&+?F`W9~zt?;V<tr$%*#Z0Y;(#IvE0qqsz|uP`}ZJ*=>G
zN9Qgr&5S62&An=oA9PzD|7}z4+gSP3li*1zSQ;Kdygs1by`e6+y$fY_C(1<ip{jav
z4-^i-EdU(W$Oxk(qthgBT~%1QPQB|s0MUE;L!-MDd!C2fw3YzT>MKnFg(v3m3^?+{
z%ASG0*<hVVG!=aUjXvArnS7-4FOyCyw)m}-y$KOE0O2kX-leLp&Q?<&-Xs-mD(*LI
z)Ko48jM{+Weu8uirNMoyrERR^{+ML<YdM*-gfv?KNNAP|RX}yHCo#BeG@Z%yhR{NG
z02ps3X2JkEV+$lectgG{jw!P1Mj;ZxVwxh?;~cOK0hW*-%O;cuIfpvBFhaL5h)kpz
zS>8lQ5;<T)0y;v-_UG_PLQ~`?za-K7aWe*q_%uYY#T02i-hrSxtVLLIc@P4J#-kYe
z`uYfCB3d6uFf_pFAq=qkI1HAE(Z`~&`XqfU$pDM^+>m5>(W?oM=|gg&ton=-U6~^N
z1cCq(1``|{j1D$Lb9ufPeIk*F!5Ux;3{avGD1Hb>Ko_Doe60y5U;I!YK7+>!5U{u$
z#P~Jco4ZwDibTpICbiG^#SZuf#NmIAohXVJAw2-2kH%ttAaZc{cNv>Kt&%UW3KIOg
zlJ61{0AZXUK6fjR0a*n>9D&w!u9%E}`~+;}`G1NJlYxQ!A+|`u7x~hk_9Fy*dE}Y?
zWBk+o!@C!-e0-+Yj`#df1_@a|kd60D$jD!HCZZsdEO`)Jz~#Acx&GvdEc^PvCVE~Q
z&nkqD1D(O*j2m@LFyD)&^9NBx2U(=Wqp(IOEY3wAPr~9!1~?Sfn1sbnaxyXUJ0A|B
z>}S#i^q=wa&Dih#aG5Nhke~AMb>z1`92`it9KL|gVL-MNvdDmDv6v)?Kp@hYI6BG*
zkHw>m4C&q|f;SzD^7i&-;vlTP4}r<}<oH|p6xS3kW9xV-iTWl=pUGv2-2amxB)m6W
zU*FgWhcXnEJ4*DHh$1rSL=gxFG4+`^L&%W+1vJUo6wn$TOSCf4{U?nXzZa$m=7YuI
zaX!WdC??UHi83-WGDLafm_{g^D6DiWUW7C-{DPRq%@mXki!Vxz5b`(q`Dx8?h62B}
ze(CUMeOf~T=sZ3&UYe%JFSUuBA{f_vDsd8>F~0hc8K2UKX@dF2`ePYu>U92G2&Tw?
z%n0^_IRBP%lScj637_jD2&VHOb6-)Uf37yC6HbXZW+LSO2UEeP2mS9R;^*q>|H(vr
z1u*>R9AAh@#(WWeG5p-zO<2FJt}hikwHi&B43ZC*$EFL&EH>R2!US-9O)y`LlSY1B
zhb+1NT%Kr=gvg>on9e+{%GT0y?ZoeWWD1KP;7<>kES}UmNj?4rH@?n@B1Zp|K>8+_
zDYoy&Q^%9kf9t0{V*{+HiMB!5@d=*N{k@d-?^4>oNWXTua##Yg!Dq>L=1HpYZ{dlU
zIg2)o5E+ld8RASZ-<!WztYrxye=3C~T5<WGBQmXhS{>~NI@%Q7L>f)n*iGyjF`u`M
zq8%&dpDpWj$^74MFa(Cc5EueOU<eF>Aut4nzz`S$LtqFDfgvyihQJUQ0z+U341pmq
z1cty67y?6J2n>NCFa(Cc5EueOU<eF>Aut4nzz`S$Ltw~%86yAnPoNNoX^IR+nvwhS
zcIf~B5DXh92LL$Q0PrsYF_|0yLb3o@qXfj{AONs`05Ip?20l$+G<L2n#oR?$?>#U!
hW+oxJ9vhPYvLoUjAE$4nkN?%w*2<A`!NP0%e*nP=bK(F1
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_priming-top.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1246540</title>
+  <meta http-equiv='content-type' content="text/html;charset=utf-8" />
+</head>
+<body>
+  <p id="display"></p>
+  <div id="content" style="visibility: hidden">
+  </div>
+
+<script type="text/javascript">
+/*
+ * Description of the test:
+ * Attempt to load an insecure resource. If the resource responds to HSTS
+ * priming with an STS header, the load should continue securely.
+ * If it does not, the load should continue be blocked or continue insecurely.
+ */
+
+function parse_query_string() {
+  var q = {};
+  document.location.search.substr(1).
+    split('&').forEach(function (item, idx, ar) {
+      let [k, v] = item.split('=');
+      q[k] = unescape(v);
+    });
+  return q;
+}
+
+var args = parse_query_string();
+
+var subresources = {
+  css: { mimetype: 'text/css', file: 'file_stylesheet.css' },
+  img: { mimetype: 'image/png', file: 'file_1x1.png' },
+  script: { mimetype: 'text/javascript', file: 'file_priming.js' },
+};
+
+function handler(ev) {
+  console.log("HSTS_PRIMING: Blocked "+args.id);
+}
+
+function loadCss(src) {
+  let head = document.getElementsByTagName("head")[0];
+  let link = document.createElement("link");
+  link.setAttribute("rel", "stylesheet");
+  link.setAttribute("type", subresources[args.type].mimetype);
+  link.setAttribute("href", src);
+  head.appendChild(link);
+}
+
+function loadResource(src) {
+  let content = document.getElementById("content");
+  let testElem = document.createElement(args.type);
+  testElem.setAttribute("id", args.id);
+  testElem.setAttribute("charset", "UTF-8");
+  testElem.onerror = handler;
+  content.appendChild(testElem);
+  testElem.src = src;
+}
+
+function loadTest() {
+  let subresource = subresources[args.type];
+
+  let src = "http://"
+    + args.host
+    + "/browser/dom/security/test/hsts/file_testserver.sjs"
+    + "?file=" +escape("browser/dom/security/test/hsts/" + subresource.file)
+    + "&primer=" + escape(args.id)
+    + "&mimetype=" + escape(subresource.mimetype)
+    ;
+  if (args.type == 'css') {
+    loadCss(src);
+    return;
+  }
+
+  loadResource(src);
+}
+
+// start running the tests
+loadTest();
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_priming.js
@@ -0,0 +1,4 @@
+function completed() {
+  return;
+}
+completed();
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_testserver.sjs
@@ -0,0 +1,66 @@
+// SJS file for HSTS mochitests
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+function loadFromFile(path) {
+  // Load the HTML to return in the response from file.
+  // Since it's relative to the cwd of the test runner, we start there and
+  // append to get to the actual path of the file.
+  var testFile =
+    Components.classes["@mozilla.org/file/directory_service;1"].
+    getService(Components.interfaces.nsIProperties).
+    get("CurWorkD", Components.interfaces.nsILocalFile);
+  var dirs = path.split("/");
+  for (var i = 0; i < dirs.length; i++) {
+    testFile.append(dirs[i]);
+  }
+  var testFileStream =
+    Components.classes["@mozilla.org/network/file-input-stream;1"].
+    createInstance(Components.interfaces.nsIFileInputStream);
+  testFileStream.init(testFile, -1, 0, 0);
+  var test = NetUtil.readInputStreamToString(testFileStream, testFileStream.available());
+  return test;
+}
+
+function handleRequest(request, response)
+{
+  const query = new URLSearchParams(request.queryString);
+
+  redir = query.get('redir');
+  if (redir == 'same') {
+    query.delete("redir");
+    response.setStatus(302);
+    let newURI = request.uri;
+    newURI.queryString = query.serialize();
+    response.setHeader("Location", newURI.spec)
+  }
+
+  // avoid confusing cache behaviors
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  // if we have a priming header, check for required behavior
+  // and set header appropriately
+  if (request.hasHeader('Upgrade-Insecure-Requests')) {
+    var expected = query.get('primer');
+    if (expected == 'prime-hsts') {
+      // set it for 5 minutes
+      response.setHeader("Strict-Transport-Security", "max-age="+(60*5), false);
+    } else if (expected == 'reject-upgrade') {
+      response.setHeader("Strict-Transport-Security", "max-age=0", false);
+    }
+    response.write('');
+    return;
+  }
+
+  var file = query.get('file');
+  if (file) {
+    var mimetype = unescape(query.get('mimetype'));
+    response.setHeader("Content-Type", mimetype, false);
+    response.write(loadFromFile(unescape(file)));
+    return;
+  }
+
+  response.setHeader("Content-Type", "application/json", false);
+  response.write('{}');
+}
--- a/dom/security/test/mixedcontentblocker/test_main.html
+++ b/dom/security/test/mixedcontentblocker/test_main.html
@@ -157,16 +157,19 @@ https://bugzilla.mozilla.org/show_bug.cg
         testsToRun["imageLeavePicture"] = true;
         break;
 
     }
     checkTestsCompleted();
   }
 
   function startTest() {
+    // Set prefs to use mixed-content before HSTS
+    SpecialPowers.pushPrefEnv({'set': [["security.mixed_content.use_hsts", false],
+                                       ["security.mixed_content.send_hsts_priming", false]]});
     //Set the first set of mixed content settings and increment the counter.
     //Enable <picture> and <img srcset> for the test.
     changePrefs([[ "dom.image.srcset.enabled", true ], [ "dom.image.picture.enabled", true ]],
       function() {
         //listen for a messages from the mixed content test harness
         window.addEventListener("message", receiveMessage, false);
 
         //Kick off test
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -22,9 +22,10 @@ MOCHITEST_MANIFESTS += [
 
 MOCHITEST_CHROME_MANIFESTS += [
     'csp/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += [
     'contentverifier/browser.ini',
     'csp/browser.ini',
+    'hsts/browser.ini',
 ]
deleted file mode 100644
--- a/dom/tests/mochitest/bugs/bug918719.sjs
+++ /dev/null
@@ -1,47 +0,0 @@
-// Keep track of one in-flight XHR by allowing secondary ones to
-// tell us when the client has received a Progress event and wants
-// more data or to stop the in-flight XHR.
-
-const STATE_NAME = "bug918719_loading_event_test";
-const CHUNK_DATA = "chunk";
-
-function setReq(req) {
-  setObjectState(STATE_NAME, req);
-}
-
-function getReq() {
-  let req;
-  getObjectState(STATE_NAME, function(v) {
-    req = v;
-  });
-  return req;
-}
-
-function handleRequest(request, response)
-{
-  var pairs = request.queryString.split("&");
-  var command = pairs.shift();
-
-  response.setHeader("Content-Type", "text/plain");
-  response.setHeader("Cache-Control", "no-cache", false);
-
-  switch(command) {
-    case "more":
-      getReq().write(CHUNK_DATA);
-      break;
-
-    case "done":
-      getReq().finish();
-      setReq(null);
-      break;
-
-    default:
-      response.processAsync();
-      response.write(CHUNK_DATA);
-      setReq(response);
-      return;
-  }
-
-  response.setHeader("Content-Length", "2", false);
-  response.write("ok");
-}
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -2,17 +2,16 @@
 support-files =
   bug289714.sjs
   bug346659-echoer.html
   bug346659-opener-echoer.html
   bug346659-opener.html
   bug346659-parent-echoer.html
   bug346659-parent.html
   bug458091_child.html
-  bug918719.sjs
   child_bug260264.html
   devicemotion_inner.html
   devicemotion_outer.html
   file_bug291653.html
   file_bug406375.html
   file_bug504862.html
   file_bug593174_1.html
   file_bug593174_2.html
@@ -151,17 +150,16 @@ skip-if = (buildapp == 'b2g' && toolkit 
 [test_bug809290.html]
 [test_bug817476.html]
 [test_bug823173.html]
 [test_bug848088.html]
 [test_bug850517.html]
 [test_bug857555.html]
 [test_bug862540.html]
 [test_bug876098.html]
-[test_bug918719.html]
 [test_bug927901.html]
 [test_devicemotion_multiple_listeners.html]
 skip-if = toolkit == 'android' #bug 775227
 [test_domparser_after_blank.html]
 [test_errorReporting.html]
 [test_onerror_message.html]
 [test_protochains.html]
 [test_resize_move_windows.html]
deleted file mode 100644
--- a/dom/tests/mochitest/bugs/test_bug918719.html
+++ /dev/null
@@ -1,89 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=918719
--->
-<head>
-  <title>Test for Bug 918719</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=918719">Mozilla Bug 918719</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-
-SimpleTest.waitForExplicitFinish();
-
-const SERVER_URL = "bug918719.sjs";
-
-function sendCommand(cmd) {
-  let xhr = new XMLHttpRequest();
-  xhr.open("get", SERVER_URL + "?" + cmd);
-  xhr.send();
-}
-
-function runTest() {
-  // Manipulate one in-flight XHR using secondary command XHRs, to guarantee
-  // that multiple OnDataAvailable events are triggered (which are where
-  // LOADING readystatechanges are triggered). We return a promise that will
-  // resolve with a count of the number of LOADING events that were detected.
-
-  return new Promise((resolve, reject) => {
-    let xhr = new XMLHttpRequest();
-    let numProgressEvents = 0;
-    let numLoadingEvents = 0;
-
-    xhr.onreadystatechange = e => {
-      if (xhr.readyState === xhr.LOADING) {
-        ++numLoadingEvents;
-      }
-    };
-
-    xhr.onprogress = e => {
-      if (++numProgressEvents < 2) {
-        sendCommand("more");
-      } else {
-        sendCommand("done");
-      }
-    };
-
-    xhr.onerror = e => {
-      reject(e);
-    };
-
-    xhr.onloadend = e => {
-      resolve(numLoadingEvents);
-    };
-
-    xhr.open("GET", SERVER_URL);
-    xhr.send();
-  });
-}
-
-function prefChangePromise(args) {
-  return new Promise(function(resolve) {
-    SpecialPowers.pushPrefEnv(args, resolve);
-  });
-}
-
-runTest().then(function(count) {
-  ok(count === 1, "Only one loading readystatechange event should have been fired with the pref off.");
-}).then(function() {
-  return prefChangePromise({"set": [["dom.fire_extra_xhr_loading_readystatechanges", true]]});
-}).then(function() {
-  return runTest();
-}).then(function(count) {
-  ok(count > 1, "Multiple loading readystatechange events should have been fired with the pref on.");
-  SimpleTest.finish();
-});
-
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/workers/test/serviceworkers/create_another_sharedWorker.html
+++ b/dom/workers/test/serviceworkers/create_another_sharedWorker.html
@@ -1,6 +1,6 @@
 <!DOCTYPE HTML>
 <title>Shared workers: create antoehr sharedworekr client</title>
 <pre id=log>Hello World</pre>
 <script>
   var worker = new SharedWorker('sharedWorker_fetch.js');
-</script>
\ No newline at end of file
+</script>
--- a/dom/workers/test/serviceworkers/fetch.js
+++ b/dom/workers/test/serviceworkers/fetch.js
@@ -1,13 +1,11 @@
 addEventListener('fetch', function(event) {
   if (event.request.url.indexOf("fail.html") !== -1) {
-    event.respondWith(fetch("serviceworker.html", {"integrity": "abc"}));
+    event.respondWith(fetch("hello.html", {"integrity": "abc"}));
   } else if (event.request.url.indexOf("fake.html") !== -1) {
-    event.respondWith(fetch("serviceworker.html"));
-  } else {
-    event.respondWith(new Response("Hello world"));
+    event.respondWith(fetch("hello.html"));
   }
 });
 
 addEventListener("activate", function(event) {
   event.waitUntil(clients.claim());
 });
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/hello.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+  </head>
+  <body>
+    Hello.
+  </body>
+<html>
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -205,17 +205,17 @@ support-files =
   !/dom/security/test/cors/file_CrossSiteXHR_server.sjs
   !/dom/tests/mochitest/notification/MockServices.js
   !/dom/tests/mochitest/notification/NotificationTest.js
   blocking_install_event_worker.js
   sw_bad_mime_type.js
   sw_bad_mime_type.js^headers^
   error_reporting_helpers.js
   fetch.js
-  serviceworker.html
+  hello.html
   create_another_sharedWorker.html
   sharedWorker_fetch.js
 
 [test_bug1151916.html]
 [test_bug1240436.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
--- a/dom/workers/test/serviceworkers/sharedWorker_fetch.js
+++ b/dom/workers/test/serviceworkers/sharedWorker_fetch.js
@@ -22,10 +22,8 @@ onconnect = function(e) {
         fetch("SharedWorker_SRIFailed.html", {"integrity": "abc"}).then(
             function () {
                 clients[0].postMessage('SRI_failed');
             });
       }
     }
   }
 }
-
-
--- a/dom/workers/test/serviceworkers/test_fetch_integrity.html
+++ b/dom/workers/test/serviceworkers/test_fetch_integrity.html
@@ -170,9 +170,9 @@ add_task(function* test_integrity_shared
   yield wait_for_expected_message(expectedMessage);
 
   yield wait_for_expected_message(expectedMessage2);
   client_win.close();
 });
 
 </script>
 </body>
-</html>
\ No newline at end of file
+</html>
--- a/dom/xhr/XMLHttpRequestMainThread.cpp
+++ b/dom/xhr/XMLHttpRequestMainThread.cpp
@@ -164,17 +164,16 @@ XMLHttpRequestMainThread::XMLHttpRequest
   : mResponseBodyDecodedPos(0),
     mResponseType(XMLHttpRequestResponseType::_empty),
     mRequestObserver(nullptr),
     mState(State::unsent),
     mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false),
     mFlagSyncLooping(false), mFlagBackgroundRequest(false),
     mFlagHadUploadListenersOnSend(false), mFlagACwithCredentials(false),
     mFlagTimedOut(false), mFlagDeleted(false), mFlagSend(false),
-    mSendExtraLoadingEvents(Preferences::GetBool("dom.fire_extra_xhr_loading_readystatechanges", true)),
     mUploadTransferred(0), mUploadTotal(0), mUploadComplete(true),
     mProgressSinceLastProgressEvent(false),
     mRequestSentTime(0), mTimeoutMilliseconds(0),
     mErrorLoad(false), mErrorParsingXML(false),
     mWaitingForOnStopRequest(false),
     mProgressTimerIsActive(false),
     mIsHtml(false),
     mWarnAboutSyncHtml(false),
@@ -1710,19 +1709,17 @@ XMLHttpRequestMainThread::OnDataAvailabl
     }
 
     ChangeState(State::loading);
     return request->Cancel(NS_OK);
   }
 
   mDataAvailable += totalRead;
 
-  if (mState == State::headers_received || mSendExtraLoadingEvents) {
-    ChangeState(State::loading);
-  }
+  ChangeState(State::loading);
 
   if (!mFlagSynchronous && !mProgressTimerIsActive) {
     StartProgressEventTimer();
   }
 
   return NS_OK;
 }
 
@@ -2409,16 +2406,37 @@ XMLHttpRequestMainThread::CreateChannel(
     secFlags = nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS |
                nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
 
   if (mIsAnon) {
     secFlags |= nsILoadInfo::SEC_COOKIES_OMIT;
   }
 
+  nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(mPrincipal);
+  if (ep) {
+    // If we have an expanded principal, instead of using that, select the
+    // principal in the whitelist which can load our URL, and use that instead.
+    nsTArray<nsCOMPtr<nsIPrincipal>>* whitelist = nullptr;
+    ep->GetWhiteList(&whitelist);
+    if (!whitelist) {
+      return NS_ERROR_FAILURE;
+    }
+    MOZ_ASSERT(!(secFlags & nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS));
+    bool dataInherits = (secFlags &
+      (nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
+       nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) != 0;
+    for (const auto& principal : *whitelist) {
+      if (NS_SUCCEEDED(principal->CheckMayLoad(mRequestURL, false, dataInherits))) {
+        mPrincipal = principal;
+        break;
+      }
+    }
+  }
+
   // Use the responsibleDocument if we have it, except for dedicated workers
   // where it will be the parent document, which is not the one we want to use.
   nsresult rv;
   nsCOMPtr<nsIDocument> responsibleDocument = GetDocumentIfCurrent();
   if (responsibleDocument && responsibleDocument->NodePrincipal() == mPrincipal) {
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        mRequestURL,
                        responsibleDocument,
--- a/dom/xhr/XMLHttpRequestMainThread.h
+++ b/dom/xhr/XMLHttpRequestMainThread.h
@@ -681,24 +681,16 @@ protected:
   bool mFlagDeleted;
 
   // The XHR2 spec's send() flag. Set when the XHR begins uploading, until it
   // finishes downloading (or an error/abort has occurred during either phase).
   // Used to guard against the user trying to alter headers/etc when it's too
   // late, and ensure the XHR only handles one in-flight request at once.
   bool mFlagSend;
 
-  // Before ProgressEvents were a thing, multiple readystatechange events were
-  // fired during the loading state to give sites a way to monitor XHR progress.
-  // The XHR spec now has proper progress events and dictates that only one
-  // "loading" readystatechange should be fired per send. However, it's possible
-  // that some content still relies on this old behavior, so we're keeping it
-  // (behind a preference) for now. See bug 918719.
-  bool mSendExtraLoadingEvents;
-
   RefPtr<XMLHttpRequestUpload> mUpload;
   int64_t mUploadTransferred;
   int64_t mUploadTotal;
   bool mUploadComplete;
   bool mProgressSinceLastProgressEvent;
 
   // Timeout support
   PRTime mRequestSentTime;
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52342
+52343
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -13742,16 +13742,17 @@ admonishment/MS
 admonition/MS
 admonitory
 ado/M
 adobe/MS
 adolescence/SM
 adolescent/SM
 adopt/AGVDS
 adoptable
+adoptee/MS
 adopter/MS
 adoption/SM
 adorableness/M
 adorably
 adoration/M
 adore/BZGDRS
 adorer/M
 adoring/Y
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -818,17 +818,17 @@ GLContextGLX::CreateGLContext(CreateCont
             printf("[GLX] FBConfig is %sdouble-buffered\n", db ? "" : "not ");
         }
     }
 
     GLXContext context;
     RefPtr<GLContextGLX> glContext;
     bool error;
 
-    ScopedXErrorHandler xErrorHandler;
+    OffMainThreadScopedXErrorHandler xErrorHandler;
 
     do {
         error = false;
 
         GLXContext glxContext = shareContext ? shareContext->mContext : nullptr;
         if (glx.HasCreateContextAttribs()) {
             AutoTArray<int, 11> attrib_list;
             if (glx.HasRobustness()) {
@@ -1306,17 +1306,17 @@ CreateOffscreenPixmapContext(CreateConte
         NS_WARNING("Failed to find a compatible config.");
         return nullptr;
     }
 
     Visual* visual;
     int depth;
     FindVisualAndDepth(display, visid, &visual, &depth);
 
-    ScopedXErrorHandler xErrorHandler;
+    OffMainThreadScopedXErrorHandler xErrorHandler;
     bool error = false;
 
     Drawable drawable;
     GLXPixmap pixmap = 0;
 
     gfx::IntSize dummySize(16, 16);
     RefPtr<gfxXlibSurface> surface = gfxXlibSurface::Create(DefaultScreenOfDisplay(display),
                                                             visual,
--- a/gfx/gl/SurfaceTypes.cpp
+++ b/gfx/gl/SurfaceTypes.cpp
@@ -1,16 +1,16 @@
 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
 /* 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 "SurfaceTypes.h"
 
-#include "mozilla/layers/ISurfaceAllocator.h"
+#include "mozilla/layers/TextureForwarder.h"
 
 namespace mozilla {
 namespace gl {
 
 SurfaceCaps::SurfaceCaps()
 {
     Clear();
 }
--- a/gfx/gl/SurfaceTypes.h
+++ b/gfx/gl/SurfaceTypes.h
@@ -7,34 +7,34 @@
 #define SURFACE_TYPES_H_
 
 #include "mozilla/RefPtr.h"
 #include "mozilla/Attributes.h"
 #include <stdint.h>
 
 namespace mozilla {
 namespace layers {
-class ISurfaceAllocator;
+class LayersIPCChannel;
 } // namespace layers
 
 namespace gl {
 
 struct SurfaceCaps final
 {
     bool any;
     bool color, alpha;
     bool bpp16;
     bool depth, stencil;
     bool antialias;
     bool premultAlpha;
     bool preserve;
 
     // The surface allocator that we want to create this
     // for.  May be null.
-    RefPtr<layers::ISurfaceAllocator> surfaceAllocator;
+    RefPtr<layers::LayersIPCChannel> surfaceAllocator;
 
     SurfaceCaps();
     SurfaceCaps(const SurfaceCaps& other);
     ~SurfaceCaps();
 
     void Clear();
 
     SurfaceCaps& operator=(const SurfaceCaps& other);
--- a/gfx/layers/GrallocImages.cpp
+++ b/gfx/layers/GrallocImages.cpp
@@ -68,17 +68,17 @@ GrallocImage::SetData(const Data& aData)
   mData = aData;
   mSize = aData.mPicSize;
 
   if (gfxPlatform::GetPlatform()->IsInGonkEmulator()) {
     // Emulator does not support HAL_PIXEL_FORMAT_YV12.
     return false;
   }
 
-  RefPtr<ClientIPCAllocator> allocator = ImageBridgeChild::GetSingleton();
+  RefPtr<LayersIPCChannel> allocator = ImageBridgeChild::GetSingleton();
   GrallocTextureData* texData = GrallocTextureData::Create(mData.mYSize, HAL_PIXEL_FORMAT_YV12,
                                                            gfx::BackendType::NONE,
                                                            GraphicBuffer::USAGE_SW_READ_OFTEN |
                                                              GraphicBuffer::USAGE_SW_WRITE_OFTEN |
                                                              GraphicBuffer::USAGE_HW_TEXTURE,
                                                            allocator
   );
 
@@ -464,15 +464,15 @@ GrallocImage::GetNativeBuffer()
   android::sp<android::GraphicBuffer> graphicBuffer = GetGraphicBuffer();
   if (!graphicBuffer.get()) {
     return nullptr;
   }
   return graphicBuffer->getNativeBuffer();
 }
 
 TextureClient*
-GrallocImage::GetTextureClient(TextureForwarder* aForwarder)
+GrallocImage::GetTextureClient(KnowsCompositor* aForwarder)
 {
   return mTextureClient;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/GrallocImages.h
+++ b/gfx/layers/GrallocImages.h
@@ -88,17 +88,17 @@ public:
   virtual already_AddRefed<gfx::SourceSurface> GetAsSourceSurface() override;
 
   android::sp<android::GraphicBuffer> GetGraphicBuffer() const;
 
   void* GetNativeBuffer();
 
   virtual bool IsValid() { return !!mTextureClient; }
 
-  virtual TextureClient* GetTextureClient(TextureForwarder* aForwarder) override;
+  virtual TextureClient* GetTextureClient(KnowsCompositor* aForwarder) override;
 
   virtual GrallocImage* AsGrallocImage() override
   {
     return this;
   }
 
   virtual uint8_t* GetBuffer()
   {
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -299,22 +299,27 @@ ClientLayerManager::EndTransactionIntern
   // properties.
   GetRoot()->ApplyPendingUpdatesToSubtree();
 
   mPaintedLayerCallback = aCallback;
   mPaintedLayerCallbackData = aCallbackData;
 
   GetRoot()->ComputeEffectiveTransforms(Matrix4x4());
 
-  if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
-    TimeStamp start = TimeStamp::Now();
-    root->RenderLayer();
-    mLastPaintTime = TimeStamp::Now() - start;
+  // Skip the painting if the device is in device-reset status.
+  if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+    if (gfxPrefs::AlwaysPaint() && XRE_IsContentProcess()) {
+      TimeStamp start = TimeStamp::Now();
+      root->RenderLayer();
+      mLastPaintTime = TimeStamp::Now() - start;
+    } else {
+      root->RenderLayer();
+    }
   } else {
-    root->RenderLayer();
+    gfxCriticalNote << "LayerManager::EndTransaction skip RenderLayer().";
   }
 
   if (!mRepeatTransaction && !GetRoot()->GetInvalidRegion().IsEmpty()) {
     GetRoot()->Mutated();
   }
 
   if (!mIsRepeatTransaction) {
     mAnimationReadyTime = TimeStamp::Now();
@@ -621,18 +626,22 @@ ClientLayerManager::StopFrameTimeRecordi
   }
 }
 
 void
 ClientLayerManager::ForwardTransaction(bool aScheduleComposite)
 {
   TimeStamp start = TimeStamp::Now();
 
-  if (mForwarder->GetSyncObject()) {
-    mForwarder->GetSyncObject()->FinalizeFrame();
+  // Skip the synchronization for buffer since we also skip the painting during
+  // device-reset status.
+  if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
+    if (mForwarder->GetSyncObject()) {
+      mForwarder->GetSyncObject()->FinalizeFrame();
+    }
   }
 
   mPhase = PHASE_FORWARD;
 
   mLatestTransactionId = mTransactionIdAllocator->GetTransactionId();
   TimeStamp transactionStart;
   if (!mTransactionIdAllocator->GetTransactionStart().IsNull()) {
     transactionStart = mTransactionIdAllocator->GetTransactionStart();
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -19,35 +19,24 @@
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/Preferences.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for mozilla::gfx::IntRect
 #include "gfx2DGlue.h"
 #include "ReadbackProcessor.h"
 
-#ifdef XP_WIN
-#include "gfxWindowsPlatform.h"
-#endif
-
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 void
 ClientPaintedLayer::PaintThebes()
 {
-#ifdef XP_WIN
-  if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
-    // If our rendering device has reset simply avoid rendering completely.
-    return;
-  }
-#endif
-
   PROFILER_LABEL("ClientPaintedLayer", "PaintThebes",
     js::ProfileEntry::Category::GRAPHICS);
 
   NS_ASSERTION(ClientManager()->InDrawing(),
                "Can only draw in drawing phase");
   
   uint32_t flags = RotatedContentBuffer::PAINT_CAN_DRAW_ROTATED;
 #ifndef MOZ_IGNORE_PAINT_WILL_RESAMPLE
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1127,17 +1127,17 @@ TextureClient::CreateForDrawing(TextureF
       gl::sGLXLibrary.UseTextureFromPixmap())
   {
     data = X11TextureData::Create(aSize, aFormat, aTextureFlags, aAllocator);
   }
 #endif
 #endif
 
 #ifdef MOZ_WIDGET_GONK
-  if (!data) {
+  if (!data && aSize.width <= aMaxTextureSize && aSize.height <= aMaxTextureSize) {
     data = GrallocTextureData::CreateForDrawing(aSize, aFormat, moz2DBackend,
                                                 aAllocator);
   }
 #endif
 
 #ifdef XP_MACOSX
   if (!data && gfxPrefs::UseIOSurfaceTextures()) {
     data = MacIOSurfaceTextureData::Create(aSize, aFormat, moz2DBackend);
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -16,16 +16,17 @@
 #include "mozilla/RefPtr.h"             // for RefPtr, RefCounted
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/layers/FenceUtils.h"  // for FenceHandle
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/AtomicRefCountedWithFinalize.h"
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags, etc
+#include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "mozilla/gfx/CriticalSection.h"
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for TextureImage::AddRef, etc
 #include "GfxTexturesReporter.h"
 #include "pratom.h"
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -330,19 +330,19 @@ ImageBridgeChild::NotifyNotUsedToNonRecy
 
   MOZ_ASSERT(aTextureId == client->GetSerial());
   client->ClearWaitFenceHandleOnImageBridge(mWaitingFenceHandleMutex);
   mTexturesWaitingFenceHandle.Remove(aTextureId);
 
   // Release TextureClient on allocator's message loop.
   RefPtr<TextureClientReleaseTask> task =
     MakeAndAddRef<TextureClientReleaseTask>(client);
-  RefPtr<ClientIPCAllocator> allocator = client->GetAllocator();
+  RefPtr<LayersIPCChannel> allocator = client->GetAllocator();
   client = nullptr;
-  allocator->AsClientAllocator()->GetMessageLoop()->PostTask(task.forget());
+  allocator->GetMessageLoop()->PostTask(task.forget());
 #else
   NS_RUNTIMEABORT("not reached");
 #endif
 }
 
 void
 ImageBridgeChild::CancelWaitFenceHandle(TextureClient* aClient)
 {
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_WIDGET_GONK
 
 #include "mozilla/gfx/2D.h"
 #include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTracker
 #include "mozilla/layers/GrallocTextureClient.h"
-#include "mozilla/layers/CompositableForwarder.h"
+#include "mozilla/layers/TextureForwarder.h"
 #include "mozilla/layers/ISurfaceAllocator.h"
 #include "mozilla/layers/ShadowLayerUtilsGralloc.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "gfx2DGlue.h"
 #include "gfxPrefs.h" // for gfxPrefs
 #include "SharedSurfaceGralloc.h"
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
@@ -290,20 +290,16 @@ GrallocTextureData::UpdateFromSurface(gf
 GrallocTextureData*
 GrallocTextureData::Create(gfx::IntSize aSize, AndroidFormat aAndroidFormat,
                            gfx::BackendType aMoz2dBackend, uint32_t aUsage,
                            LayersIPCChannel* aAllocator)
 {
   if (!aAllocator || !aAllocator->IPCOpen()) {
     return nullptr;
   }
-  int32_t maxSize = aAllocator->AsClientAllocator()->GetMaxTextureSize();
-  if (aSize.width > maxSize || aSize.height > maxSize) {
-    return nullptr;
-  }
   gfx::SurfaceFormat format;
   switch (aAndroidFormat) {
   case android::PIXEL_FORMAT_RGBA_8888:
     format = gfx::SurfaceFormat::B8G8R8A8;
     break;
   case android::PIXEL_FORMAT_BGRA_8888:
     format = gfx::SurfaceFormat::B8G8R8A8;
     break;
@@ -442,16 +438,17 @@ GrallocTextureData::TextureClientFromSha
                   (uint32_t)required);
     MOZ_CRASH("Flag requirement mismatch.");
   }
   return ret.forget();
 }
 
 TextureData*
 GrallocTextureData::CreateSimilar(LayersIPCChannel* aAllocator,
+                                  LayersBackend aLayersBackend,
                                   TextureFlags aFlags,
                                   TextureAllocationFlags aAllocFlags) const
 {
   if (mFormat == gfx::SurfaceFormat::YUV) {
     return GrallocTextureData::CreateForYCbCr(mSize, mSize*2, aAllocator);
   } else {
     return GrallocTextureData::CreateForDrawing(mSize, mFormat, mMoz2DBackend, aAllocator);
   }
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -70,16 +70,17 @@ public:
                                     LayersIPCChannel* aAllocator);
 
 
   static already_AddRefed<TextureClient>
   TextureClientFromSharedSurface(gl::SharedSurface* abstractSurf, TextureFlags flags);
 
   virtual TextureData*
   CreateSimilar(LayersIPCChannel* aAllocator,
+                LayersBackend aLayersBackend,
                 TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const override;
 
   // use TextureClient's default implementation
   virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
 
   /// Hold android::MediaBuffer.
   /// MediaBuffer needs to be add refed to keep MediaBuffer alive while the texture
--- a/gfx/src/X11Util.cpp
+++ b/gfx/src/X11Util.cpp
@@ -52,23 +52,25 @@ ScopedXErrorHandler::ErrorHandler(Displa
 {
     // only record the error if no error was previously recorded.
     // this means that in case of multiple errors, it's the first error that we report.
     if (!sXErrorPtr->mError.error_code)
       sXErrorPtr->mError = *ev;
     return 0;
 }
 
-ScopedXErrorHandler::ScopedXErrorHandler()
+ScopedXErrorHandler::ScopedXErrorHandler(bool aAllowOffMainThread)
 {
-    // Off main thread usage is not safe in general, but OMTC GL layers uses this
-    // with the main thread blocked, which makes it safe.
-    NS_WARNING_ASSERTION(
+    if (!aAllowOffMainThread) {
+      // Off main thread usage is not safe in general, but OMTC GL layers uses this
+      // with the main thread blocked, which makes it safe.
+      NS_WARNING_ASSERTION(
         NS_IsMainThread(),
         "ScopedXErrorHandler being called off main thread, may cause issues");
+    }
     // let sXErrorPtr point to this object's mXError object, but don't reset this mXError object!
     // think of the case of nested ScopedXErrorHandler's.
     mOldXErrorPtr = sXErrorPtr;
     sXErrorPtr = &mXError;
     mOldErrorHandler = XSetErrorHandler(ErrorHandler);
 }
 
 ScopedXErrorHandler::~ScopedXErrorHandler()
--- a/gfx/src/X11Util.h
+++ b/gfx/src/X11Util.h
@@ -113,24 +113,36 @@ private:
     // what to restore the error handler to on destruction
     int (*mOldErrorHandler)(Display *, XErrorEvent *);
 
 public:
 
     static int
     ErrorHandler(Display *, XErrorEvent *ev);
 
-    ScopedXErrorHandler();
+    /**
+     * @param aAllowOffMainThread whether to warn if used off main thread
+     */
+    explicit ScopedXErrorHandler(bool aAllowOffMainThread = false);
 
     ~ScopedXErrorHandler();
 
     /** \returns true if a X error occurred since the last time this method was called on this ScopedXErrorHandler object,
      *           or since the creation of this ScopedXErrorHandler object if this method was never called on it.
      *
      * \param ev this optional parameter, if set, will be filled with the XErrorEvent object. If multiple errors occurred,
      *           the first one will be returned.
      */
     bool SyncAndGetError(Display *dpy, XErrorEvent *ev = nullptr);
 };
 
+class OffMainThreadScopedXErrorHandler : public ScopedXErrorHandler
+{
+public:
+  OffMainThreadScopedXErrorHandler()
+    : ScopedXErrorHandler(true)
+  {
+  }
+};
+
 } // namespace mozilla
 
 #endif  // mozilla_X11Util_h
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -166,17 +166,17 @@ gfxASurface::Wrap (cairo_surface_t *csur
     }
 #endif
 #ifdef CAIRO_HAS_QUARTZ_SURFACE
     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
         result = new gfxQuartzSurface(csurf, aSize);
     }
 #endif
     else {
-        MOZ_CRASH("Unknown cairo surface type");
+        result = new gfxUnknownSurface(csurf, aSize);
     }
 
     // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
 
     return result.forget();
 }
 
 void
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -178,9 +178,27 @@ private:
 
     int32_t mFloatingRefs;
     int32_t mBytesRecorded;
 
 protected:
     bool mSurfaceValid;
 };
 
+/**
+ * An Unknown surface; used to wrap unknown cairo_surface_t returns from cairo
+ */
+class gfxUnknownSurface : public gfxASurface {
+public:
+    gfxUnknownSurface(cairo_surface_t *surf, const mozilla::gfx::IntSize& aSize)
+        : mSize(aSize)
+    {
+        Init(surf, true);
+    }
+
+    virtual ~gfxUnknownSurface() { }
+    virtual const mozilla::gfx::IntSize GetSize() const override { return mSize; }
+
+private:
+    mozilla::gfx::IntSize mSize;
+};
+
 #endif /* GFX_ASURFACE_H */
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2093,23 +2093,25 @@ static mozilla::Atomic<bool> sLayersSupp
 static bool sLayersHardwareVideoDecodingFailed = false;
 static bool sBufferRotationCheckPref = true;
 
 static mozilla::Atomic<bool> sLayersAccelerationPrefsInitialized(false);
 
 void VideoDecodingFailedChangedCallback(const char* aPref, void*)
 {
   sLayersHardwareVideoDecodingFailed = Preferences::GetBool(aPref, false);
-  gfxPlatform::GetPlatform()->UpdateCanUseHardareVideoDecoding();
+  gfxPlatform::GetPlatform()->UpdateCanUseHardwareVideoDecoding();
 }
 
 void
-gfxPlatform::UpdateCanUseHardareVideoDecoding()
+gfxPlatform::UpdateCanUseHardwareVideoDecoding()
 {
-  gfxVars::SetCanUseHardwareVideoDecoding(CanUseHardwareVideoDecoding());
+  if (XRE_IsParentProcess()) {
+    gfxVars::SetCanUseHardwareVideoDecoding(CanUseHardwareVideoDecoding());
+  }
 }
 
 void
 gfxPlatform::InitAcceleration()
 {
   if (sLayersAccelerationPrefsInitialized) {
     return;
   }
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -451,17 +451,17 @@ public:
         // platform-specific override, by default do nothing
     }
 
     // Are we in safe mode?
     static bool InSafeMode();
 
     static bool OffMainThreadCompositingEnabled();
 
-    void UpdateCanUseHardareVideoDecoding();
+    void UpdateCanUseHardwareVideoDecoding();
 
     // Returns a prioritized list of all available compositor backends.
     void GetCompositorBackends(bool useAcceleration, nsTArray<mozilla::layers::LayersBackend>& aBackends);
 
     /**
      * Is it possible to use buffer rotation.  Note that these
      * check the preference, but also allow for the override to
      * disable it using DisableBufferRotation.
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -360,16 +360,20 @@ gfxWindowsPlatform::InitAcceleration()
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0);
   mFeatureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3);
 
   DeviceManagerDx::Init();
   DeviceManagerD3D9::Init();
 
+  // CanUseHardwareVideoDecoding depends on DeviceManagerDx state,
+  // so update the cached value now.
+  UpdateCanUseHardwareVideoDecoding();
+
   InitializeConfig();
   InitializeDevices();
   UpdateANGLEConfig();
   UpdateRenderMode();
 
   // If we have Skia and we didn't init dwrite already, do it now.
   if (!mDWriteFactory && GetDefaultContentBackend() == BackendType::SKIA) {
     InitDWriteSupport();
@@ -2025,17 +2029,17 @@ gfxWindowsPlatform::ImportGPUDeviceData(
         FeatureStatus::Unavailable,
         "Direct2D requires Direct3D 11 compositing",
         NS_LITERAL_CSTRING("FEATURE_FAILURE_D2D_D3D11_COMP"));
     }
   }
 
   // CanUseHardwareVideoDecoding depends on d3d11 state, so update
   // the cached value now.
-  UpdateCanUseHardareVideoDecoding();
+  UpdateCanUseHardwareVideoDecoding();
 
   // For completeness (and messaging in about:support). Content recomputes this
   // on its own, and we won't use ANGLE in the UI process if we're using a GPU
   // process.
   UpdateANGLEConfig();
 }
 
 void
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -602,16 +602,29 @@ ShouldLoadCachedImage(imgRequest* aImgRe
                                              aLoadingPrincipal,
                                              &decision);
       if (NS_FAILED(rv) || !NS_CP_ACCEPTED(decision)) {
         return false;
       }
     }
   }
 
+  bool sendPriming = false;
+  bool mixedContentWouldBlock = false;
+  rv = nsMixedContentBlocker::GetHSTSPrimingFromRequestingContext(contentLocation,
+      aLoadingContext, &sendPriming, &mixedContentWouldBlock);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+  if (sendPriming && mixedContentWouldBlock) {
+    // if either of the securty checks above would cause a priming request, we
+    // can't load this image from the cache, so go ahead and return false here
+    return false;
+  }
+
   return true;
 }
 
 // Returns true if this request is compatible with the given CORS mode on the
 // given loading principal, and false if the request may not be reused due
 // to CORS.  Also checks the Referrer Policy, since requests with different
 // referrers/policies may generate different responses.
 static bool
@@ -711,20 +724,16 @@ NewImageChannel(nsIChannel** aResult,
     : nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS;
   if (aCORSMode == imgIRequest::CORS_ANONYMOUS) {
     securityFlags |= nsILoadInfo::SEC_COOKIES_SAME_ORIGIN;
   } else if (aCORSMode == imgIRequest::CORS_USE_CREDENTIALS) {
     securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
   }
   securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;
 
-  if (aRespectPrivacy) {
-    securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
-  }
-
   // Note we are calling NS_NewChannelWithTriggeringPrincipal() here with a
   // node and a principal. This is for things like background images that are
   // specified by user stylesheets, where the document is being styled, but
   // the principal is that of the user stylesheet.
   if (requestingNode && aLoadingPrincipal) {
     rv = NS_NewChannelWithTriggeringPrincipal(aResult,
                                               aURI,
                                               requestingNode,
@@ -745,16 +754,32 @@ NewImageChannel(nsIChannel** aResult,
     rv = NS_NewChannel(aResult,
                        aURI,
                        nsContentUtils::GetSystemPrincipal(),
                        securityFlags,
                        aPolicyType,
                        nullptr,   // loadGroup
                        callbacks,
                        aLoadFlags);
+
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    // Use the OriginAttributes from the loading principal, if one is available,
+    // and adjust the private browsing ID based on what kind of load the caller
+    // has asked us to perform.
+    NeckoOriginAttributes neckoAttrs;
+    if (aLoadingPrincipal) {
+      neckoAttrs.InheritFromDocToNecko(BasePrincipal::Cast(aLoadingPrincipal)->OriginAttributesRef());
+    }
+    neckoAttrs.mPrivateBrowsingId = aRespectPrivacy ? 1 : 0;
+
+    nsCOMPtr<nsILoadInfo> loadInfo = (*aResult)->GetLoadInfo();
+    rv = loadInfo->SetOriginAttributes(neckoAttrs);
   }
 
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   // only inherit if we have a principal
   *aForcePrincipalCheckForCacheEntry =
--- a/image/imgLoader.h
+++ b/image/imgLoader.h
@@ -250,19 +250,16 @@ public:
    * Get the normal image loader instance that is used by gecko code, creating
    * it if necessary.
    */
   static imgLoader* NormalLoader();
 
   /**
    * Get the Private Browsing image loader instance that is used by gecko code,
    * creating it if necessary.
-   *
-   * The nsIChannel objects that this instance creates are created with the
-   * nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING flag.
    */
   static imgLoader* PrivateBrowsingLoader();
 
   /**
    * Gecko code should use NormalLoader() or PrivateBrowsingLoader() to get the
    * appropriate image loader.
    *
    * This constructor is public because the XPCOM module code that creates
--- a/image/test/unit/test_private_channel.js
+++ b/image/test/unit/test_private_channel.js
@@ -28,44 +28,47 @@ function imageHandler(metadata, response
   var body = "iVBORw0KGgoAAAANSUhEUgAAAAMAAAADCAIAAADZSiLoAAAAEUlEQVQImWP4z8AAQTAamQkAhpcI+DeMzFcAAAAASUVORK5CYII=";
   response.bodyOutputStream.write(body, body.length);
 }
 
 var requests = [];
 var listeners = [];
 
 function NotificationCallbacks(isPrivate) {
+  this.originAttributes.privateBrowsingId = isPrivate ? 1 : 0;
   this.usePrivateBrowsing = isPrivate;
 }
 
 NotificationCallbacks.prototype = {
   QueryInterface: function (iid) {
     if (iid.equals(Ci.nsISupports) ||
         iid.equals(Ci.nsILoadContext))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
   getInterface: function(iid) {
     if (iid.equals(Ci.nsILoadContext))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
-  originAttributes: {}
+  originAttributes: {
+    privateBrowsingId: 0
+  }
 };
 
 var gImgPath = 'http://localhost:' + server.identity.primaryPort + '/image.png';
 
 function setup_chan(path, isPrivate, callback) {
   var uri = NetUtil.newURI(gImgPath);
   var securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
-  if (isPrivate) {
-    securityFlags |= Ci.nsILoadInfo.SEC_FORCE_PRIVATE_BROWSING;
-  }
-  var chan =  NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true,
-                                  securityFlags: securityFlags});
+  var principal = Services.scriptSecurityManager
+                          .createCodebasePrincipal(uri, {privateBrowsingId: isPrivate ? 1 : 0});
+  var chan =  NetUtil.newChannel({uri: uri, loadingPrincipal: principal,
+                                  securityFlags: securityFlags,
+                                  contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE});
   chan.notificationCallbacks = new NotificationCallbacks(isPrivate);
   var channelListener = new ChannelListener();
   chan.asyncOpen2(channelListener);
 
   var listener = new ImageListener(null, callback);
   var outlistener = {};
   var loader = isPrivate ? gPrivateLoader : gPublicLoader;
   var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -261,17 +261,19 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
       aLoadInfo->GetEnforceSecurity(),
       aLoadInfo->GetInitialSecurityCheckDone(),
       aLoadInfo->GetIsInThirdPartyContext(),
       aLoadInfo->GetOriginAttributes(),
       redirectChainIncludingInternalRedirects,
       redirectChain,
       aLoadInfo->CorsUnsafeHeaders(),
       aLoadInfo->GetForcePreflight(),
-      aLoadInfo->GetIsPreflight());
+      aLoadInfo->GetIsPreflight(),
+      aLoadInfo->GetForceHSTSPriming(),
+      aLoadInfo->GetMixedContentWouldBlock());
 
   return NS_OK;
 }
 
 nsresult
 LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
                        nsILoadInfo** outLoadInfo)
 {
@@ -333,16 +335,19 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
                           loadInfoArgs.enforceSecurity(),
                           loadInfoArgs.initialSecurityCheckDone(),
                           loadInfoArgs.isInThirdPartyContext(),
                           loadInfoArgs.originAttributes(),
                           redirectChainIncludingInternalRedirects,
                           redirectChain,
                           loadInfoArgs.corsUnsafeHeaders(),
                           loadInfoArgs.forcePreflight(),
-                          loadInfoArgs.isPreflight());
+                          loadInfoArgs.isPreflight(),
+                          loadInfoArgs.forceHSTSPriming(),
+                          loadInfoArgs.mixedContentWouldBlock()
+                          );
 
    loadInfo.forget(outLoadInfo);
    return NS_OK;
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -479,27 +479,27 @@ MessageChannel::MessageChannel(MessageLi
     mInTimeoutSecondHalf(false),
     mNextSeqno(0),
     mLastSendError(SyncSendError::SendSuccess),
     mDispatchingAsyncMessage(false),
     mDispatchingAsyncMessagePriority(0),
     mTransactionStack(nullptr),
     mTimedOutMessageSeqno(0),
     mTimedOutMessagePriority(0),
+#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
+    mPending(AnnotateAllocator<Message>(*this)),
+#endif
     mRemoteStackDepthGuess(false),
     mSawInterruptOutMsg(false),
     mIsWaitingForIncoming(false),
     mAbortOnError(false),
     mNotifiedChannelDone(false),
     mFlags(REQUIRE_DEFAULT),
     mPeerPidSet(false),
     mPeerPid(-1)
-#if defined(MOZ_CRASHREPORTER) && defined(OS_WIN)
-    , mPending(AnnotateAllocator<Message>(*this))
-#endif
 {
     MOZ_COUNT_CTOR(ipc::MessageChannel);
 
 #ifdef OS_WIN
     mTopFrame = nullptr;
     mIsSyncWaitingOnNonMainThread = false;
 #endif
 
--- a/ipc/mscom/MainThreadInvoker.cpp
+++ b/ipc/mscom/MainThreadInvoker.cpp
@@ -9,18 +9,17 @@
 #include "GeckoProfiler.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/HangMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "private/prpriv.h" // For PR_GetThreadID
-
-#include <winternl.h> // For NTSTATUS and NTAPI
+#include "WinUtils.h"
 
 namespace {
 
 class SyncRunnable : public mozilla::Runnable
 {
 public:
   SyncRunnable(HANDLE aEvent, already_AddRefed<nsIRunnable>&& aRunnable)
     : mDoneEvent(aEvent)
@@ -37,60 +36,43 @@ public:
     return NS_OK;
   }
 
 private:
   HANDLE                mDoneEvent;
   nsCOMPtr<nsIRunnable> mRunnable;
 };
 
-typedef NTSTATUS (NTAPI* NtTestAlertPtr)(VOID);
-
 } // anonymous namespace
 
 namespace mozilla {
 namespace mscom {
 
 HANDLE MainThreadInvoker::sMainThread = nullptr;
-StaticRefPtr<nsIRunnable> MainThreadInvoker::sAlertRunnable;
 
 /* static */ bool
 MainThreadInvoker::InitStatics()
 {
   nsCOMPtr<nsIThread> mainThread;
   nsresult rv = ::NS_GetMainThread(getter_AddRefs(mainThread));
   if (NS_FAILED(rv)) {
     return false;
   }
   PRThread* mainPrThread = nullptr;
   rv = mainThread->GetPRThread(&mainPrThread);
   if (NS_FAILED(rv)) {
     return false;
   }
   PRUint32 tid = ::PR_GetThreadID(mainPrThread);
   sMainThread = ::OpenThread(SYNCHRONIZE | THREAD_SET_CONTEXT, FALSE, tid);
-  if (!sMainThread) {
-    return false;
-  }
-  NtTestAlertPtr NtTestAlert =
-    reinterpret_cast<NtTestAlertPtr>(
-        ::GetProcAddress(::GetModuleHandleW(L"ntdll.dll"), "NtTestAlert"));
-  sAlertRunnable = ::NS_NewRunnableFunction([NtTestAlert]() -> void {
-    // We're using NtTestAlert() instead of SleepEx() so that the main thread
-    // never gives up its quantum if there are no APCs pending.
-    NtTestAlert();
-  }).take();
-  if (sAlertRunnable) {
-    ClearOnShutdown(&sAlertRunnable);
-  }
-  return !!sAlertRunnable;
+  return !!sMainThread;
 }
 
 MainThreadInvoker::MainThreadInvoker()
-  : mDoneEvent(::CreateEvent(nullptr, FALSE, FALSE, nullptr))
+  : mDoneEvent(::CreateEventW(nullptr, FALSE, FALSE, nullptr))
 {
   static const bool gotStatics = InitStatics();
   MOZ_ASSERT(gotStatics);
 }
 
 MainThreadInvoker::~MainThreadInvoker()
 {
   if (mDoneEvent) {
@@ -124,21 +106,21 @@ MainThreadInvoker::Invoke(already_AddRef
   // Make sure that wrappedRunnable remains valid while sitting in the APC queue
   wrappedRunnable->AddRef();
   if (!::QueueUserAPC(&MainThreadAPC, sMainThread,
                       reinterpret_cast<UINT_PTR>(wrappedRunnable.get()))) {
     // Enqueue failed so cancel the above AddRef
     wrappedRunnable->Release();
     return false;
   }
-  // We should enqueue a call to NtTestAlert() so that the main thread will
-  // check for APCs during event processing. If we omit this then the main
-  // thread will not check its APC queue until it is idle. Note that failing to
-  // dispatch this event is non-fatal, but it will delay execution of the APC.
-  Unused << NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(sAlertRunnable)));
+  // We should ensure a call to NtTestAlert() is made on the main thread so
+  // that the main thread will check for APCs during event processing. If we
+  // omit this then the main thread will not check its APC queue until it is
+  // idle.
+  widget::WinUtils::SetAPCPending();
   return WaitForCompletion(aTimeout);
 }
 
 /* static */ VOID CALLBACK
 MainThreadInvoker::MainThreadAPC(ULONG_PTR aParam)
 {
   GeckoProfilerWakeRAII wakeProfiler;
   mozilla::HangMonitor::NotifyActivity(mozilla::HangMonitor::kGeneralActivity);
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -83,24 +83,27 @@ endif
 endif
 
 check-style::
 	(cd $(srcdir) && $(PYTHON) $(topsrcdir)/config/check_spidermonkey_style.py);
 
 check-masm::
 	(cd $(srcdir) && $(PYTHON) $(topsrcdir)/config/check_macroassembler_style.py);
 
+check-js-msg::
+	(cd $(topsrcdir) && $(PYTHON) $(topsrcdir)/config/check_js_msg_encoding.py);
+
 check-jit-test::
 	$(JITTEST_SANITIZER_ENV) $(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/jit-test/jit_test.py \
 	        --no-slow --no-progress --format=automation --jitflags=all \
 			$(JITTEST_VALGRIND_FLAG) \
 			$(JITTEST_EXTRA_ARGS) \
 	        $(DIST)/bin/$(JS_SHELL_NAME)$(BIN_SUFFIX) $(JITTEST_TEST_ARGS)
 
-check:: check-style check-masm
+check:: check-style check-masm check-js-msg
 
 check-jstests:
 	$(wildcard $(RUN_TEST_PROGRAM)) $(PYTHON) -u $(srcdir)/tests/jstests.py \
 		--no-progress --format=automation --timeout 300 \
 		$(JSTESTS_EXTRA_ARGS) \
 		$(DIST)/bin/$(JS_SHELL_NAME)$(BIN_SUFFIX)
 
 # FIXME:
--- a/js/src/asmjs/WasmJS.cpp
+++ b/js/src/asmjs/WasmJS.cpp
@@ -1573,19 +1573,19 @@ WebAssembly_validate(JSContext* cx, unsi
     // If the reason for validation failure was OOM (signalled by null error
     // message), report out-of-memory so that validate's return is always
     // correct.
     if (!validated && !error) {
         ReportOutOfMemory(cx);
         return false;
     }
 
-    if (error) {
-        JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
-                                     JSMSG_WASM_COMPILE_ERROR, error.get());
+    if (error && !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
+                                               JSMSG_WASM_COMPILE_ERROR, error.get())) {
+        return false;
     }
 
     callArgs.rval().setBoolean(validated);
     return true;
 }
 
 static const JSFunctionSpec WebAssembly_static_methods[] =
 {
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -249,17 +249,17 @@ class NodeBuilder
     RootedValue userv;                 /* user-specified builder object or null */
 
   public:
     NodeBuilder(JSContext* c, bool l, char const* s)
       : cx(c), tokenStream(nullptr), saveLoc(l), src(s), srcval(c), callbacks(cx),
           userv(c)
     {}
 
-    bool init(HandleObject userobj = nullptr) {
+    MOZ_MUST_USE bool init(HandleObject userobj = nullptr) {
         if (src) {
             if (!atomValue(src, &srcval))
                 return false;
         } else {
             srcval.setNull();
         }
 
         if (!userobj) {
@@ -300,99 +300,99 @@ class NodeBuilder
         return true;
     }
 
     void setTokenStream(TokenStream* ts) {
         tokenStream = ts;
     }
 
   private:
-    bool callbackHelper(HandleValue fun, const InvokeArgs& args, size_t i,
-                        TokenPos* pos, MutableHandleValue dst)
+    MOZ_MUST_USE bool callbackHelper(HandleValue fun, const InvokeArgs& args, size_t i,
+                                     TokenPos* pos, MutableHandleValue dst)
     {
         // The end of the implementation of callback(). All arguments except
         // loc have already been stored in range [0, i).
         if (saveLoc) {
             if (!newNodeLoc(pos, args[i]))
                 return false;
         }
 
         return js::Call(cx, fun, userv, args, dst);
     }
 
     // Helper function for callback(). Note that all Arguments must be types
     // that convert to HandleValue, so this isn't as template-y as it seems,
     // just variadic.
     template <typename... Arguments>
-    bool callbackHelper(HandleValue fun, const InvokeArgs& args, size_t i,
-                        HandleValue head, Arguments&&... tail)
+    MOZ_MUST_USE bool callbackHelper(HandleValue fun, const InvokeArgs& args, size_t i,
+                                     HandleValue head, Arguments&&... tail)
     {
         // Recursive loop to store the arguments into args. This eventually
         // bottoms out in a call to the non-template callbackHelper() above.
         args[i].set(head);
         return callbackHelper(fun, args, i + 1, Forward<Arguments>(tail)...);
     }
 
     // Invoke a user-defined callback. The actual signature is:
     //
     //     bool callback(HandleValue fun, HandleValue... args, TokenPos* pos,
     //                   MutableHandleValue dst);
     template <typename... Arguments>
-    bool callback(HandleValue fun, Arguments&&... args) {
+    MOZ_MUST_USE bool callback(HandleValue fun, Arguments&&... args) {
         InvokeArgs iargs(cx);
         if (!iargs.init(sizeof...(args) - 2 + size_t(saveLoc)))
             return false;
 
         return callbackHelper(fun, iargs, 0, Forward<Arguments>(args)...);
     }
 
     // WARNING: Returning a Handle is non-standard, but it works in this case
     // because both |v| and |UndefinedHandleValue| are definitely rooted on a
     // previous stack frame (i.e. we're just choosing between two
     // already-rooted values).
     HandleValue opt(HandleValue v) {
         MOZ_ASSERT_IF(v.isMagic(), v.whyMagic() == JS_SERIALIZE_NO_NODE);
         return v.isMagic(JS_SERIALIZE_NO_NODE) ? JS::UndefinedHandleValue : v;
     }
 
-    bool atomValue(const char* s, MutableHandleValue dst) {
+    MOZ_MUST_USE bool atomValue(const char* s, MutableHandleValue dst) {
         /*
          * Bug 575416: instead of Atomize, lookup constant atoms in tbl file
          */
         RootedAtom atom(cx, Atomize(cx, s, strlen(s)));
         if (!atom)
             return false;
 
         dst.setString(atom);
         return true;
     }
 
-    bool newObject(MutableHandleObject dst) {
+    MOZ_MUST_USE bool newObject(MutableHandleObject dst) {
         RootedPlainObject nobj(cx, NewBuiltinClassInstance<PlainObject>(cx));
         if (!nobj)
             return false;
 
         dst.set(nobj);
         return true;
     }
 
-    bool newArray(NodeVector& elts, MutableHandleValue dst);
-
-    bool createNode(ASTType type, TokenPos* pos, MutableHandleObject dst);
-
-    bool newNodeHelper(HandleObject obj, MutableHandleValue dst) {
+    MOZ_MUST_USE bool newArray(NodeVector& elts, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool createNode(ASTType type, TokenPos* pos, MutableHandleObject dst);
+
+    MOZ_MUST_USE bool newNodeHelper(HandleObject obj, MutableHandleValue dst) {
         // The end of the implementation of newNode().
         MOZ_ASSERT(obj);
         dst.setObject(*obj);
         return true;
     }
 
     template <typename... Arguments>
-    bool newNodeHelper(HandleObject obj, const char *name, HandleValue value,
-                       Arguments&&... rest)
+    MOZ_MUST_USE bool newNodeHelper(HandleObject obj, const char *name, HandleValue value,
+                                    Arguments&&... rest)
     {
         // Recursive loop to define properties. Note that the newNodeHelper()
         // call below passes two fewer arguments than we received, as we omit
         // `name` and `value`. This eventually bottoms out in a call to the
         // non-template newNodeHelper() above.
         return defineProperty(obj, name, value)
                && newNodeHelper(obj, Forward<Arguments>(rest)...);
     }
@@ -400,237 +400,245 @@ class NodeBuilder
     // Create a node object with "type" and "loc" properties, as well as zero
     // or more properties passed in as arguments. The signature is really more
     // like:
     //
     //     bool newNode(ASTType type, TokenPos* pos,
     //                  {const char *name0, HandleValue value0,}...
     //                  MutableHandleValue dst);
     template <typename... Arguments>
-    bool newNode(ASTType type, TokenPos* pos, Arguments&&... args) {
+    MOZ_MUST_USE bool newNode(ASTType type, TokenPos* pos, Arguments&&... args) {
         RootedObject node(cx);
         return createNode(type, pos, &node) &&
                newNodeHelper(node, Forward<Arguments>(args)...);
     }
 
-    bool listNode(ASTType type, const char* propName, NodeVector& elts, TokenPos* pos,
-                  MutableHandleValue dst) {
+    MOZ_MUST_USE bool listNode(ASTType type, const char* propName, NodeVector& elts, TokenPos* pos,
+                               MutableHandleValue dst) {
         RootedValue array(cx);
         if (!newArray(elts, &array))
             return false;
 
         RootedValue cb(cx, callbacks[type]);
         if (!cb.isNull())
             return callback(cb, array, pos, dst);
 
         return newNode(type, pos, propName, array, dst);
     }
 
-    bool defineProperty(HandleObject obj, const char* name, HandleValue val) {
+    MOZ_MUST_USE bool defineProperty(HandleObject obj, const char* name, HandleValue val) {
         MOZ_ASSERT_IF(val.isMagic(), val.whyMagic() == JS_SERIALIZE_NO_NODE);
 
         /*
          * Bug 575416: instead of Atomize, lookup constant atoms in tbl file
          */
         RootedAtom atom(cx, Atomize(cx, name, strlen(name)));
         if (!atom)
             return false;
 
         /* Represent "no node" as null and ensure users are not exposed to magic values. */
         RootedValue optVal(cx, val.isMagic(JS_SERIALIZE_NO_NODE) ? NullValue() : val);
         return DefineProperty(cx, obj, atom->asPropertyName(), optVal);
     }
 
-    bool newNodeLoc(TokenPos* pos, MutableHandleValue dst);
-
-    bool setNodeLoc(HandleObject node, TokenPos* pos);
+    MOZ_MUST_USE bool newNodeLoc(TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool setNodeLoc(HandleObject node, TokenPos* pos);
 
   public:
     /*
      * All of the public builder methods take as their last two
      * arguments a nullable token position and a non-nullable, rooted
      * outparam.
      *
      * Any Value arguments representing optional subnodes may be a
      * JS_SERIALIZE_NO_NODE magic value.
      */
 
     /*
      * misc nodes
      */
 
-    bool program(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool literal(HandleValue val, TokenPos* pos, MutableHandleValue dst);
-
-    bool identifier(HandleValue name, TokenPos* pos, MutableHandleValue dst);
-
-    bool function(ASTType type, TokenPos* pos,
-                  HandleValue id, NodeVector& args, NodeVector& defaults,
-                  HandleValue body, HandleValue rest, GeneratorStyle generatorStyle,
-                  bool isExpression, MutableHandleValue dst);
-
-    bool variableDeclarator(HandleValue id, HandleValue init, TokenPos* pos,
-                            MutableHandleValue dst);
-
-    bool switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos,
-                     MutableHandleValue dst);
-
-    bool prototypeMutation(HandleValue val, TokenPos* pos, MutableHandleValue dst);
-    bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind, bool isShorthand,
-                             bool isMethod, TokenPos* pos, MutableHandleValue dst);
+    MOZ_MUST_USE bool program(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool literal(HandleValue val, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool identifier(HandleValue name, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool function(ASTType type, TokenPos* pos,
+                               HandleValue id, NodeVector& args, NodeVector& defaults,
+                               HandleValue body, HandleValue rest, GeneratorStyle generatorStyle,
+                               bool isExpression, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool variableDeclarator(HandleValue id, HandleValue init, TokenPos* pos,
+                                         MutableHandleValue dst);
+
+    MOZ_MUST_USE bool switchCase(HandleValue expr, NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool catchClause(HandleValue var, HandleValue guard, HandleValue body, TokenPos* pos,
+                                  MutableHandleValue dst);
+
+    MOZ_MUST_USE bool prototypeMutation(HandleValue val, TokenPos* pos, MutableHandleValue dst);
+    MOZ_MUST_USE bool propertyInitializer(HandleValue key, HandleValue val, PropKind kind,
+                                          bool isShorthand, bool isMethod, TokenPos* pos,
+                                          MutableHandleValue dst);
 
 
     /*
      * statements
      */
 
-    bool blockStatement(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool expressionStatement(HandleValue expr, TokenPos* pos, MutableHandleValue dst);
-
-    bool emptyStatement(TokenPos* pos, MutableHandleValue dst);
-
-    bool ifStatement(HandleValue test, HandleValue cons, HandleValue alt, TokenPos* pos,
+    MOZ_MUST_USE bool blockStatement(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool expressionStatement(HandleValue expr, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool emptyStatement(TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool ifStatement(HandleValue test, HandleValue cons, HandleValue alt, TokenPos* pos,
                      MutableHandleValue dst);
 
-    bool breakStatement(HandleValue label, TokenPos* pos, MutableHandleValue dst);
-
-    bool continueStatement(HandleValue label, TokenPos* pos, MutableHandleValue dst);
-
-    bool labeledStatement(HandleValue label, HandleValue stmt, TokenPos* pos,
+    MOZ_MUST_USE bool breakStatement(HandleValue label, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool continueStatement(HandleValue label, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool labeledStatement(HandleValue label, HandleValue stmt, TokenPos* pos,
                           MutableHandleValue dst);
 
-    bool throwStatement(HandleValue arg, TokenPos* pos, MutableHandleValue dst);
-
-    bool returnStatement(HandleValue arg, TokenPos* pos, MutableHandleValue dst);
-
-    bool forStatement(HandleValue init, HandleValue test, HandleValue update, HandleValue stmt,
+    MOZ_MUST_USE bool throwStatement(HandleValue arg, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool returnStatement(HandleValue arg, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool forStatement(HandleValue init, HandleValue test, HandleValue update, HandleValue stmt,
                       TokenPos* pos, MutableHandleValue dst);
 
-    bool forInStatement(HandleValue var, HandleValue expr, HandleValue stmt,
-                        bool isForEach, TokenPos* pos, MutableHandleValue dst);
-
-    bool forOfStatement(HandleValue var, HandleValue expr, HandleValue stmt, TokenPos* pos,
-                        MutableHandleValue dst);
-
-    bool withStatement(HandleValue expr, HandleValue stmt, TokenPos* pos, MutableHandleValue dst);
-
-    bool whileStatement(HandleValue test, HandleValue stmt, TokenPos* pos, MutableHandleValue dst);
-
-    bool doWhileStatement(HandleValue stmt, HandleValue test, TokenPos* pos,
-                          MutableHandleValue dst);
-
-    bool switchStatement(HandleValue disc, NodeVector& elts, bool lexical, TokenPos* pos,
-                         MutableHandleValue dst);
-
-    bool tryStatement(HandleValue body, NodeVector& guarded, HandleValue unguarded,
-                      HandleValue finally, TokenPos* pos, MutableHandleValue dst);
-
-    bool debuggerStatement(TokenPos* pos, MutableHandleValue dst);
-
-    bool importDeclaration(NodeVector& elts, HandleValue moduleSpec, TokenPos* pos, MutableHandleValue dst);
-
-    bool importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos* pos, MutableHandleValue dst);
-
-    bool exportDeclaration(HandleValue decl, NodeVector& elts, HandleValue moduleSpec,
-                           HandleValue isDefault, TokenPos* pos, MutableHandleValue dst);
-
-    bool exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos* pos, MutableHandleValue dst);
-
-    bool exportBatchSpecifier(TokenPos* pos, MutableHandleValue dst);
-
-    bool classDefinition(bool expr, HandleValue name, HandleValue heritage, HandleValue block, TokenPos* pos,
-                         MutableHandleValue dst);
-    bool classMethods(NodeVector& methods, MutableHandleValue dst);
-    bool classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic, TokenPos* pos, MutableHandleValue dst);
+    MOZ_MUST_USE bool forInStatement(HandleValue var, HandleValue expr, HandleValue stmt,
+                                     bool isForEach, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool forOfStatement(HandleValue var, HandleValue expr, HandleValue stmt, TokenPos* pos,
+                                     MutableHandleValue dst);
+
+    MOZ_MUST_USE bool withStatement(HandleValue expr, HandleValue stmt, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool whileStatement(HandleValue test, HandleValue stmt, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool doWhileStatement(HandleValue stmt, HandleValue test, TokenPos* pos,
+                                       MutableHandleValue dst);
+
+    MOZ_MUST_USE bool switchStatement(HandleValue disc, NodeVector& elts, bool lexical, TokenPos* pos,
+                                      MutableHandleValue dst);
+
+    MOZ_MUST_USE bool tryStatement(HandleValue body, NodeVector& guarded, HandleValue unguarded,
+                                   HandleValue finally, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool debuggerStatement(TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool importDeclaration(NodeVector& elts, HandleValue moduleSpec, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool importSpecifier(HandleValue importName, HandleValue bindingName, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool exportDeclaration(HandleValue decl, NodeVector& elts, HandleValue moduleSpec,
+                                        HandleValue isDefault, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool exportSpecifier(HandleValue bindingName, HandleValue exportName, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool exportBatchSpecifier(TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool classDefinition(bool expr, HandleValue name, HandleValue heritage,
+                                      HandleValue block, TokenPos* pos, MutableHandleValue dst);
+    MOZ_MUST_USE bool classMethods(NodeVector& methods, MutableHandleValue dst);
+    MOZ_MUST_USE bool classMethod(HandleValue name, HandleValue body, PropKind kind, bool isStatic,
+                                  TokenPos* pos, MutableHandleValue dst);
 
     /*
      * expressions
      */
 
-    bool binaryExpression(BinaryOperator op, HandleValue left, HandleValue right, TokenPos* pos,
-                          MutableHandleValue dst);
-
-    bool unaryExpression(UnaryOperator op, HandleValue expr, TokenPos* pos, MutableHandleValue dst);
-
-    bool assignmentExpression(AssignmentOperator op, HandleValue lhs, HandleValue rhs,
-                              TokenPos* pos, MutableHandleValue dst);
-
-    bool updateExpression(HandleValue expr, bool incr, bool prefix, TokenPos* pos,
-                          MutableHandleValue dst);
-
-    bool logicalExpression(bool lor, HandleValue left, HandleValue right, TokenPos* pos,
-                           MutableHandleValue dst);
-
-    bool conditionalExpression(HandleValue test, HandleValue cons, HandleValue alt, TokenPos* pos,
-                               MutableHandleValue dst);
-
-    bool sequenceExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool newExpression(HandleValue callee, NodeVector& args, TokenPos* pos, MutableHandleValue dst);
-
-    bool callExpression(HandleValue callee, NodeVector& args, TokenPos* pos,
-                        MutableHandleValue dst);
-
-    bool memberExpression(bool computed, HandleValue expr, HandleValue member, TokenPos* pos,
-                          MutableHandleValue dst);
-
-    bool arrayExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool templateLiteral(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool taggedTemplate(HandleValue callee, NodeVector& args, TokenPos* pos,
-                        MutableHandleValue dst);
-
-    bool callSiteObj(NodeVector& raw, NodeVector& cooked, TokenPos* pos, MutableHandleValue dst);
-
-    bool spreadExpression(HandleValue expr, TokenPos* pos, MutableHandleValue dst);
-
-    bool computedName(HandleValue name, TokenPos* pos, MutableHandleValue dst);
-
-    bool objectExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool thisExpression(TokenPos* pos, MutableHandleValue dst);
-
-    bool yieldExpression(HandleValue arg, YieldKind kind, TokenPos* pos, MutableHandleValue dst);
-
-    bool comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach, bool isForOf, TokenPos* pos,
-                            MutableHandleValue dst);
-    bool comprehensionIf(HandleValue test, TokenPos* pos, MutableHandleValue dst);
-
-    bool comprehensionExpression(HandleValue body, NodeVector& blocks, HandleValue filter,
-                                 bool isLegacy, TokenPos* pos, MutableHandleValue dst);
-
-    bool generatorExpression(HandleValue body, NodeVector& blocks, HandleValue filter,
-                             bool isLegacy, TokenPos* pos, MutableHandleValue dst);
-
-    bool metaProperty(HandleValue meta, HandleValue property, TokenPos* pos, MutableHandleValue dst);
-
-    bool super(TokenPos* pos, MutableHandleValue dst);
+    MOZ_MUST_USE bool binaryExpression(BinaryOperator op, HandleValue left, HandleValue right,
+                                       TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool unaryExpression(UnaryOperator op, HandleValue expr, TokenPos* pos,
+                                      MutableHandleValue dst);
+
+    MOZ_MUST_USE bool assignmentExpression(AssignmentOperator op, HandleValue lhs, HandleValue rhs,
+                                           TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool updateExpression(HandleValue expr, bool incr, bool prefix, TokenPos* pos,
+                                       MutableHandleValue dst);
+
+    MOZ_MUST_USE bool logicalExpression(bool lor, HandleValue left, HandleValue right, TokenPos* pos,
+                                        MutableHandleValue dst);
+
+    MOZ_MUST_USE bool conditionalExpression(HandleValue test, HandleValue cons, HandleValue alt,
+                                            TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool sequenceExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool newExpression(HandleValue callee, NodeVector& args, TokenPos* pos,
+                                    MutableHandleValue dst);
+
+    MOZ_MUST_USE bool callExpression(HandleValue callee, NodeVector& args, TokenPos* pos,
+                                     MutableHandleValue dst);
+
+    MOZ_MUST_USE bool memberExpression(bool computed, HandleValue expr, HandleValue member,
+                                       TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool arrayExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool templateLiteral(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool taggedTemplate(HandleValue callee, NodeVector& args, TokenPos* pos,
+                                     MutableHandleValue dst);
+
+    MOZ_MUST_USE bool callSiteObj(NodeVector& raw, NodeVector& cooked, TokenPos* pos,
+                                  MutableHandleValue dst);
+
+    MOZ_MUST_USE bool spreadExpression(HandleValue expr, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool computedName(HandleValue name, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool objectExpression(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool thisExpression(TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool yieldExpression(HandleValue arg, YieldKind kind, TokenPos* pos,
+                                      MutableHandleValue dst);
+
+    MOZ_MUST_USE bool comprehensionBlock(HandleValue patt, HandleValue src, bool isForEach,
+                                         bool isForOf, TokenPos* pos, MutableHandleValue dst);
+    MOZ_MUST_USE bool comprehensionIf(HandleValue test, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool comprehensionExpression(HandleValue body, NodeVector& blocks,
+                                              HandleValue filter, bool isLegacy, TokenPos* pos,
+                                              MutableHandleValue dst);
+
+    MOZ_MUST_USE bool generatorExpression(HandleValue body, NodeVector& blocks, HandleValue filter,
+                                          bool isLegacy, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool metaProperty(HandleValue meta, HandleValue property, TokenPos* pos,
+                                   MutableHandleValue dst);
+
+    MOZ_MUST_USE bool super(TokenPos* pos, MutableHandleValue dst);
 
     /*
      * declarations
      */
 
-    bool variableDeclaration(NodeVector& elts, VarDeclKind kind, TokenPos* pos,
-                             MutableHandleValue dst);
+    MOZ_MUST_USE bool variableDeclaration(NodeVector& elts, VarDeclKind kind, TokenPos* pos,
+                                          MutableHandleValue dst);
 
     /*
      * patterns
      */
 
-    bool arrayPattern(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool objectPattern(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
-
-    bool propertyPattern(HandleValue key, HandleValue patt, bool isShorthand, TokenPos* pos,
-                         MutableHandleValue dst);
+    MOZ_MUST_USE bool arrayPattern(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool objectPattern(NodeVector& elts, TokenPos* pos, MutableHandleValue dst);
+
+    MOZ_MUST_USE bool propertyPattern(HandleValue key, HandleValue patt, bool isShorthand,
+                                      TokenPos* pos, MutableHandleValue dst);
 };
 
 } /* anonymous namespace */
 
 bool
 NodeBuilder::createNode(ASTType type, TokenPos* pos, MutableHandleObject dst)
 {
     MOZ_ASSERT(type > AST_ERROR && type < AST_LIMIT);
@@ -729,18 +737,17 @@ NodeBuilder::newNodeLoc(TokenPos* pos, M
     return true;
 }
 
 bool
 NodeBuilder::setNodeLoc(HandleObject node, TokenPos* pos)
 {
     if (!saveLoc) {
         RootedValue nullVal(cx, NullValue());
-        defineProperty(node, "loc", nullVal);
-        return true;
+        return defineProperty(node, "loc", nullVal);
     }
 
     RootedValue loc(cx);
     return newNodeLoc(pos, &loc) &&
            defineProperty(node, "loc", loc);
 }
 
 bool
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1305220.js
@@ -0,0 +1,23 @@
+// |jit-test| allow-oom
+if (!('oomAfterAllocations' in this))
+    quit();
+s = newGlobal();
+evalcx("\
+    gczeal(10, 2);\
+    k = {\
+        [Symbol]() {}\
+    };\
+", s);
+gczeal(0);
+evalcx("\
+    var g = newGlobal();\
+    b = new Debugger;\
+    g.h = function() {\
+        g.oomAfterAllocations(1);\
+    };\
+    g.eval(\"\" + function f() g());\
+    g.eval(\"\" + function g() h());\
+    g.eval(\"(\" + function() {\
+        f();\
+    } + \")()\");\
+", s);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1296667.js
@@ -0,0 +1,11 @@
+args = ""
+for (i = 0; i < 2000; i++) {
+    args += "arg" + i;
+    if (i != 1999) args += ",";
+}
+MyFunc = MyObject = Function(args, "for (var i = 0; i < MyFunc.length; i++ )   break; eval('this.arg'+i +'=arg'+i) ");
+new function TestCase() {
+    if (inIon())
+        return;
+    TestCase(eval("var EXP_1 = new MyObject; var EXP_2 = new MyObject; EXP_1 - EXP_2"));
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1298354.js
@@ -0,0 +1,17 @@
+// |jit-test| error: ReferenceError
+
+new Function(`
+  while (true) {
+    try {
+        var buf = new Uint8ClampedArray(a);
+    } catch (e) {
+        break;
+    }
+  }
+  var caughtInvalidArguments = false;
+  while (true) {
+    var a = inIon() ? -true.get : 0;
+    while (x > 7 & 0) {}
+  }
+`)();
+
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1299007.js
@@ -0,0 +1,41 @@
+
+evalInFrame = function(global) {
+   dbgGlobal = newGlobal()
+   dbg = new dbgGlobal.Debugger
+   return function(upCount, code) {
+       dbg.addDebuggee(global)
+       var frame = dbg.getNewestFrame().older
+       for (var i = 0; i < upCount; i++)
+           if (!frame) frame = older
+       completion = frame.eval(code)
+   }
+}(this);
+function h() {
+    evalInFrame(0, "")
+    evalInFrame(0, "i")
+    evalInFrame(0, "a.push")
+    evalInFrame(1, "a.pushy")
+}
+function g() h()
+function f() g()
+f()
+evaluate(`
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+g()
+h()
+`);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/validate.js
@@ -0,0 +1,33 @@
+// |jit-test| test-also-wasm-baseline
+load(libdir + "wasm.js");
+
+const { validate } = WebAssembly;
+
+assertErrorMessage(() => validate(), Error, /requires more than 0 arguments/);
+
+const argError = /first argument must be an ArrayBuffer or typed array object/;
+assertErrorMessage(() => validate(null), Error, argError);
+assertErrorMessage(() => validate(true), Error, argError);
+assertErrorMessage(() => validate(42), Error, argError);
+assertErrorMessage(() => validate(NaN), Error, argError);
+assertErrorMessage(() => validate('yo'), Error, argError);
+assertErrorMessage(() => validate([]), Error, argError);
+assertErrorMessage(() => validate({}), Error, argError);
+assertErrorMessage(() => validate(Symbol.iterator), Error, argError);
+assertErrorMessage(() => validate({ valueOf: () => new ArrayBuffer(65536) }), Error, argError);
+
+assertEq(validate(wasmTextToBinary(`(module)`)), true);
+
+assertEq(validate(wasmTextToBinary(`(module (export "run" 0))`)), false);
+assertEq(validate(wasmTextToBinary(`(module (func) (export "run" 0))`)), true);
+
+// Feature-testing proof-of-concept.
+assertEq(validate(wasmTextToBinary(`(module (memory 1) (func (result i32) (current_memory)))`)), true);
+assertEq(validate(wasmTextToBinary(`(module (memory 1) (func (result i32) (grow_memory (i32.const 42))))`)), true);
+
+// Enable warning as errors.
+options("werror");
+assertErrorMessage(() => validate(wasmTextToBinary(`(module (func) (func) (export "a" 2))`)),
+                  WebAssembly.CompileError,
+                  /exported function index out of bounds/);
+options("werror");
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -818,17 +818,18 @@ IonBuilder::build()
     } else {
         JitSpew(JitSpew_IonScripts, "%sompiling script %s:%" PRIuSIZE " (%p) (warmup-counter=%" PRIu32 ", level=%s)",
                 (script()->hasIonScript() ? "Rec" : "C"),
                 script()->filename(), script()->lineno(), (void*)script(),
                 script()->getWarmUpCount(), OptimizationLevelString(optimizationInfo().level()));
     }
 #endif
 
-    initParameters();
+    if (!initParameters())
+        return false;
     initLocals();
 
     // Initialize something for the env chain. We can bail out before the
     // start instruction, but the snapshot is encoded *at* the start
     // instruction, which means generating any code that could load into
     // registers is illegal.
     MInstruction* env = MConstant::New(alloc(), UndefinedValue());
     current->add(env);
@@ -857,17 +858,18 @@ IonBuilder::build()
     current->add(check);
     MResumePoint* entryRpCopy = MResumePoint::Copy(alloc(), current->entryResumePoint());
     if (!entryRpCopy)
         return false;
     check->setResumePoint(entryRpCopy);
 
     // Parameters have been checked to correspond to the typeset, now we unbox
     // what we can in an infallible manner.
-    rewriteParameters();
+    if (!rewriteParameters())
+        return false;
 
     // Check for redeclaration errors for global scripts.
     if (!info().funMaybeLazy() && !info().module() &&
         script()->bodyScope()->is<GlobalScope>() &&
         script()->bodyScope()->as<GlobalScope>().hasBindings())
     {
         MGlobalNameConflictsCheck* redeclCheck = MGlobalNameConflictsCheck::New(alloc());
         current->add(redeclCheck);
@@ -1131,35 +1133,39 @@ IonBuilder::rewriteParameter(uint32_t sl
     // As usual, it would be invalid for v1 to be captured in the initial
     // resume point, rather than v0.
     current->rewriteSlot(slotIdx, actual);
 }
 
 // Apply Type Inference information to parameters early on, unboxing them if
 // they have a definitive type. The actual guards will be emitted by the code
 // generator, explicitly, as part of the function prologue.
-void
+bool
 IonBuilder::rewriteParameters()
 {
     MOZ_ASSERT(info().environmentChainSlot() == 0);
 
     if (!info().funMaybeLazy())
-        return;
+        return true;
 
     for (uint32_t i = info().startArgSlot(); i < info().endArgSlot(); i++) {
+        if (!alloc().ensureBallast())
+            return false;
         MDefinition* param = current->getSlot(i);
         rewriteParameter(i, param, param->toParameter()->index());
     }
-}
-
-void
+
+    return true;
+}
+
+bool
 IonBuilder::initParameters()
 {
     if (!info().funMaybeLazy())
-        return;
+        return true;
 
     // If we are doing OSR on a frame which initially executed in the
     // interpreter and didn't accumulate type information, try to use that OSR
     // frame to determine possible initial types for 'this' and parameters.
 
     if (thisTypes->empty() && baselineFrame_) {
         TypeSet::Type type = baselineFrame_->thisType;
         if (type.isSingletonUnchecked())
@@ -1177,20 +1183,24 @@ IonBuilder::initParameters()
             !script_->baselineScript()->modifiesArguments())
         {
             TypeSet::Type type = baselineFrame_->argTypes[i];
             if (type.isSingletonUnchecked())
                 checkNurseryObject(type.singleton());
             types->addType(type, alloc_->lifoAlloc());
         }
 
-        param = MParameter::New(alloc(), i, types);
+        param = MParameter::New(alloc().fallible(), i, types);
+        if (!param)
+            return false;
         current->add(param);
         current->initSlot(info().argSlotUnchecked(i), param);
     }
+
+    return true;
 }
 
 void
 IonBuilder::initLocals()
 {
     // Initialize all frame slots to undefined. Lexical bindings are temporal
     // dead zoned in bytecode.
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -331,20 +331,20 @@ class IonBuilder
     // IonBuilder.cpp, near the definition for this function.
     MOZ_MUST_USE bool resume(MInstruction* ins, jsbytecode* pc, MResumePoint::Mode mode);
     MOZ_MUST_USE bool resumeAt(MInstruction* ins, jsbytecode* pc);
     MOZ_MUST_USE bool resumeAfter(MInstruction* ins);
     MOZ_MUST_USE bool maybeInsertResume();
 
     void insertRecompileCheck();
 
-    void initParameters();
+    MOZ_MUST_USE bool initParameters();
     void initLocals();
     void rewriteParameter(uint32_t slotIdx, MDefinition* param, int32_t argIndex);
-    void rewriteParameters();
+    MOZ_MUST_USE bool rewriteParameters();
     MOZ_MUST_USE bool initEnvironmentChain(MDefinition* callee = nullptr);
     MOZ_MUST_USE bool initArgumentsObject();
     void pushConstant(const Value& v);
 
     MConstant* constant(const Value& v);
     MConstant* constantInt(int32_t i);
     MInstruction* initializedLength(MDefinition* obj, MDefinition* elements,
                                     JSValueType unboxedType);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1862,21 +1862,20 @@ MAtomicIsLockFree::foldsTo(TempAllocator
     MDefinition* input = getOperand(0);
     if (!input->isConstant() || input->type() != MIRType::Int32)
         return this;
 
     int32_t i = input->toConstant()->toInt32();
     return MConstant::New(alloc, BooleanValue(AtomicOperations::isLockfree(i)));
 }
 
-MParameter*
-MParameter::New(TempAllocator& alloc, int32_t index, TemporaryTypeSet* types)
-{
-    return new(alloc) MParameter(index, types);
-}
+// Define |THIS_SLOT| as part of this translation unit, as it is used to
+// specialized the parameterized |New| function calls introduced by
+// TRIVIAL_NEW_WRAPPERS.
+const int32_t MParameter::THIS_SLOT;
 
 void
 MParameter::printOpcode(GenericPrinter& out) const
 {
     PrintOpcodeName(out, op());
     if (index() == THIS_SLOT)
         out.printf(" THIS_SLOT");
     else
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -2660,30 +2660,28 @@ class MCloneLiteral
     INSTRUCTION_HEADER(CloneLiteral)
     TRIVIAL_NEW_WRAPPERS
 };
 
 class MParameter : public MNullaryInstruction
 {
     int32_t index_;
 
-  public:
-    static const int32_t THIS_SLOT = -1;
-
     MParameter(int32_t index, TemporaryTypeSet* types)
       : index_(index)
     {
         setResultType(MIRType::Value);
         setResultTypeSet(types);
     }
 
   public:
     INSTRUCTION_HEADER(Parameter)
-    static MParameter* New(TempAllocator& alloc, int32_t index, TemporaryTypeSet* types);
-
+    TRIVIAL_NEW_WRAPPERS
+
+    static const int32_t THIS_SLOT = -1;
     int32_t index() const {
         return index_;
     }
     void printOpcode(GenericPrinter& out) const override;
 
     HashNumber valueHash() const override;
     bool congruentTo(const MDefinition* ins) const override;
 };
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -2341,16 +2341,20 @@ RangeAnalysis::addRangeAssertions()
 
     // Check the computed range for this instruction, if the option is set. Note
     // that this code is quite invasive; it adds numerous additional
     // instructions for each MInstruction with a computed range, and it uses
     // registers, so it also affects register allocation.
     for (ReversePostorderIterator iter(graph_.rpoBegin()); iter != graph_.rpoEnd(); iter++) {
         MBasicBlock* block = *iter;
 
+        // Do not add assertions in unreachable blocks.
+        if (block->unreachable())
+            continue;
+
         for (MDefinitionIterator iter(block); iter; iter++) {
             MDefinition* ins = *iter;
 
             // Perform range checking for all numeric and numeric-like types.
             if (!IsNumberType(ins->type()) &&
                 ins->type() != MIRType::Boolean &&
                 ins->type() != MIRType::Value)
             {
@@ -3486,23 +3490,24 @@ RangeAnalysis::prepareForUCE(bool* shoul
         if (!cond->isTest())
             continue;
 
         // Replace the condition of the test control instruction by a constant
         // chosen based which of the successors has the unreachable flag which is
         // added by MBeta::computeRange on its own block.
         MTest* test = cond->toTest();
         MDefinition* condition = test->input();
-        MConstant* constant = nullptr;
-        if (block == test->ifTrue()) {
-            constant = MConstant::New(alloc(), BooleanValue(false));
-        } else {
-            MOZ_ASSERT(block == test->ifFalse());
-            constant = MConstant::New(alloc(), BooleanValue(true));
-        }
+
+        // If the false-branch is unreachable, then the test condition must be true.
+        // If the true-branch is unreachable, then the test condition must be false.
+        MOZ_ASSERT(block == test->ifTrue() || block == test->ifFalse());
+        bool value = block == test->ifFalse();
+        MConstant* constant = MConstant::New(alloc().fallible(), BooleanValue(value));
+        if (!constant)
+            return false;
 
         if (DeadIfUnused(condition))
             condition->setGuardRangeBailoutsUnchecked();
 
         test->block()->insertBefore(test, constant);
 
         test->replaceOperand(0, constant);
         JitSpew(JitSpew_Range, "Update condition of %d to reflect unreachable branches.",
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -7,16 +7,17 @@
 /*
  * JS bytecode descriptors, disassemblers, and (expression) decompilers.
  */
 
 #include "jsopcodeinlines.h"
 
 #define __STDC_FORMAT_MACROS
 
+#include "mozilla/Attributes.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/Sprintf.h"
 
 #include <algorithm>
 #include <ctype.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <string.h>
@@ -142,91 +143,115 @@ js::StackDefs(JSScript* script, jsbyteco
     JSOp op = (JSOp) *pc;
     const JSCodeSpec& cs = CodeSpec[op];
     MOZ_ASSERT(cs.ndefs >= 0);
     return cs.ndefs;
 }
 
 const char * PCCounts::numExecName = "interp";
 
-void
-js::DumpIonScriptCounts(Sprinter* sp, HandleScript script,
-        jit::IonScriptCounts* ionCounts)
+static MOZ_MUST_USE bool
+DumpIonScriptCounts(Sprinter* sp, HandleScript script, jit::IonScriptCounts* ionCounts)
 {
-    Sprint(sp, "IonScript [%lu blocks]:\n", ionCounts->numBlocks());
+    if (!sp->jsprintf("IonScript [%lu blocks]:\n", ionCounts->numBlocks()))
+        return false;
+
     for (size_t i = 0; i < ionCounts->numBlocks(); i++) {
         const jit::IonBlockCounts& block = ionCounts->block(i);
         unsigned lineNumber = 0, columnNumber = 0;
         lineNumber = PCToLineNumber(script, script->offsetToPC(block.offset()), &columnNumber);
-        Sprint(sp, "BB #%lu [%05u,%u,%u]", block.id(), block.offset(),
-               lineNumber, columnNumber);
-        if (block.description())
-            Sprint(sp, " [inlined %s]", block.description());
-        for (size_t j = 0; j < block.numSuccessors(); j++)
-            Sprint(sp, " -> #%lu", block.successor(j));
-        Sprint(sp, " :: %llu hits\n", block.hitCount());
-        Sprint(sp, "%s\n", block.code());
+        if (!sp->jsprintf("BB #%lu [%05u,%u,%u]",
+                          block.id(), block.offset(), lineNumber, columnNumber))
+        {
+            return false;
+        }
+        if (block.description()) {
+            if (!sp->jsprintf(" [inlined %s]", block.description()))
+                return false;
+        }
+        for (size_t j = 0; j < block.numSuccessors(); j++) {
+            if (!sp->jsprintf(" -> #%lu", block.successor(j)))
+                return false;
+        }
+        if (!sp->jsprintf(" :: %llu hits\n", block.hitCount()))
+            return false;
+        if (!sp->jsprintf("%s\n", block.code()))
+            return false;
     }
+
+    return true;
 }
 
-void
-js::DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp)
+static MOZ_MUST_USE bool
+DumpPCCounts(JSContext* cx, HandleScript script, Sprinter* sp)
 {
     MOZ_ASSERT(script->hasScriptCounts());
 
 #ifdef DEBUG
     jsbytecode* pc = script->code();
     while (pc < script->codeEnd()) {
         jsbytecode* next = GetNextPc(pc);
 
         if (!Disassemble1(cx, script, pc, script->pcToOffset(pc), true, sp))
-            return;
+            return false;
 
-        Sprint(sp, "                  {");
+        if (sp->put("                  {") < 0)
+            return false;
+
         PCCounts* counts = script->maybeGetPCCounts(pc);
-        double val = counts ? counts->numExec() : 0.0;
-        if (val)
-            Sprint(sp, "\"%s\": %.0f", PCCounts::numExecName, val);
-        Sprint(sp, "}\n");
+        if (double val = counts ? counts->numExec() : 0.0) {
+            if (!sp->jsprintf("\"%s\": %.0f", PCCounts::numExecName, val))
+                return false;
+        }
+        if (sp->put("}\n") < 0)
+            return false;
 
         pc = next;
     }
 #endif
 
     jit::IonScriptCounts* ionCounts = script->getIonCounts();
+    while (ionCounts) {
+        if (!DumpIonScriptCounts(sp, script, ionCounts))
+            return false;
 
-    while (ionCounts) {
-        DumpIonScriptCounts(sp, script, ionCounts);
         ionCounts = ionCounts->previous();
     }
+
+    return true;
 }
 
-void
+bool
 js::DumpCompartmentPCCounts(JSContext* cx)
 {
     Rooted<GCVector<JSScript*>> scripts(cx, GCVector<JSScript*>(cx));
     for (auto iter = cx->zone()->cellIter<JSScript>(); !iter.done(); iter.next()) {
         JSScript* script = iter;
         if (script->compartment() != cx->compartment())
             continue;
-        if (script->hasScriptCounts() && !scripts.append(script))
-            return;
+        if (script->hasScriptCounts()) {
+            if (!scripts.append(script))
+                return false;
+        }
     }
 
     for (uint32_t i = 0; i < scripts.length(); i++) {
         HandleScript script = scripts[i];
         Sprinter sprinter(cx);
         if (!sprinter.init())
-            return;
+            return false;
 
         fprintf(stdout, "--- SCRIPT %s:%" PRIuSIZE " ---\n", script->filename(), script->lineno());
-        DumpPCCounts(cx, script, &sprinter);
+        if (!DumpPCCounts(cx, script, &sprinter))
+            return false;
         fputs(sprinter.string(), stdout);
         fprintf(stdout, "--- END SCRIPT %s:%" PRIuSIZE " ---\n", script->filename(), script->lineno());
     }
+
+    return true;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Bytecode Parser
 /////////////////////////////////////////////////////////////////////
 
 namespace {
 
@@ -637,82 +662,108 @@ js::ReconstructStackDepth(JSContext* cx,
     return true;
 }
 
 /*
  * If pc != nullptr, include a prefix indicating whether the PC is at the
  * current line. If showAll is true, include the source note type and the
  * entry stack depth.
  */
-static bool
+static MOZ_MUST_USE bool
 DisassembleAtPC(JSContext* cx, JSScript* scriptArg, bool lines,
                 jsbytecode* pc, bool showAll, Sprinter* sp)
 {
     RootedScript script(cx, scriptArg);
     BytecodeParser parser(cx, script);
 
-    if (showAll && !parser.parse())
+    if (showAll) {
+        if (!parser.parse())
+            return false;
+
+        if (!sp->jsprintf("%s:%u\n", script->filename(), unsigned(script->lineno())))
+            return false;
+    }
+
+    if (pc != nullptr) {
+        if (sp->put("    ") < 0)
+            return false;
+    }
+    if (showAll) {
+        if (sp->put("sn stack ") < 0)
+            return false;
+    }
+    if (sp->put("loc   ") < 0)
+        return false;
+    if (lines) {
+        if (sp->put("line") < 0)
+            return false;
+    }
+    if (sp->put("  op\n") < 0)
         return false;
 
-    if (showAll)
-        Sprint(sp, "%s:%" PRIuSIZE "\n", script->filename(), script->lineno());
-
-    if (pc != nullptr)
-        sp->put("    ");
-    if (showAll)
-        sp->put("sn stack ");
-    sp->put("loc   ");
-    if (lines)
-        sp->put("line");
-    sp->put("  op\n");
-
-    if (pc != nullptr)
-        sp->put("    ");
-    if (showAll)
-        sp->put("-- ----- ");
-    sp->put("----- ");
-    if (lines)
-        sp->put("----");
-    sp->put("  --\n");
+    if (pc != nullptr) {
+        if (sp->put("    ") < 0)
+            return false;
+    }
+    if (showAll) {
+        if (sp->put("-- ----- ") < 0)
+            return false;
+    }
+    if (sp->put("----- ") < 0)
+        return false;
+    if (lines) {
+        if (sp->put("----") < 0)
+            return false;
+    }
+    if (sp->put("  --\n") < 0)
+        return false;
 
     jsbytecode* next = script->code();
     jsbytecode* end = script->codeEnd();
     while (next < end) {
-        if (next == script->main())
-            sp->put("main:\n");
+        if (next == script->main()) {
+            if (sp->put("main:\n") < 0)
+                return false;
+        }
         if (pc != nullptr) {
-            if (pc == next)
-                sp->put("--> ");
-            else
-                sp->put("    ");
+            if (sp->put(pc == next ? "--> " : "    ") < 0)
+                return false;
         }
         if (showAll) {
             jssrcnote* sn = GetSrcNote(cx, script, next);
             if (sn) {
                 MOZ_ASSERT(!SN_IS_TERMINATOR(sn));
                 jssrcnote* next = SN_NEXT(sn);
                 while (!SN_IS_TERMINATOR(next) && SN_DELTA(next) == 0) {
-                    Sprint(sp, "%02u\n    ", SN_TYPE(sn));
+                    if (!sp->jsprintf("%02u\n    ", SN_TYPE(sn)))
+                        return false;
                     sn = next;
                     next = SN_NEXT(sn);
                 }
-                Sprint(sp, "%02u ", SN_TYPE(sn));
+                if (!sp->jsprintf("%02u ", SN_TYPE(sn)))
+                    return false;
+            } else {
+                if (sp->put("   ") < 0)
+                    return false;
             }
-            else
-                sp->put("   ");
-            if (parser.isReachable(next))
-                Sprint(sp, "%05u ", parser.stackDepthAtPC(next));
-            else
-                Sprint(sp, "      ");
+            if (parser.isReachable(next)) {
+                if (!sp->jsprintf("%05u ", parser.stackDepthAtPC(next)))
+                    return false;
+            } else {
+                if (sp->put("      ") < 0)
+                    return false;
+            }
         }
         unsigned len = Disassemble1(cx, script, next, script->pcToOffset(next), lines, sp);
         if (!len)
             return false;
+
         next += len;
     }
+
     return true;
 }
 
 bool
 js::Disassemble(JSContext* cx, HandleScript script, bool lines, Sprinter* sp)
 {
     return DisassembleAtPC(cx, script, lines, nullptr, false, sp);
 }
@@ -881,162 +932,164 @@ js::Disassemble1(JSContext* cx, HandleSc
         SprintfLiteral(numBuf1, "%d", op);
         SprintfLiteral(numBuf2, "%d", JSOP_LIMIT);
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              JSMSG_BYTECODE_TOO_BIG, numBuf1, numBuf2);
         return 0;
     }
     const JSCodeSpec* cs = &CodeSpec[op];
     ptrdiff_t len = (ptrdiff_t) cs->length;
-    if (Sprint(sp, "%05u:", loc) == -1)
+    if (!sp->jsprintf("%05u:", loc))
         return 0;
-    if (lines && Sprint(sp, "%4u", PCToLineNumber(script, pc)) == -1)
-        return 0;
-    if (Sprint(sp, "  %s", CodeName[op]) == -1)
+    if (lines) {
+        if (!sp->jsprintf("%4u", PCToLineNumber(script, pc)))
+            return 0;
+    }
+    if (!sp->jsprintf("  %s", CodeName[op]))
         return 0;
 
     int i;
     switch (JOF_TYPE(cs->format)) {
       case JOF_BYTE:
           // Scan the trynotes to find the associated catch block
           // and make the try opcode look like a jump instruction
           // with an offset. This simplifies code coverage analysis
           // based on this disassembled output.
           if (op == JSOP_TRY) {
               TryNoteArray* trynotes = script->trynotes();
               uint32_t i;
               for(i = 0; i < trynotes->length; i++) {
                   JSTryNote note = trynotes->vector[i];
                   if (note.kind == JSTRY_CATCH && note.start == loc + 1) {
-                      if (Sprint(sp, " %u (%+d)",
-                                 (unsigned int) (loc+note.length+1),
-                                 (int) (note.length+1)) == -1)
+                      if (!sp->jsprintf(" %u (%+d)",
+                                        unsigned(loc + note.length + 1),
+                                        int(note.length + 1)))
                       {
                           return 0;
                       }
                       break;
                   }
               }
           }
         break;
 
       case JOF_JUMP: {
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
-        if (Sprint(sp, " %u (%+d)", loc + (int) off, (int) off) == -1)
+        if (!sp->jsprintf(" %u (%+d)", unsigned(loc + int(off)), int(off)))
             return 0;
         break;
       }
 
       case JOF_SCOPE: {
         RootedScope scope(cx, script->getScope(GET_UINT32_INDEX(pc)));
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, scope, &bytes))
             return 0;
-        if (Sprint(sp, " %s", bytes.ptr()) == -1)
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_ENVCOORD: {
         RootedValue v(cx,
             StringValue(EnvironmentCoordinateName(cx->caches.envCoordinateNameCache, script, pc)));
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
         EnvironmentCoordinate ec(pc);
-        if (Sprint(sp, " %s (hops = %u, slot = %u)", bytes.ptr(), ec.hops(), ec.slot()) == -1)
+        if (!sp->jsprintf(" %s (hops = %u, slot = %u)", bytes.ptr(), ec.hops(), ec.slot()))
             return 0;
         break;
       }
 
       case JOF_ATOM: {
         RootedValue v(cx, StringValue(script->getAtom(GET_UINT32_INDEX(pc))));
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        if (Sprint(sp, " %s", bytes.ptr()) == -1)
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_DOUBLE: {
         RootedValue v(cx, script->getConst(GET_UINT32_INDEX(pc)));
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        if (Sprint(sp, " %s", bytes.ptr()) == -1)
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_OBJECT: {
         /* Don't call obj.toSource if analysis/inference is active. */
         if (script->zone()->types.activeAnalysis) {
-            if (Sprint(sp, " object") == -1)
+            if (!sp->jsprintf(" object"))
                 return 0;
             break;
         }
 
         JSObject* obj = script->getObject(GET_UINT32_INDEX(pc));
         {
             JSAutoByteString bytes;
             RootedValue v(cx, ObjectValue(*obj));
             if (!ToDisassemblySource(cx, v, &bytes))
                 return 0;
-            if (Sprint(sp, " %s", bytes.ptr()) == -1)
+            if (!sp->jsprintf(" %s", bytes.ptr()))
                 return 0;
         }
         break;
       }
 
       case JOF_REGEXP: {
         js::RegExpObject* obj = script->getRegExp(pc);
         JSAutoByteString bytes;
         RootedValue v(cx, ObjectValue(*obj));
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
-        if (Sprint(sp, " %s", bytes.ptr()) == -1)
+        if (!sp->jsprintf(" %s", bytes.ptr()))
             return 0;
         break;
       }
 
       case JOF_TABLESWITCH:
       {
         int32_t i, low, high;
 
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
         jsbytecode* pc2 = pc + JUMP_OFFSET_LEN;
         low = GET_JUMP_OFFSET(pc2);
         pc2 += JUMP_OFFSET_LEN;
         high = GET_JUMP_OFFSET(pc2);
         pc2 += JUMP_OFFSET_LEN;
-        if (Sprint(sp, " defaultOffset %d low %d high %d", int(off), low, high) == -1)
+        if (!sp->jsprintf(" defaultOffset %d low %d high %d", int(off), low, high))
             return 0;
         for (i = low; i <= high; i++) {
             off = GET_JUMP_OFFSET(pc2);
-            if (Sprint(sp, "\n\t%d: %d", i, int(off)) == -1)
+            if (!sp->jsprintf("\n\t%d: %d", i, int(off)))
                 return 0;
             pc2 += JUMP_OFFSET_LEN;
         }
         len = 1 + pc2 - pc;
         break;
       }
 
       case JOF_QARG:
-        if (Sprint(sp, " %u", GET_ARGNO(pc)) == -1)
+        if (!sp->jsprintf(" %u", GET_ARGNO(pc)))
             return 0;
         break;
 
       case JOF_LOCAL:
-        if (Sprint(sp, " %u", GET_LOCALNO(pc)) == -1)
+        if (!sp->jsprintf(" %u", GET_LOCALNO(pc)))
             return 0;
         break;
 
       case JOF_UINT32:
-        if (Sprint(sp, " %u", GET_UINT32(pc)) == -1)
+        if (!sp->jsprintf(" %u", GET_UINT32(pc)))
             return 0;
         break;
 
       case JOF_UINT16:
         i = (int)GET_UINT16(pc);
         goto print_int;
 
       case JOF_UINT24:
@@ -1051,17 +1104,17 @@ js::Disassemble1(JSContext* cx, HandleSc
       case JOF_INT8:
         i = GET_INT8(pc);
         goto print_int;
 
       case JOF_INT32:
         MOZ_ASSERT(op == JSOP_INT32);
         i = GET_INT32(pc);
       print_int:
-        if (Sprint(sp, " %d", i) == -1)
+        if (!sp->jsprintf(" %d", i))
             return 0;
         break;
 
       default: {
         char numBuf[12];
         SprintfLiteral(numBuf, "%x", cs->format);
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
                              JSMSG_UNKNOWN_FORMAT, numBuf);
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -6,16 +6,18 @@
 
 #ifndef jsopcode_h
 #define jsopcode_h
 
 /*
  * JS bytecode definitions.
  */
 
+#include "mozilla/Attributes.h"
+
 #include "jsbytecode.h"
 #include "jstypes.h"
 #include "NamespaceImports.h"
 
 #include "frontend/SourceNotes.h"
 #include "js/UniquePtr.h"
 #include "vm/Opcodes.h"
 #include "vm/Printer.h"
@@ -842,30 +844,23 @@ GetNextPc(jsbytecode* pc)
 {
     return pc + GetBytecodeLength(pc);
 }
 
 #if defined(DEBUG)
 /*
  * Disassemblers, for debugging only.
  */
-bool
+extern MOZ_MUST_USE bool
 Disassemble(JSContext* cx, JS::Handle<JSScript*> script, bool lines, Sprinter* sp);
 
 unsigned
 Disassemble1(JSContext* cx, JS::Handle<JSScript*> script, jsbytecode* pc, unsigned loc,
              bool lines, Sprinter* sp);
 
 #endif
 
-void
-DumpPCCounts(JSContext* cx, JS::Handle<JSScript*> script, Sprinter* sp);
+extern MOZ_MUST_USE bool
+DumpCompartmentPCCounts(JSContext* cx);
 
-namespace jit { struct IonScriptCounts; }
-void
-DumpIonScriptCounts(js::Sprinter* sp, HandleScript script,
-                    jit::IonScriptCounts* ionCounts);
-
-void
-DumpCompartmentPCCounts(JSContext* cx);
 } // namespace js
 
 #endif /* jsopcode_h */
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -1211,37 +1211,45 @@ fi
 
 dnl Checks for library functions.
 dnl ========================================================
 AC_PROG_GCC_TRADITIONAL
 AC_FUNC_MEMCMP
 AC_CHECK_FUNCS([getc_unlocked _getc_nolock gmtime_r localtime_r pthread_getname_np])
 
 dnl check for clock_gettime(), the CLOCK_MONOTONIC clock
-AC_CACHE_CHECK(for clock_gettime(CLOCK_MONOTONIC),
-               ac_cv_clock_monotonic,
-               [for libs in "" -lrt; do
-                    _SAVE_LIBS="$LIBS"
-                    LIBS="$LIBS $libs"
-                    AC_TRY_LINK([#include <time.h>],
-                                 [ struct timespec ts;
-                                   clock_gettime(CLOCK_MONOTONIC, &ts); ],
-                                 ac_cv_clock_monotonic=$libs
-                                 LIBS="$_SAVE_LIBS"
-                                 break,
-                                 ac_cv_clock_monotonic=no)
-                    LIBS="$_SAVE_LIBS"
-                done])
-if test "$ac_cv_clock_monotonic" != "no"; then
-    HAVE_CLOCK_MONOTONIC=1
-    REALTIME_LIBS=$ac_cv_clock_monotonic
-    AC_DEFINE(HAVE_CLOCK_MONOTONIC)
-    AC_SUBST(HAVE_CLOCK_MONOTONIC)
-    AC_SUBST_LIST(REALTIME_LIBS)
-fi
+dnl avoid this on Darwin, since depending on your system config, we may think
+dnl it exists but it really doesn't
+case "$OS_TARGET" in
+Darwin)
+  ;;
+*)
+  AC_CACHE_CHECK(for clock_gettime(CLOCK_MONOTONIC),
+                 ac_cv_clock_monotonic,
+                 [for libs in "" -lrt; do
+                      _SAVE_LIBS="$LIBS"
+                      LIBS="$LIBS $libs"
+                      AC_TRY_LINK([#include <time.h>],
+                                   [ struct timespec ts;
+                                     clock_gettime(CLOCK_MONOTONIC, &ts); ],
+                                   ac_cv_clock_monotonic=$libs
+                                   LIBS="$_SAVE_LIBS"
+                                   break,
+                                   ac_cv_clock_monotonic=no)
+                      LIBS="$_SAVE_LIBS"
+                  done])
+  if test "$ac_cv_clock_monotonic" != "no"; then
+      HAVE_CLOCK_MONOTONIC=1
+      REALTIME_LIBS=$ac_cv_clock_monotonic
+      AC_DEFINE(HAVE_CLOCK_MONOTONIC)
+      AC_SUBST(HAVE_CLOCK_MONOTONIC)
+      AC_SUBST_LIST(REALTIME_LIBS)
+  fi
+  ;;
+esac
 
 dnl Checks for math functions.
 dnl ========================================================
 AC_CHECK_LIB(m, sin)
 AC_CHECK_LIB(m, __sincos, AC_DEFINE(HAVE_SINCOS))
 
 
 dnl check for wcrtomb/mbrtowc
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3,21 +3,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/. */
 
 /* JS shell. */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/ScopeExit.h"
 #include "mozilla/SizePrintfMacros.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/TimeStamp.h"
 
 #ifdef XP_WIN
 # include <direct.h>
 # include <process.h>
 #endif
@@ -103,16 +105,17 @@
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::cli;
 using namespace js::shell;
 
 using mozilla::ArrayLength;
 using mozilla::Atomic;
+using mozilla::MakeScopeExit;
 using mozilla::Maybe;
 using mozilla::Nothing;
 using mozilla::NumberEqualsInt32;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::TimeDuration;
 using mozilla::TimeStamp;
 
@@ -2327,237 +2330,300 @@ UpdateSwitchTableBounds(JSContext* cx, H
         MOZ_ASSERT(op == JSOP_CONDSWITCH);
         return;
     }
 
     *start = script->pcToOffset(pc);
     *end = *start + (unsigned)(n * jmplen);
 }
 
-static void
+static MOZ_MUST_USE bool
 SrcNotes(JSContext* cx, HandleScript script, Sprinter* sp)
 {
-    Sprint(sp, "\nSource notes:\n");
-    Sprint(sp, "%4s %4s %5s %6s %-8s %s\n",
-           "ofs", "line", "pc", "delta", "desc", "args");
-    Sprint(sp, "---- ---- ----- ------ -------- ------\n");
+    if (sp->put("\nSource notes:\n") < 0 ||
+        !sp->jsprintf("%4s %4s %5s %6s %-8s %s\n",
+                      "ofs", "line", "pc", "delta", "desc", "args") ||
+        sp->put("---- ---- ----- ------ -------- ------\n") < 0)
+    {
+        return false;
+    }
+
     unsigned offset = 0;
     unsigned colspan = 0;
     unsigned lineno = script->lineno();
     jssrcnote* notes = script->notes();
     unsigned switchTableEnd = 0, switchTableStart = 0;
     for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
         unsigned delta = SN_DELTA(sn);
         offset += delta;
         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
         const char* name = js_SrcNoteSpec[type].name;
-        Sprint(sp, "%3u: %4u %5u [%4u] %-8s", unsigned(sn - notes), lineno, offset, delta, name);
+        if (!sp->jsprintf("%3u: %4u %5u [%4u] %-8s",
+                          unsigned(sn - notes), lineno, offset, delta, name))
+        {
+            return false;
+        }
+
         switch (type) {
           case SRC_NULL:
           case SRC_IF:
           case SRC_CONTINUE:
           case SRC_BREAK:
           case SRC_BREAK2LABEL:
           case SRC_SWITCHBREAK:
           case SRC_ASSIGNOP:
           case SRC_XDELTA:
             break;
 
           case SRC_COLSPAN:
             colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0));
-            Sprint(sp, "%d", colspan);