merge autoland to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Fri, 20 Oct 2017 11:37:54 +0200
changeset 387314 d1e995c8640a191cd127e87273ec96cb2fabffa9
parent 387221 be030533c5afe73f222918809d03b1b5f08f7ef3 (current diff)
parent 387313 58190ad9f36d303669e45fd91b6793ae1dea9a01 (diff)
child 387315 816bd8b20efbbcbb0f8e69cf8be925b011dec5dc
child 387405 cc3c9647f61b87e4e08677b2ba2ed428b65125a1
push id96408
push userarchaeopteryx@coole-files.de
push dateFri, 20 Oct 2017 09:49:09 +0000
treeherdermozilla-inbound@816bd8b20efb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone58.0a1
first release with
nightly linux32
d1e995c8640a / 58.0a1 / 20171020100426 / files
nightly linux64
d1e995c8640a / 58.0a1 / 20171020100426 / files
nightly mac
d1e995c8640a / 58.0a1 / 20171020100426 / files
nightly win32
d1e995c8640a / 58.0a1 / 20171020100426 / files
nightly win64
d1e995c8640a / 58.0a1 / 20171020100426 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge autoland to mozilla-central. r=merge a=merge MozReview-Commit-ID: BY4c5BIOF81
browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min.js
browser/locales/searchplugins/diribg.xml
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_add_edited_input_to_history.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
dom/media/test/seek.yuv
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/README.md
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/nss_ctypes.py
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/icon-128.png
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/index.html
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/manifest.webapp
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/icon-128.png
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/index.html
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/manifest.webapp
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html
security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp
security/manager/ssl/tests/unit/test_signed_apps/privileged-app-test-1.0.zip
security/manager/ssl/tests/unit/test_signed_apps/test-privileged-app-test-1.0.zip
security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der
security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zip
security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zip
security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zip
toolkit/components/printingui/win/nsPrintDialogUtil.cpp
toolkit/components/printingui/win/nsPrintDialogUtil.h
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,56 +1,61 @@
 # Always ignore node_modules.
 **/node_modules/**/*.*
 
+# Always ignore crashtests - specially crafted files that originally caused a
+# crash.
+**/crashtests/**
+
 # Exclude expected objdirs.
 obj*/**
 
 # We ignore all these directories by default, until we get them enabled.
 # If you are enabling a directory, please add directory specific exclusions
 # below.
 chrome/**
 docshell/**
 editor/**
-embedding/**
 extensions/cookie/**
 extensions/spellcheck/**
 extensions/universalchardet/**
 gfx/**
 image/**
 intl/**
 layout/**
 media/**
 memory/**
 modules/**
 netwerk/**
 parser/**
-python/**
 rdf/**
-servo/**
 tools/update-packaging/**
 uriloader/**
-view/**
 widget/**
 
 # We currently have no js files in these directories, so we ignore them by
 # default to aid ESLint's performance.
 build/**
 config/**
 db/**
+embedding/**
 gradle/**
 hal/**
 mfbt/**
 mozglue/**
 nsprpub/**
 other-licenses/**
 probes/**
 startupcache/**
 xpfe/**
 
+# These directories only contain crashtests, but we still skip the whole
+# directory to aid performance.
+view/**
+
 # browser/ exclusions
 browser/app/**
 browser/branding/**/firefox-branding.js
 # Gzipped test file.
 browser/base/content/test/general/gZipOfflineChild.html
 browser/base/content/test/urlbar/file_blank_but_not_blank.html
 # New tab is likely to be replaced soon.
 browser/base/content/newtab/**
@@ -277,28 +282,34 @@ mobile/android/chrome/content/about.js
 # Not much JS to lint and non-standard at that
 mobile/android/installer/
 mobile/android/locales/
 
 # Non-standard `(catch ex if ...)`
 mobile/android/chrome/content/browser.js
 mobile/android/components/Snippets.js
 
+# Only contains non-standard test files.
+python/**
+
 # security/ exclusions (pref files).
 security/manager/ssl/security-prefs.js
 
 # NSS / taskcluster only.
 security/nss/**
 
 # services/ exclusions
 
 # Uses `#filter substitution`
 services/sync/modules/constants.js
 services/sync/services-sync.js
 
+# Servo is imported.
+servo/**
+
 # Remote protocol exclusions
 testing/marionette/test_*.js
 testing/marionette/atom.js
 testing/marionette/legacyaction.js
 testing/marionette/client
 testing/marionette/doc
 testing/marionette/harness
 
--- a/accessible/tests/mochitest/hittest/a11y.ini
+++ b/accessible/tests/mochitest/hittest/a11y.ini
@@ -4,12 +4,11 @@ support-files = zoom_tree.xul
   !/accessible/tests/mochitest/letters.gif
 
 [test_browser.html]
 [test_canvas_hitregion.html]
 skip-if = (os == "android" || appname == "b2g")
 [test_general.html]
 [test_menu.xul]
 [test_shadowroot.html]
-skip-if = stylo # Stylo doesn't support shadow DOM yet, bug 1293844
 [test_zoom.html]
 [test_zoom_text.html]
 [test_zoom_tree.xul]
--- a/browser/base/content/browser-pageActions.js
+++ b/browser/base/content/browser-pageActions.js
@@ -268,18 +268,22 @@ var BrowserPageActions = {
    * Returns the node in the urlbar to which popups for the given action should
    * be anchored.  If the action is null, a sensible anchor is returned.
    *
    * @param  action (PageActions.Action, optional)
    *         The action you want to anchor.
    * @return (DOM node, nonnull) The node to which the action should be
    *         anchored.
    */
-  panelAnchorNodeForAction(action) {
+  panelAnchorNodeForAction(action, event) {
     // Try each of the following nodes in order, using the first that's visible.
+    if (event && event.target.closest("panel")) {
+      return this.mainButtonNode;
+    }
+
     let potentialAnchorNodeIDs = [
       action && action.anchorIDOverride,
       action && this._urlbarButtonNodeIDForActionID(action.id),
       this.mainButtonNode.id,
       "identity-icon",
     ];
     let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindowUtils);
@@ -748,17 +752,17 @@ var BrowserPageActionFeedback = {
     delete this.feedbackLabel;
     return this.feedbackLabel = document.getElementById("pageActionFeedbackMessage");
   },
 
   show(action, event) {
     this.feedbackLabel.textContent = this.panelNode.getAttribute(action.id + "Feedback");
     this.panelNode.hidden = false;
 
-    let anchor = BrowserPageActions.panelAnchorNodeForAction(action);
+    let anchor = BrowserPageActions.panelAnchorNodeForAction(action, event);
     this.panelNode.openPopup(anchor, {
       position: "bottomcenter topright",
       triggerEvent: event,
     });
 
     this.panelNode.addEventListener("popupshown", () => {
       this.feedbackAnimationBox.setAttribute("animate", "true");
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -134,16 +134,19 @@ if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
                                      "@mozilla.org/xre/app-info;1",
                                      "nsICrashReporter");
 }
 
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle("chrome://browser/locale/browser.properties");
 });
+XPCOMUtils.defineLazyGetter(this, "gTabBrowserBundle", function() {
+  return Services.strings.createBundle("chrome://browser/locale/tabbrowser.properties");
+});
 
 XPCOMUtils.defineLazyGetter(this, "gCustomizeMode", function() {
   let scope = {};
   Cu.import("resource:///modules/CustomizeMode.jsm", scope);
   return new scope.CustomizeMode(window);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gPrefService", function() {
@@ -4071,18 +4074,17 @@ function FillHistoryMenu(aParent) {
       item.setAttribute("index", j);
 
       // Cache this so that gotoHistoryIndex doesn't need the original index
       item.setAttribute("historyindex", j - index);
 
       if (j != index) {
         // Use list-style-image rather than the image attribute in order to
         // allow CSS to override this.
-        item.style.listStyleImage =
-          "url(" + PlacesUtils.urlWithSizeRef(window, "page-icon:" + uri, 16) + ")";
+        item.style.listStyleImage = `url(page-icon:${uri})`;
       }
 
       if (j < index) {
         item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
         item.setAttribute("tooltiptext", tooltipBack);
       } else if (j == index) {
         item.setAttribute("type", "radio");
         item.setAttribute("checked", "true");
@@ -8948,18 +8950,18 @@ TabModalPromptBox.prototype = {
     try {
       hostForAllowFocusCheckbox = principalToAllowFocusFor.URI.host;
     } catch (ex) { /* Ignore exceptions for host-less URIs */ }
     if (hostForAllowFocusCheckbox) {
       let allowFocusRow = document.createElementNS(XUL_NS, "row");
       allowFocusCheckbox = document.createElementNS(XUL_NS, "checkbox");
       let spacer = document.createElementNS(XUL_NS, "spacer");
       allowFocusRow.appendChild(spacer);
-      let label = gBrowser.mStringBundle.getFormattedString("tabs.allowTabFocusByPromptForSite",
-                                                            [hostForAllowFocusCheckbox]);
+      let label = gTabBrowserBundle.formatStringFromName("tabs.allowTabFocusByPromptForSite",
+                                                      [hostForAllowFocusCheckbox], 1);
       allowFocusCheckbox.setAttribute("label", label);
       allowFocusRow.appendChild(allowFocusCheckbox);
       newPrompt.appendChild(allowFocusRow);
     }
 
     let tab = gBrowser.getTabForBrowser(browser);
     let closeCB = this._promptCloseCallback.bind(null, onCloseCallback, principalToAllowFocusFor,
                                                  allowFocusCheckbox);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -12,17 +12,16 @@
           xmlns:xbl="http://www.mozilla.org/xbl">
 
   <binding id="tabbrowser">
     <resources>
       <stylesheet src="chrome://browser/content/tabbrowser.css"/>
     </resources>
 
     <content>
-      <xul:stringbundle anonid="tbstringbundle" src="chrome://browser/locale/tabbrowser.properties"/>
       <xul:tabbox anonid="tabbox" class="tabbrowser-tabbox"
                   flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown,tabcontainer"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1" notificationside="top">
             <xul:hbox flex="1" class="browserSidebarContainer">
               <xul:vbox flex="1" class="browserContainer">
                 <xul:stack flex="1" class="browserStack" anonid="browserStack">
@@ -71,19 +70,16 @@
                    .getService(Components.interfaces.mozIPlacesAutoComplete);
       </field>
       <field name="mTabBox" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "tabbox");
       </field>
       <field name="mPanelContainer" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
       </field>
-      <field name="mStringBundle">
-        document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
-      </field>
       <field name="mCurrentTab">
         null
       </field>
       <field name="_lastRelatedTabMap">
         new WeakMap();
       </field>
       <field name="mCurrentBrowser">
         null
@@ -1627,17 +1623,17 @@
                 try {
                   var characterSet = browser.characterSet;
                   const textToSubURI = Components.classes["@mozilla.org/intl/texttosuburi;1"]
                                                  .getService(Components.interfaces.nsITextToSubURI);
                   title = textToSubURI.unEscapeNonAsciiURI(characterSet, title);
                 } catch (ex) { /* Do nothing. */ }
               } else {
                 // Still no title? Fall back to our untitled string.
-                title = this.mStringBundle.getString("tabs.emptyTabTitle");
+                title = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
               }
             }
 
             return this._setTabLabel(aTab, title, { isContentTitle });
           ]]>
         </body>
       </method>
 
@@ -2640,17 +2636,17 @@
               lazyBrowserURI = aURIObject;
               aURI = "about:blank";
             }
 
             var uriIsAboutBlank = aURI == "about:blank";
 
             if (!aNoInitialLabel) {
               if (isBlankPageURL(aURI)) {
-                t.setAttribute("label", this.mStringBundle.getString("tabs.emptyTabTitle"));
+                t.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle"));
               } else {
                 // Set URL as label so that the tab isn't empty initially.
                 this.setInitialTabTitle(t, aURI, { beforeTabOpen: true });
               }
             }
 
             if (aIsPrerendered) {
               t.setAttribute("hidden", "true");
@@ -2908,37 +2904,36 @@
           var shouldPrompt = Services.prefs.getBoolPref(pref);
           if (!shouldPrompt)
             return true;
 
           var ps = Services.prompt;
 
           // default to true: if it were false, we wouldn't get this far
           var warnOnClose = { value: true };
-          var bundle = this.mStringBundle;
 
           // focus the window before prompting.
           // this will raise any minimized window, which will
           // make it obvious which window the prompt is for and will
           // solve the problem of windows "obscuring" the prompt.
           // see bug #350299 for more details
           window.focus();
           var warningMessage =
-            PluralForm.get(tabsToClose, bundle.getString("tabs.closeWarningMultiple"))
+            PluralForm.get(tabsToClose, gTabBrowserBundle.GetStringFromName("tabs.closeWarningMultiple"))
                       .replace("#1", tabsToClose);
           var buttonPressed =
             ps.confirmEx(window,
-                         bundle.getString("tabs.closeWarningTitle"),
+                         gTabBrowserBundle.GetStringFromName("tabs.closeWarningTitle"),
                          warningMessage,
                          (ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0)
                          + (ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1),
-                         bundle.getString("tabs.closeButtonMultiple"),
+                         gTabBrowserBundle.GetStringFromName("tabs.closeButtonMultiple"),
                          null, null,
                          aCloseTabs == this.closingTabsEnum.ALL ?
-                           bundle.getString("tabs.closeWarningPromptMe") : null,
+                           gTabBrowserBundle.GetStringFromName("tabs.closeWarningPromptMe") : null,
                          warnOnClose);
           var reallyClose = (buttonPressed == 0);
 
           // don't set the pref unless they press OK and it's false
           if (aCloseTabs == this.closingTabsEnum.ALL && reallyClose && !warnOnClose.value)
             Services.prefs.setBoolPref(pref, false);
 
           return reallyClose;
@@ -5511,55 +5506,55 @@
           if (tab.localName != "tab") {
             event.preventDefault();
             return;
           }
 
           let stringWithShortcut = (stringId, keyElemId) => {
             let keyElem = document.getElementById(keyElemId);
             let shortcut = ShortcutUtils.prettifyShortcut(keyElem);
-            return this.mStringBundle.getFormattedString(stringId, [shortcut]);
+            return gTabBrowserBundle.formatStringFromName(stringId, [shortcut], 1);
           };
 
           var label;
           if (tab.mOverCloseButton) {
             label = tab.selected ?
                     stringWithShortcut("tabs.closeSelectedTab.tooltip", "key_close") :
-                    this.mStringBundle.getString("tabs.closeTab.tooltip");
+                    gTabBrowserBundle.GetStringFromName("tabs.closeTab.tooltip");
           } else if (tab._overPlayingIcon) {
             let stringID;
             if (tab.selected) {
               stringID = tab.linkedBrowser.audioMuted ?
                 "tabs.unmuteAudio.tooltip" :
                 "tabs.muteAudio.tooltip";
               label = stringWithShortcut(stringID, "key_toggleMute");
             } else {
               if (tab.hasAttribute("activemedia-blocked")) {
                 stringID = "tabs.unblockAudio.tooltip";
               } else {
                 stringID = tab.linkedBrowser.audioMuted ?
                   "tabs.unmuteAudio.background.tooltip" :
                   "tabs.muteAudio.background.tooltip";
               }
 
-              label = this.mStringBundle.getString(stringID);
+              label = gTabBrowserBundle.GetStringFromName(stringID);
             }
           } else {
             label = tab._fullLabel || tab.getAttribute("label");
             if (AppConstants.E10S_TESTING_ONLY &&
                 tab.linkedBrowser &&
                 tab.linkedBrowser.isRemoteBrowser) {
               label += " - e10s";
               if (tab.linkedBrowser.frameLoader &&
                   Services.appinfo.maxWebProcessCount > 1) {
                 label += " (" + tab.linkedBrowser.frameLoader.tabParent.osPid + ")";
               }
             }
             if (tab.userContextId) {
-              label = this.mStringBundle.getFormattedString("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)]);
+              label = gTabBrowserBundle.formatStringFromName("tabs.containers.tooltip", [label, ContextualIdentityService.getUserContextLabel(tab.userContextId)], 2);
             }
           }
 
           event.target.setAttribute("label", label);
         ]]></body>
       </method>
 
       <method name="handleEvent">
@@ -6367,20 +6362,20 @@
     </content>
 
     <implementation implements="nsIDOMEventListener, nsIObserver">
       <constructor>
         <![CDATA[
           this.mTabClipWidth = Services.prefs.getIntPref("browser.tabs.tabClipWidth");
 
           let { restoreTabsButton } = this;
-          restoreTabsButton.setAttribute("label", this.tabbrowser.mStringBundle.getString("tabs.restoreLastTabs"));
+          restoreTabsButton.setAttribute("label", gTabBrowserBundle.GetStringFromName("tabs.restoreLastTabs"));
 
           var tab = this.firstChild;
-          tab.label = this.tabbrowser.mStringBundle.getString("tabs.emptyTabTitle");
+          tab.label = gTabBrowserBundle.GetStringFromName("tabs.emptyTabTitle");
           tab.setAttribute("onerror", "this.removeAttribute('image');");
 
           window.addEventListener("resize", this);
           window.addEventListener("load", this);
 
           Services.prefs.addObserver("privacy.userContext", this);
           this.observe(null, "nsPref:changed", "privacy.userContext.enabled");
 
@@ -6611,17 +6606,17 @@
       </property>
 
       <method name="_propagateVisibility">
         <body><![CDATA[
           let visible = this.visible;
 
           document.getElementById("menu_closeWindow").hidden = !visible;
           document.getElementById("menu_close").setAttribute("label",
-            this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
+            gTabBrowserBundle.GetStringFromName(visible ? "tabs.closeTab" : "tabs.close"));
 
           TabsInTitlebar.allowedBy("tabs-visible", visible);
         ]]></body>
       </method>
 
       <method name="updateVisibility">
         <body><![CDATA[
           if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -100,17 +100,16 @@ const startupPhases = {
       "resource://gre/modules/CrashSubmit.jsm",
       "resource://gre/modules/FxAccounts.jsm",
       "resource://gre/modules/FxAccountsStorage.jsm",
       "resource://gre/modules/PlacesSyncUtils.jsm",
       "resource://gre/modules/Sqlite.jsm",
     ]),
     services: new Set([
       "@mozilla.org/browser/annotation-service;1",
-      "@mozilla.org/browser/favicon-service;1",
       "@mozilla.org/browser/nav-bookmarks-service;1",
     ])
   }},
 
   // Things that are expected to be completely out of the startup path
   // and loaded lazily when used for the first time by the user should
   // be blacklisted here.
   "before becoming idle": {blacklist: {
--- a/browser/base/content/test/urlbar/browser_page_action_menu.js
+++ b/browser/base/content/test/urlbar/browser_page_action_menu.js
@@ -121,16 +121,63 @@ add_task(async function emailLink() {
     let hiddenPromise = promisePageActionPanelHidden();
     EventUtils.synthesizeMouseAtCenter(emailLinkButton, {});
     await hiddenPromise;
 
     Assert.ok(fnCalled);
   });
 });
 
+add_task(async function copyURLFromPanel() {
+  // Open an actionable page so that the main page action button appears.  (It
+  // does not appear on about:blank for example.)
+  let url = "http://example.com/";
+  await BrowserTestUtils.withNewTab(url, async () => {
+    // Open the panel and click Copy URL.
+    await promisePageActionPanelOpen();
+    Assert.ok(true, "page action panel opened");
+
+    let copyURLButton =
+      document.getElementById("pageAction-panel-copyURL");
+    let hiddenPromise = promisePageActionPanelHidden();
+    EventUtils.synthesizeMouseAtCenter(copyURLButton, {});
+    await hiddenPromise;
+
+    let feedbackPanel = document.getElementById("pageActionFeedback");
+    let feedbackShownPromise = BrowserTestUtils.waitForEvent(feedbackPanel, "popupshown");
+    await feedbackShownPromise;
+    Assert.equal(feedbackPanel.anchorNode.id, "pageActionButton", "Feedback menu should be anchored on the main Page Action button");
+    let feedbackHiddenPromise = promisePanelHidden("pageActionFeedback");
+    await feedbackHiddenPromise;
+  });
+});
+
+add_task(async function copyURLFromURLBar() {
+  // Open an actionable page so that the main page action button appears.  (It
+  // does not appear on about:blank for example.)
+  let url = "http://example.com/";
+  await BrowserTestUtils.withNewTab(url, async () => {
+    // Add action to URL bar.
+    let action = PageActions._builtInActions.find(a => a.id == "copyURL");
+    action.shownInUrlbar = true;
+    registerCleanupFunction(() => action.shownInUrlbar = false);
+
+    let copyURLButton =
+      document.getElementById("pageAction-urlbar-copyURL");
+    let feedbackShownPromise = promisePanelShown("pageActionFeedback");
+    EventUtils.synthesizeMouseAtCenter(copyURLButton, {});
+
+    await feedbackShownPromise;
+    let panel = document.getElementById("pageActionFeedback");
+    Assert.equal(panel.anchorNode.id, "pageAction-urlbar-copyURL", "Feedback menu should be anchored on the main URL bar button");
+    let feedbackHiddenPromise = promisePanelHidden("pageActionFeedback");
+    await feedbackHiddenPromise;
+  });
+});
+
 add_task(async function sendToDevice_nonSendable() {
   // Open a tab that's not sendable.  An about: page like about:home is
   // convenient.
   await BrowserTestUtils.withNewTab("about:home", async () => {
     // ... but the page actions should be hidden on about:home, including the
     // main button.  (It's not easy to load a page that's both actionable and
     // not sendable.)  So first check that that's the case, and then unhide the
     // main button so that this test can continue.
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -1,13 +1,14 @@
 [DEFAULT]
 support-files =
   head.js
   head_pageAction.js
   head_sessions.js
+  head_webNavigation.js
   profilerSymbols.sjs
   context.html
   context_frame.html
   ctxmenu-image.png
   context_tabs_onUpdated_page.html
   context_tabs_onUpdated_iframe.html
   file_clearplugindata.html
   file_find_frames.html
@@ -171,16 +172,18 @@ skip-if = os == "linux" && debug && bits
 [browser_ext_themes_validation.js]
 [browser_ext_url_overrides_newtab.js]
 [browser_ext_user_events.js]
 [browser_ext_webRequest.js]
 [browser_ext_webNavigation_frameId0.js]
 [browser_ext_webNavigation_getFrames.js]
 [browser_ext_webNavigation_onCreatedNavigationTarget.js]
 [browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js]
+[browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js]
+[browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js]
 [browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js]
 [browser_ext_webNavigation_urlbar_transitions.js]
 [browser_ext_windows.js]
 [browser_ext_windows_create.js]
 tags = fullscreen
 [browser_ext_windows_create_params.js]
 [browser_ext_windows_create_tabId.js]
 [browser_ext_windows_create_url.js]
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
@@ -1,15 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+                                    this);
 
 async function background() {
   const tabs = await browser.tabs.query({active: true, currentWindow: true});
   const sourceTabId = tabs[0].id;
 
   const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
 
   browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -29,83 +28,65 @@ async function background() {
     browser.test.sendMessage("tabsOnCreated", tab.id);
   });
 
   browser.test.sendMessage("expectedSourceTab", {
     sourceTabId, sourceTabFrames,
   });
 }
 
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
-  await openNavTarget();
-
-  const webNavMsg = await extension.awaitMessage("webNavOnCreated");
-  const createdTabId = await extension.awaitMessage("tabsOnCreated");
-  const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
-  let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
-  is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
-  is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
-  is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
-  is(webNavMsg.url, url, "Got the expected url property");
-
-  is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
-  is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
 add_task(async function test_on_created_navigation_target_from_mouse_click() {
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
 
   const extension = ExtensionTestUtils.loadExtension({
     background,
     manifest: {
       permissions: ["webNavigation"],
     },
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
   info("Open link in a new tab using Ctrl-click");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-tab-from-mouse-click",
                                                {ctrlKey: true, metaKey: true},
                                                tab.linkedBrowser);
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
       url: `${OPENED_PAGE}#new-tab-from-mouse-click`,
     },
   });
 
   info("Open link in a new window using Shift-click");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-window-from-mouse-click",
                                                {shiftKey: true},
                                                tab.linkedBrowser);
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
       url: `${OPENED_PAGE}#new-window-from-mouse-click`,
     },
   });
 
   info("Open link with target=\"_blank\" in a new tab using click");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       BrowserTestUtils.synthesizeMouseAtCenter("#test-create-new-tab-from-targetblank-click",
                                                {},
                                                tab.linkedBrowser);
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
@@ -130,17 +111,17 @@ add_task(async function test_on_created_
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
   info("Open a subframe link in a new tab using Ctrl-click");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       BrowserTestUtils.synthesizeMouseAtCenter(function() {
         // This code runs as a framescript in the child process and it returns the
         // target link in the subframe.
         return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
           .querySelector("#test-create-new-tab-from-mouse-click-subframe");
       }, {ctrlKey: true, metaKey: true}, tab.linkedBrowser);
@@ -149,17 +130,17 @@ add_task(async function test_on_created_
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
       url: `${OPENED_PAGE}#new-tab-from-mouse-click-subframe`,
     },
   });
 
   info("Open a subframe link in a new window using Shift-click");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       BrowserTestUtils.synthesizeMouseAtCenter(function() {
         // This code runs as a framescript in the child process and it returns the
         // target link in the subframe.
         return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
                       .querySelector("#test-create-new-window-from-mouse-click-subframe");
       }, {shiftKey: true}, tab.linkedBrowser);
@@ -168,17 +149,17 @@ add_task(async function test_on_created_
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
       url: `${OPENED_PAGE}#new-window-from-mouse-click-subframe`,
     },
   });
 
   info("Open a subframe link with target=\"_blank\" in a new tab using click");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       BrowserTestUtils.synthesizeMouseAtCenter(function() {
         // This code runs as a framescript in the child process and it returns the
         // target link in the subframe.
         return this.content.frames[0].document // eslint-disable-line mozilla/no-cpows-in-tests
           .querySelector("#test-create-new-tab-from-targetblank-click-subframe");
       }, {}, tab.linkedBrowser);
@@ -189,9 +170,8 @@ add_task(async function test_on_created_
       url: `${OPENED_PAGE}#new-tab-from-targetblank-click-subframe`,
     },
   });
 
   await BrowserTestUtils.removeTab(tab);
 
   await extension.unload();
 });
-
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
@@ -1,15 +1,22 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+                                    this);
+
+async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
+  const contentAreaContextMenu = await openContextMenu(pageElementSelector);
+  const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
+  is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
+  item[0].click();
+  await closeContextMenu();
+}
 
 async function background() {
   const tabs = await browser.tabs.query({active: true, currentWindow: true});
   const sourceTabId = tabs[0].id;
 
   const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
 
   browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -29,76 +36,50 @@ async function background() {
     browser.test.sendMessage("tabsOnCreated", tab.id);
   });
 
   browser.test.sendMessage("expectedSourceTab", {
     sourceTabId, sourceTabFrames,
   });
 }
 
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
-  await openNavTarget();
-
-  const webNavMsg = await extension.awaitMessage("webNavOnCreated");
-  const createdTabId = await extension.awaitMessage("tabsOnCreated");
-  const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
-  let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
-  is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
-  is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
-  is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
-  is(webNavMsg.url, url, "Got the expected url property");
-
-  is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
-  is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-async function clickContextMenuItem({pageElementSelector, contextMenuItemLabel}) {
-  const contentAreaContextMenu = await openContextMenu(pageElementSelector);
-  const item = contentAreaContextMenu.getElementsByAttribute("label", contextMenuItemLabel);
-  is(item.length, 1, `found contextMenu item for "${contextMenuItemLabel}"`);
-  item[0].click();
-  await closeContextMenu();
-}
-
 add_task(async function test_on_created_navigation_target_from_context_menu() {
   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
 
   const extension = ExtensionTestUtils.loadExtension({
     background,
     manifest: {
       permissions: ["webNavigation"],
     },
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
   info("Open link in a new tab from the context menu");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     async openNavTarget() {
       await clickContextMenuItem({
         pageElementSelector: "#test-create-new-tab-from-context-menu",
         contextMenuItemLabel: "Open Link in New Tab",
       });
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
       url: `${OPENED_PAGE}#new-tab-from-context-menu`,
     },
   });
 
   info("Open link in a new window from the context menu");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     async openNavTarget() {
       await clickContextMenuItem({
         pageElementSelector: "#test-create-new-window-from-context-menu",
         contextMenuItemLabel: "Open Link in New Window",
       });
     },
     expectedWebNavProps: {
@@ -124,17 +105,17 @@ add_task(async function test_on_created_
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
   info("Open a subframe link in a new tab from the context menu");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     async openNavTarget() {
       await clickContextMenuItem({
         pageElementSelector: function() {
           // This code runs as a framescript in the child process and it returns the
           // target link in the subframe.
           return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
             .document.querySelector("#test-create-new-tab-from-context-menu-subframe");
@@ -146,17 +127,17 @@ add_task(async function test_on_created_
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
       url: `${OPENED_PAGE}#new-tab-from-context-menu-subframe`,
     },
   });
 
   info("Open a subframe link in a new window from the context menu");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     async openNavTarget() {
       await clickContextMenuItem({
         pageElementSelector: function() {
           // This code runs as a framescript in the child process and it returns the
           // target link in the subframe.
           return this.content.frames[0] // eslint-disable-line mozilla/no-cpows-in-tests
             .document.querySelector("#test-create-new-window-from-context-menu-subframe");
copy from browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
copy to browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js
@@ -1,15 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+                                    this);
 
 async function background() {
   const tabs = await browser.tabs.query({active: true, currentWindow: true});
   const sourceTabId = tabs[0].id;
 
   const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
 
   browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -35,135 +34,64 @@ async function background() {
     }
   });
 
   browser.test.sendMessage("expectedSourceTab", {
     sourceTabId, sourceTabFrames,
   });
 }
 
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
-  await openNavTarget();
-
-  const webNavMsg = await extension.awaitMessage("webNavOnCreated");
-  const createdTabId = await extension.awaitMessage("tabsOnCreated");
-  const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
-  let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
-  is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
-  is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
-  is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
-  is(webNavMsg.url, url, "Got the expected url property");
-
-  is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
-  is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-add_task(async function test_on_created_navigation_target_from_window_open() {
+add_task(async function test_window_open_in_named_win() {
   const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
 
   gBrowser.selectedTab = tab1;
 
   const extension = ExtensionTestUtils.loadExtension({
     background,
     manifest: {
       permissions: ["webNavigation", "tabs", "<all_urls>"],
     },
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
-  info("open an url in a new tab from a window.open call");
+  info("open a url in a new named window from a window.open call");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       extension.sendMessage({
         type: "execute-contentscript",
-        code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
+        code: `window.open("${OPENED_PAGE}#new-named-window-open", "TestWinName"); true;`,
       });
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
-      url: `${OPENED_PAGE}#new-tab-from-window-open`,
+      url: `${OPENED_PAGE}#new-named-window-open`,
     },
   });
 
-  info("open an url in a new window from a window.open call");
+  info("open a url in an existent named window from a window.open call");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       extension.sendMessage({
         type: "execute-contentscript",
-        code: `window.open("${OPENED_PAGE}#new-win-from-window-open", "_blank", "toolbar=0"); true;`,
+        code: `window.open("${OPENED_PAGE}#existent-named-window-open", "TestWinName"); true;`,
       });
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
-      url: `${OPENED_PAGE}#new-win-from-window-open`,
+      url: `${OPENED_PAGE}#existent-named-window-open`,
     },
   });
 
+  assertNoPendingCreatedNavigationTargetData();
+
   await BrowserTestUtils.removeTab(tab1);
 
   await extension.unload();
 });
-
-add_task(async function test_on_created_navigation_target_from_window_open_subframe() {
-  const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
-
-  gBrowser.selectedTab = tab1;
-
-  const extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["webNavigation", "tabs", "<all_urls>"],
-    },
-  });
-
-  await extension.startup();
-
-  const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
-
-  info("open an url in a new tab from subframe window.open call");
-
-  await runTestCase({
-    extension,
-    openNavTarget() {
-      extension.sendMessage({
-        type: "execute-contentscript",
-        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
-      });
-    },
-    expectedWebNavProps: {
-      sourceTabId: expectedSourceTab.sourceTabId,
-      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
-      url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
-    },
-  });
-
-  info("open an url in a new window from subframe window.open call");
-
-  await runTestCase({
-    extension,
-    openNavTarget() {
-      extension.sendMessage({
-        type: "execute-contentscript",
-        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
-      });
-    },
-    expectedWebNavProps: {
-      sourceTabId: expectedSourceTab.sourceTabId,
-      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
-      url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
-    },
-  });
-
-  await BrowserTestUtils.removeTab(tab1);
-
-  await extension.unload();
-});
copy from browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
copy to browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js
@@ -1,15 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+                                    this);
 
 async function background() {
   const tabs = await browser.tabs.query({active: true, currentWindow: true});
   const sourceTabId = tabs[0].id;
 
   const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
 
   browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -35,135 +34,130 @@ async function background() {
     }
   });
 
   browser.test.sendMessage("expectedSourceTab", {
     sourceTabId, sourceTabFrames,
   });
 }
 
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
-  await openNavTarget();
-
-  const webNavMsg = await extension.awaitMessage("webNavOnCreated");
-  const createdTabId = await extension.awaitMessage("tabsOnCreated");
-  const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
-  let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
-  is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
-  is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
-  is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
-  is(webNavMsg.url, url, "Got the expected url property");
-
-  is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
-  is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-add_task(async function test_on_created_navigation_target_from_window_open() {
+add_task(async function test_window_open_from_subframe() {
   const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
 
   gBrowser.selectedTab = tab1;
 
   const extension = ExtensionTestUtils.loadExtension({
     background,
     manifest: {
       permissions: ["webNavigation", "tabs", "<all_urls>"],
     },
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
-  info("open an url in a new tab from a window.open call");
+  info("open a url in a new tab from subframe window.open call");
+
+  await runCreatedNavigationTargetTest({
+    extension,
+    openNavTarget() {
+      extension.sendMessage({
+        type: "execute-contentscript",
+        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
+      });
+    },
+    expectedWebNavProps: {
+      sourceTabId: expectedSourceTab.sourceTabId,
+      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+      url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
+    },
+  });
+
+  info("open a url in a new window from subframe window.open call");
+
+  await runCreatedNavigationTargetTest({
+    extension,
+    openNavTarget() {
+      extension.sendMessage({
+        type: "execute-contentscript",
+        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
+      });
+    },
+    expectedWebNavProps: {
+      sourceTabId: expectedSourceTab.sourceTabId,
+      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+      url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
+    },
+  });
+
+  assertNoPendingCreatedNavigationTargetData();
+
+  await BrowserTestUtils.removeTab(tab1);
+
+  await extension.unload();
+});
 
-  await runTestCase({
+add_task(async function test_window_open_close_from_browserAction_popup() {
+  const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+
+  gBrowser.selectedTab = tab1;
+
+  function popup() {
+    window.open("", "_self").close();
+
+    browser.test.sendMessage("browserAction_popup_executed");
+  }
+
+  const extension = ExtensionTestUtils.loadExtension({
+    background,
+    manifest: {
+      browser_action: {
+        default_popup: "popup.html",
+      },
+      permissions: ["webNavigation", "tabs", "<all_urls>"],
+    },
+    files: {
+      "popup.html": `<!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="utf-8">
+          </head>
+          <body>
+            <script src="popup.js"></script>
+          </body>
+        </html>
+      `,
+      "popup.js": popup,
+    },
+  });
+
+  await extension.startup();
+
+  const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
+
+  clickBrowserAction(extension);
+
+  await extension.awaitMessage("browserAction_popup_executed");
+
+  info("open a url in a new tab from a window.open call");
+
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       extension.sendMessage({
         type: "execute-contentscript",
         code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
       });
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
       url: `${OPENED_PAGE}#new-tab-from-window-open`,
     },
   });
 
-  info("open an url in a new window from a window.open call");
-
-  await runTestCase({
-    extension,
-    openNavTarget() {
-      extension.sendMessage({
-        type: "execute-contentscript",
-        code: `window.open("${OPENED_PAGE}#new-win-from-window-open", "_blank", "toolbar=0"); true;`,
-      });
-    },
-    expectedWebNavProps: {
-      sourceTabId: expectedSourceTab.sourceTabId,
-      sourceFrameId: 0,
-      url: `${OPENED_PAGE}#new-win-from-window-open`,
-    },
-  });
+  assertNoPendingCreatedNavigationTargetData();
 
   await BrowserTestUtils.removeTab(tab1);
 
   await extension.unload();
 });
-
-add_task(async function test_on_created_navigation_target_from_window_open_subframe() {
-  const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
-
-  gBrowser.selectedTab = tab1;
-
-  const extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["webNavigation", "tabs", "<all_urls>"],
-    },
-  });
-
-  await extension.startup();
-
-  const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
-
-  info("open an url in a new tab from subframe window.open call");
-
-  await runTestCase({
-    extension,
-    openNavTarget() {
-      extension.sendMessage({
-        type: "execute-contentscript",
-        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
-      });
-    },
-    expectedWebNavProps: {
-      sourceTabId: expectedSourceTab.sourceTabId,
-      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
-      url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
-    },
-  });
-
-  info("open an url in a new window from subframe window.open call");
-
-  await runTestCase({
-    extension,
-    openNavTarget() {
-      extension.sendMessage({
-        type: "execute-contentscript",
-        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
-      });
-    },
-    expectedWebNavProps: {
-      sourceTabId: expectedSourceTab.sourceTabId,
-      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
-      url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
-    },
-  });
-
-  await BrowserTestUtils.removeTab(tab1);
-
-  await extension.unload();
-});
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
@@ -1,15 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
-const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
-const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+Services.scriptloader.loadSubScript(new URL("head_webNavigation.js", gTestPath).href,
+                                    this);
 
 async function background() {
   const tabs = await browser.tabs.query({active: true, currentWindow: true});
   const sourceTabId = tabs[0].id;
 
   const sourceTabFrames = await browser.webNavigation.getAllFrames({tabId: sourceTabId});
 
   browser.webNavigation.onCreatedNavigationTarget.addListener((msg) => {
@@ -35,135 +34,130 @@ async function background() {
     }
   });
 
   browser.test.sendMessage("expectedSourceTab", {
     sourceTabId, sourceTabFrames,
   });
 }
 
-async function runTestCase({extension, openNavTarget, expectedWebNavProps}) {
-  await openNavTarget();
-
-  const webNavMsg = await extension.awaitMessage("webNavOnCreated");
-  const createdTabId = await extension.awaitMessage("tabsOnCreated");
-  const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
-
-  let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
-
-  is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
-  is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
-  is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
-  is(webNavMsg.url, url, "Got the expected url property");
-
-  is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
-  is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
-}
-
-add_task(async function test_on_created_navigation_target_from_window_open() {
+add_task(async function test_window_open() {
   const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
 
   gBrowser.selectedTab = tab1;
 
   const extension = ExtensionTestUtils.loadExtension({
     background,
     manifest: {
       permissions: ["webNavigation", "tabs", "<all_urls>"],
     },
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
-  info("open an url in a new tab from a window.open call");
+  info("open a url in a new tab from a window.open call");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       extension.sendMessage({
         type: "execute-contentscript",
         code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
       });
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
       url: `${OPENED_PAGE}#new-tab-from-window-open`,
     },
   });
 
-  info("open an url in a new window from a window.open call");
+  info("open a url in a new window from a window.open call");
 
-  await runTestCase({
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       extension.sendMessage({
         type: "execute-contentscript",
         code: `window.open("${OPENED_PAGE}#new-win-from-window-open", "_blank", "toolbar=0"); true;`,
       });
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
       sourceFrameId: 0,
       url: `${OPENED_PAGE}#new-win-from-window-open`,
     },
   });
 
+  assertNoPendingCreatedNavigationTargetData();
+
   await BrowserTestUtils.removeTab(tab1);
 
   await extension.unload();
 });
 
-add_task(async function test_on_created_navigation_target_from_window_open_subframe() {
+add_task(async function test_window_open_close_from_browserAction_popup() {
   const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
 
   gBrowser.selectedTab = tab1;
 
+  function popup() {
+    window.open("", "_self").close();
+
+    browser.test.sendMessage("browserAction_popup_executed");
+  }
+
   const extension = ExtensionTestUtils.loadExtension({
     background,
     manifest: {
+      browser_action: {
+        default_popup: "popup.html",
+      },
       permissions: ["webNavigation", "tabs", "<all_urls>"],
     },
+    files: {
+      "popup.html": `<!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="utf-8">
+          </head>
+          <body>
+            <script src="popup.js"></script>
+          </body>
+        </html>
+      `,
+      "popup.js": popup,
+    },
   });
 
   await extension.startup();
 
   const expectedSourceTab = await extension.awaitMessage("expectedSourceTab");
 
-  info("open an url in a new tab from subframe window.open call");
+  clickBrowserAction(extension);
+
+  await extension.awaitMessage("browserAction_popup_executed");
 
-  await runTestCase({
+  info("open a url in a new tab from a window.open call");
+
+  await runCreatedNavigationTargetTest({
     extension,
     openNavTarget() {
       extension.sendMessage({
         type: "execute-contentscript",
-        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-tab-from-window-open-subframe"); true;`,
+        code: `window.open("${OPENED_PAGE}#new-tab-from-window-open"); true;`,
       });
     },
     expectedWebNavProps: {
       sourceTabId: expectedSourceTab.sourceTabId,
-      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
-      url: `${OPENED_PAGE}#new-tab-from-window-open-subframe`,
+      sourceFrameId: 0,
+      url: `${OPENED_PAGE}#new-tab-from-window-open`,
     },
   });
 
-  info("open an url in a new window from subframe window.open call");
-
-  await runTestCase({
-    extension,
-    openNavTarget() {
-      extension.sendMessage({
-        type: "execute-contentscript",
-        code: `document.querySelector('iframe').contentWindow.open("${OPENED_PAGE}#new-win-from-window-open-subframe", "_blank", "toolbar=0"); true;`,
-      });
-    },
-    expectedWebNavProps: {
-      sourceTabId: expectedSourceTab.sourceTabId,
-      sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
-      url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
-    },
-  });
+  assertNoPendingCreatedNavigationTargetData();
 
   await BrowserTestUtils.removeTab(tab1);
 
   await extension.unload();
 });
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/head_webNavigation.js
@@ -0,0 +1,39 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+/* exported BASE_URL, SOURCE_PAGE, OPENED_PAGE,
+   runCreatedNavigationTargetTest, assertNoPendingCreatedNavigationTargetData */
+
+const BASE_URL = "http://mochi.test:8888/browser/browser/components/extensions/test/browser";
+const SOURCE_PAGE = `${BASE_URL}/webNav_createdTargetSource.html`;
+const OPENED_PAGE = `${BASE_URL}/webNav_createdTarget.html`;
+
+async function runCreatedNavigationTargetTest({extension, openNavTarget, expectedWebNavProps}) {
+  await openNavTarget();
+
+  const webNavMsg = await extension.awaitMessage("webNavOnCreated");
+  const createdTabId = await extension.awaitMessage("tabsOnCreated");
+  const completedNavMsg = await extension.awaitMessage("webNavOnCompleted");
+
+  let {sourceTabId, sourceFrameId, url} = expectedWebNavProps;
+
+  is(webNavMsg.tabId, createdTabId, "Got the expected tabId property");
+  is(webNavMsg.sourceTabId, sourceTabId, "Got the expected sourceTabId property");
+  is(webNavMsg.sourceFrameId, sourceFrameId, "Got the expected sourceFrameId property");
+  is(webNavMsg.url, url, "Got the expected url property");
+
+  is(completedNavMsg.tabId, createdTabId, "Got the expected webNavigation.onCompleted tabId property");
+  is(completedNavMsg.url, url, "Got the expected webNavigation.onCompleted url property");
+}
+
+
+// Test that there are no pending createdNavigationTarget messages still tracked
+// in WebNavigation.jsm (to be called before the extension is unloaded, because
+// once the last extension which have subscribed a webNavigation event is unloaded
+// all the pending created navigation target data is completely cleared).
+function assertNoPendingCreatedNavigationTargetData() {
+  const {Manager} = Cu.import("resource://gre/modules/WebNavigation.jsm", {});
+  Assert.equal(Manager.createdNavigationTargetByOuterWindowId.size, 0,
+               "There should be no pending createdNavigationTarget messages in WebNavigation");
+}
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -957,16 +957,19 @@ BrowserGlue.prototype = {
     DirectoryLinksProvider.init();
     NewTabUtils.init();
     NewTabUtils.links.addProvider(DirectoryLinksProvider);
 
     PageActions.init();
 
     this._firstWindowTelemetry(aWindow);
     this._firstWindowLoaded();
+
+    // Set the default favicon size for UI views that use the page-icon protocol.
+    PlacesUtils.favicons.setDefaultIconURIPreferredSize(16 * aWindow.devicePixelRatio);
   },
 
   _sendMediaTelemetry() {
     let win = Services.appShell.hiddenDOMWindow;
     let v = win.document.createElementNS("http://www.w3.org/1999/xhtml", "video");
     v.reportCanPlayTelemetry();
   },
 
--- a/browser/components/resistfingerprinting/test/browser/browser.ini
+++ b/browser/components/resistfingerprinting/test/browser/browser.ini
@@ -13,17 +13,18 @@ support-files =
 [browser_netInfo.js]
 [browser_performanceAPI.js]
 [browser_roundedWindow_dialogWindow.js]
 [browser_roundedWindow_newWindow.js]
 [browser_roundedWindow_open_max_inner.js]
 [browser_roundedWindow_open_max_outer.js]
 [browser_roundedWindow_open_mid_inner.js]
 [browser_roundedWindow_open_mid_outer.js]
-[browser_roundedWindow_open_min.js]
+[browser_roundedWindow_open_min_inner.js]
+[browser_roundedWindow_open_min_outer.js]
 [browser_roundedWindow_windowSetting_max_inner.js]
 [browser_roundedWindow_windowSetting_max_outer.js]
 [browser_roundedWindow_windowSetting_mid_inner.js]
 [browser_roundedWindow_windowSetting_mid_outer.js]
 [browser_roundedWindow_windowSetting_min_inner.js]
 [browser_roundedWindow_windowSetting_min_outer.js]
 [browser_timezone.js]
 [browser_bug1369357_site_specific_zoom_level.js]
deleted file mode 100644
--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/*
- * Bug 1330882 - A test case for opening new windows through window.open() as
- *   rounded size when fingerprinting resistance is enabled. This test is for
- *   minimum values.
- */
-
-OpenTest.run([
-  {settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100},
-  {settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100}
-]);
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min_inner.js
@@ -0,0 +1,10 @@
+/*
+ * Bug 1330882 - A test case for opening new windows through window.open() as
+ *   rounded size when fingerprinting resistance is enabled. This test is for
+ *   minimum values.
+ */
+
+OpenTest.run([
+  {settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100},
+  {settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100}
+], false);
new file mode 100644
--- /dev/null
+++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_open_min_outer.js
@@ -0,0 +1,10 @@
+/*
+ * Bug 1330882 - A test case for opening new windows through window.open() as
+ *   rounded size when fingerprinting resistance is enabled. This test is for
+ *   minimum values.
+ */
+
+OpenTest.run([
+  {settingWidth: 199, settingHeight: 99, targetWidth: 200, targetHeight: 100},
+  {settingWidth: 10, settingHeight: 10, targetWidth: 200, targetHeight: 100}
+], true);
--- a/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
+++ b/browser/extensions/activity-stream/data/content/activity-stream.bundle.js
@@ -2758,18 +2758,18 @@ class PreferencesPane extends React.Pure
                 key: nestedPref.name,
                 prefName: nestedPref.name,
                 disabled: !enabled,
                 value: prefs[nestedPref.name],
                 onChange: this.handlePrefChange,
                 titleString: nestedPref.titleString,
                 labelClassName: `icon ${nestedPref.icon}` }))
             )),
-            React.createElement("hr", null),
-            React.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
+            !prefs.disableSnippets && React.createElement("hr", null),
+            !prefs.disableSnippets && React.createElement(PreferencesInput, { className: "showSnippets", prefName: "feeds.snippets",
               value: prefs["feeds.snippets"], onChange: this.handlePrefChange,
               titleString: { id: "settings_pane_snippets_header" },
               descString: { id: "settings_pane_snippets_body" } })
           ),
           React.createElement(
             "section",
             { className: "actions" },
             React.createElement(
@@ -3883,23 +3883,23 @@ function addSnippetsSubscriber(store) {
 
   let initializing = false;
 
   store.subscribe(async () => {
     const state = store.getState();
     // state.Prefs.values["feeds.snippets"]:  Should snippets be shown?
     // state.Snippets.initialized             Is the snippets data initialized?
     // snippets.initialized:                  Is SnippetsProvider currently initialised?
-    if (state.Prefs.values["feeds.snippets"] && state.Snippets.initialized && !snippets.initialized &&
+    if (state.Prefs.values["feeds.snippets"] && !state.Prefs.values.disableSnippets && state.Snippets.initialized && !snippets.initialized &&
     // Don't call init multiple times
     !initializing) {
       initializing = true;
       await snippets.init({ appData: state.Snippets });
       initializing = false;
-    } else if (state.Prefs.values["feeds.snippets"] === false && snippets.initialized) {
+    } else if ((state.Prefs.values["feeds.snippets"] === false || state.Prefs.values.disableSnippets === true) && snippets.initialized) {
       snippets.uninit();
     }
   });
 
   // These values are returned for testing purposes
   return snippets;
 }
 
@@ -3907,9 +3907,9 @@ module.exports = {
   addSnippetsSubscriber,
   SnippetsMap,
   SnippetsProvider,
   SNIPPETS_UPDATE_INTERVAL_MS
 };
 /* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(4)))
 
 /***/ })
-/******/ ]);
\ No newline at end of file
+/******/ ]);
--- a/browser/extensions/activity-stream/lib/ActivityStream.jsm
+++ b/browser/extensions/activity-stream/lib/ActivityStream.jsm
@@ -92,16 +92,20 @@ const PREFS_CONFIG = new Map([
   ["prerender", {
     title: "Use the prerendered version of activity-stream.html. This is set automatically by PrefsFeed.jsm.",
     value: true
   }],
   ["showSearch", {
     title: "Show the Search bar",
     value: true
   }],
+  ["disableSnippets", {
+    title: "Disable snippets on activity stream",
+    value: false
+  }],
   ["showTopSites", {
     title: "Show the Top Sites section",
     value: true
   }],
   ["collapseTopSites", {
     title: "Collapse the Top Sites section",
     value: false
   }],
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -346,21 +346,58 @@ FormAutofillHandler.prototype = {
         }
         // Delete the field so the phishing hint won't treat it as a "also fill"
         // field.
         delete profile[fieldName];
       }
     }
   },
 
+  _creditCardExpDateTransformer(profile) {
+    if (!profile["cc-exp"]) {
+      return;
+    }
+
+    let detail = this.getFieldDetailByName("cc-exp");
+    if (!detail) {
+      return;
+    }
+
+    let element = detail.elementWeakRef.get();
+    if (element.tagName != "INPUT" || !element.placeholder) {
+      return;
+    }
+
+    let result,
+      ccExpMonth = profile["cc-exp-month"],
+      ccExpYear = profile["cc-exp-year"],
+      placeholder = element.placeholder;
+
+    result = /(?:[^m]|\b)(m{1,2})\s*([-/\\]*)\s*(y{2,4})(?!y)/i.exec(placeholder);
+    if (result) {
+      profile["cc-exp"] = String(ccExpMonth).padStart(result[1].length, "0") +
+                          result[2] +
+                          String(ccExpYear).substr(-1 * result[3].length);
+      return;
+    }
+
+    result = /(?:[^y]|\b)(y{2,4})\s*([-/\\]*)\s*(m{1,2})(?!m)/i.exec(placeholder);
+    if (result) {
+      profile["cc-exp"] = String(ccExpYear).substr(-1 * result[1].length) +
+                          result[2] +
+                          String(ccExpMonth).padStart(result[3].length, "0");
+    }
+  },
+
   getAdaptedProfiles(originalProfiles) {
     for (let profile of originalProfiles) {
       this._addressTransformer(profile);
       this._telTransformer(profile);
       this._matchSelectOptions(profile);
+      this._creditCardExpDateTransformer(profile);
     }
     return originalProfiles;
   },
 
   /**
    * Processes form fields that can be autofilled, and populates them with the
    * profile provided by backend.
    *
--- a/browser/extensions/formautofill/content/FormAutofillFrameScript.js
+++ b/browser/extensions/formautofill/content/FormAutofillFrameScript.js
@@ -31,16 +31,19 @@ var FormAutofillFrameScript = {
       return;
     }
     this._hasPendingTask = true;
 
     setTimeout(() => {
       FormAutofillContent.identifyAutofillFields(this._nextHandleElement);
       this._hasPendingTask = false;
       this._nextHandleElement = null;
+      // This is for testing purpose only which sends a message to indicate that the
+      // form has been identified, and ready to open popup.
+      sendAsyncMessage("FormAutofill:FieldsIdentified");
     });
   },
 
   init() {
     addEventListener("focusin", this);
     addMessageListener("FormAutofill:PreviewProfile", this);
     addMessageListener("FormAutoComplete:PopupClosed", this);
     addMessageListener("FormAutoComplete:PopupOpened", this);
--- a/browser/extensions/formautofill/test/browser/head.js
+++ b/browser/extensions/formautofill/test/browser/head.js
@@ -93,16 +93,54 @@ function getDisplayedPopupItems(browser,
 
   return [...listItemElems].filter(item => item.getAttribute("collapsed") != "true");
 }
 
 async function sleep(ms = 500) {
   await new Promise(resolve => setTimeout(resolve, ms));
 }
 
+async function focusAndWaitForFieldsIdentified(browser, selector) {
+  /* eslint no-shadow: ["error", { "allow": ["selector", "previouslyFocused", "previouslyIdentified"] }] */
+  const {previouslyFocused, previouslyIdentified} = await ContentTask.spawn(browser, {selector}, async function({selector}) {
+    Components.utils.import("resource://gre/modules/FormLikeFactory.jsm");
+    const input = content.document.querySelector(selector);
+    const rootElement = FormLikeFactory.findRootForField(input);
+    const previouslyFocused = content.document.activeElement == input;
+    const previouslyIdentified = rootElement.hasAttribute("test-formautofill-identified");
+
+    input.focus();
+
+    return {previouslyFocused, previouslyIdentified};
+  });
+
+  if (previouslyIdentified) {
+    return;
+  }
+
+  // Once the input is previously focused, no more FormAutofill:FieldsIdentified will be
+  // sent as the message goes along with focus event.
+  if (!previouslyFocused) {
+    await new Promise(resolve => {
+      Services.mm.addMessageListener("FormAutofill:FieldsIdentified", function onIdentified() {
+        Services.mm.removeMessageListener("FormAutofill:FieldsIdentified", onIdentified);
+        resolve();
+      });
+    });
+  }
+  // Wait 500ms to ensure that "markAsAutofillField" is completely finished.
+  await sleep();
+  await ContentTask.spawn(browser, {}, async function() {
+    Components.utils.import("resource://gre/modules/FormLikeFactory.jsm");
+    FormLikeFactory
+      .findRootForField(content.document.activeElement)
+      .setAttribute("test-formautofill-identified", "true");
+  });
+}
+
 async function expectPopupOpen(browser) {
   const {autoCompletePopup} = browser;
   const listItemElems = getDisplayedPopupItems(browser);
 
   await BrowserTestUtils.waitForCondition(() => autoCompletePopup.popupOpen,
                                          "popup should be open");
   await BrowserTestUtils.waitForCondition(() => {
     return [...listItemElems].every(item => {
@@ -111,33 +149,17 @@ async function expectPopupOpen(browser) 
              item.getAttribute("originaltype") == "autofill-footer") &&
              item.hasAttribute("formautofillattached");
     });
   }, "The popup should be a form autofill one");
 }
 
 async function openPopupOn(browser, selector) {
   await SimpleTest.promiseFocus(browser);
-  /* eslint no-shadow: ["error", { "allow": ["selector"] }] */
-  const identified = await ContentTask.spawn(browser, {selector}, async function({selector}) {
-    const input = content.document.querySelector(selector);
-    const forms = content.document.getElementsByTagName("form");
-    const rootElement = [...forms].find(form => form.contains(input)) || content.document.body;
-
-    input.focus();
-    if (rootElement.hasAttribute("test-formautofill-identified")) {
-      return true;
-    }
-    rootElement.setAttribute("test-formautofill-identified", "true");
-    return false;
-  });
-  // Wait 2 seconds for identifyAutofillFields if the form hasn't been identified yet.
-  if (!identified) {
-    await sleep(2000);
-  }
+  await focusAndWaitForFieldsIdentified(browser, selector);
   await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
   await expectPopupOpen(browser);
 }
 
 async function expectPopupClose(browser) {
   await BrowserTestUtils.waitForCondition(() => !browser.autoCompletePopup.popupOpen,
     "popup should have closed");
 }
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -9,28 +9,48 @@ let expectingPopup = null;
 
 const {FormAutofillUtils} = SpecialPowers.Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 async function sleep(ms = 500, reason = "Intentionally wait for UI ready") {
   SimpleTest.requestFlakyTimeout(reason);
   await new Promise(resolve => setTimeout(resolve, ms));
 }
 
-async function setInput(selector, value) {
-  let input = document.querySelector("input" + selector);
-  input.value = value;
+async function focusAndWaitForFieldsIdentified(input) {
+  const rootElement = input.form || input.ownerDocument.documentElement;
+  const previouslyFocused = input != document.activeElement;
+
   input.focus();
 
-  // "identifyAutofillFields" is invoked asynchronously in "focusin" event. We
-  // should make sure fields are ready for popup before doing tests.
-  //
-  // TODO: "sleep" is used here temporarily because there's no event to
-  //       notify us of the state of "identifyAutofillFields" for now. We should
-  //       figure out a better way after the heuristics land.
-  await sleep(500, "Guarantee asynchronous identifyAutofillFields is invoked");
+  if (rootElement.hasAttribute("test-formautofill-identified")) {
+    return;
+  }
+  if (!previouslyFocused) {
+    await new Promise(resolve => {
+      formFillChromeScript.addMessageListener("FormAutofillTest:FieldsIdentified", function onIdentified() {
+        formFillChromeScript.removeMessageListener("FormAutofillTest:FieldsIdentified", onIdentified);
+        resolve();
+      });
+    });
+  }
+  // In order to ensure that "markAsAutofillField" is fully executed, a short period
+  // of timeout is still required.
+  await sleep(300, "Guarantee asynchronous identifyAutofillFields is invoked");
+  rootElement.setAttribute("test-formautofill-identified", "true");
+}
+
+async function setInput(selector, value, userInput = false) {
+  const input = document.querySelector("input" + selector);
+  if (userInput) {
+    SpecialPowers.wrap(input).setUserInput(value);
+  } else {
+    input.value = value;
+  }
+  await focusAndWaitForFieldsIdentified(input);
+
   return input;
 }
 
 function clickOnElement(selector) {
   let element = document.querySelector(selector);
 
   if (!element) {
     throw new Error("Can not find the element");
--- a/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_parent_utils.js
@@ -164,16 +164,20 @@ var ParentUtils = {
   observe(subject, topic, data) {
     assert.ok(topic === "formautofill-storage-changed");
     sendAsyncMessage("formautofill-storage-changed", {subject: null, topic, data});
   },
 };
 
 Services.obs.addObserver(ParentUtils, "formautofill-storage-changed");
 
+Services.mm.addMessageListener("FormAutofill:FieldsIdentified", () => {
+  sendAsyncMessage("FormAutofillTest:FieldsIdentified");
+});
+
 addMessageListener("FormAutofillTest:AddAddress", (msg) => {
   ParentUtils.operateAddress("add", msg, "FormAutofillTest:AddressAdded");
 });
 
 addMessageListener("FormAutofillTest:RemoveAddress", (msg) => {
   ParentUtils.operateAddress("remove", msg, "FormAutofillTest:AddressRemoved");
 });
 
--- a/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
+++ b/browser/extensions/formautofill/test/unit/test_getAdaptedProfiles.js
@@ -729,16 +729,90 @@ const TESTCASES = [
       "cc-exp-month": 1,
     })],
     expectedResult: [{
       "guid": "123",
       "cc-exp-month": 1,
     }],
     expectedOptionElements: [],
   },
+  {
+    description: "Use placeholder to adjust cc-exp format [mm/yy].",
+    document: `<form><input placeholder="mm/yy" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+      "cc-exp": "01/25",
+    })],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [mm / yy].",
+    document: `<form><input placeholder="mm / yy" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+      "cc-exp": "01/25",
+    })],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [MM / YY].",
+    document: `<form><input placeholder="MM / YY" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+      "cc-exp": "01/25",
+    })],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [mm / yyyy].",
+    document: `<form><input placeholder="mm / yyyy" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+      "cc-exp": "01/2025",
+    })],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [mm - yyyy].",
+    document: `<form><input placeholder="mm - yyyy" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+      "cc-exp": "01-2025",
+    })],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [yyyy-mm].",
+    document: `<form><input placeholder="yyyy-mm" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+      "cc-exp": "2025-01",
+    })],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [yyy-mm].",
+    document: `<form><input placeholder="yyy-mm" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD, {
+      "cc-exp": "025-01",
+    })],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [mmm yyyy].",
+    document: `<form><input placeholder="mmm yyyy" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [mm foo yyyy].",
+    document: `<form><input placeholder="mm foo yyyy" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+  },
+  {
+    description: "Use placeholder to adjust cc-exp format [mm - - yyyy].",
+    document: `<form><input placeholder="mm - - yyyy" autocomplete="cc-exp"></form>`,
+    profileData: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+    expectedResult: [Object.assign({}, DEFAULT_CREDITCARD_RECORD)],
+  },
 ];
 
 for (let testcase of TESTCASES) {
   add_task(async function() {
     do_print("Starting testcase: " + testcase.description);
 
     let doc = MockDocument.createTestDocument("http://localhost:8080/test/",
                                               testcase.document);
--- a/browser/locales/search/list.json
+++ b/browser/locales/search/list.json
@@ -1,12 +1,12 @@
 {
   "default": {
     "visibleDefaultEngines": [
-      "google", "yahoo", "amazondotcom", "bing", "ddg", "twitter", "wikipedia"
+      "google", "yahoo", "amazondotcom", "bing", "ddg", "ebay", "twitter", "wikipedia"
     ]
   },
   "regionOverrides": {
     "US": {
       "google": "google-nocodes"
     },
     "CA": {
       "google": "google-nocodes",
@@ -129,17 +129,17 @@
         "visibleDefaultEngines": [
           "yandex-by", "google", "ddg", "wikipedia-be", "wikipedia-be-tarask"
         ]
       }
     },
     "bg": {
       "default": {
         "visibleDefaultEngines": [
-          "google", "diribg", "amazondotcom", "ddg", "portalbgdict", "wikipedia-bg"
+          "google", "amazondotcom", "ddg", "portalbgdict", "wikipedia-bg"
         ]
       }
     },
     "bn-BD": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo", "bing", "ddg", "wikipedia-bn"
         ]
deleted file mode 100644
--- a/browser/locales/searchplugins/diribg.xml
+++ /dev/null
@@ -1,17 +0,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/. -->
-
-<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
-<ShortName>Дири.бг</ShortName>
-<Description>Търсачка Дири на dir.bg</Description>
-<InputEncoding>UTF-8</InputEncoding>
-<Image width="16" height="16">data:image/x-icon;base64,AAABAAEAEBAAAAEACABoBQAAFgAAACgAAAAQAAAAIAAAAAEACAAAAAAAQAUAAAAAAAAAAAAAAAEAAAABAAD///8A7NLCAM+OZgDFd0cAyoNXAOK8pACySgoA+/TxANCPZwC0TAsAwm45AOzTwwDPiFkAuVENAMJmKgDpyLMAynxJAO7UwwDnwKYAvlYPAMp1OwD79PAA0opZAO/VxADXjlwAxFsRAPDWwwD46uEA8NbEAMlfEwDWh00A8tfFAM5kFgDRbiUA2opQAPPZxQDZfDUA02kYAPns4gDWciYA9NrGAOy3jQDZbhoA56VwAO6/mgD228YA/fbxAOB8KgDecxwA7LCAAPHCnAD33McA+ubVAOWALADjeB4A6ppWAOeJOgD43scA/O7jAO+scgDleh8A7aNkAOybVwD53sgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIDAwQFAAMGAQAAAAAHCAkJCQkJCQoKCQsAAAAADA0ODwAADxANDQ0RAAAAEhMUFQAAAAAAFhMTFwAAABgZGgAAAAAAABsZGRwAAAAdHQAAAAAAAAAAHh0fAAAAICEAAAAAAAAAACIgIwAAACQlJgAAAAAAAAAnJSgAAAApKisAAAAAAAAsKiotAAAALi8wMS4AAAAyMDAwMwAAAAA0NTY1Nzc4NjU1NjkAAAAAADo7PDw8PD00Pjw/AAAAAAAAAAA5OQAAAD48PwAAAAAAAAAAAAAAAAA+PD8AAAAAAAAAAAAAAAAAPjw/AAAAAAAAAAAAAAAAAD48PwAAAAAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8AAP//AAD//wAA//8=</Image>
-<Url type="text/html" method="GET" template="http://www.diri.bg/search.php" resultdomain="diri.bg">
-  <Param name="came" value="s" />
-  <Param name="u" value="1" />
-  <Param name="browser" value="ff" />
-  <Param name="textfield" value="{searchTerms}" />
-</Url>
-<SearchForm>http://diri.bg/</SearchForm> 
-</SearchPlugin>
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -538,23 +538,31 @@ tabbrowser {
 .tabbrowser-tab[image] > .tab-stack > .tab-content[attention]:not([pinned]):not([selected="true"]):-moz-locale-dir(rtl) {
   background-position-x: right 11px;
 }
 
 .tab-label[attention]:not([selected="true"]) {
   font-weight: bold;
 }
 
-/* Tab separators */
+/* Drag space */
 
 .titlebar-placeholder[type="pre-tabs"],
 .titlebar-placeholder[type="post-tabs"] {
   width: 40px;
 }
 
+@media (max-width: 500px) {
+  .titlebar-placeholder[type="post-tabs"] {
+    display: none;
+  }
+}
+
+/* Tab separators */
+
 .titlebar-placeholder[type="pre-tabs"] {
   border-inline-end: 1px solid;
   opacity: 0.2;
 }
 
 .tabbrowser-tab::after,
 .tabbrowser-tab::before {
   border-left: 1px solid;
--- a/browser/themes/windows/compacttheme.css
+++ b/browser/themes/windows/compacttheme.css
@@ -41,16 +41,26 @@
     #TabsToolbar {
       color: hsl(240,9%,98%);
     }
 
     /* Keep showing the correct color inside the tabs. */
     .tabbrowser-tab {
       color: var(--chrome-color) !important;
     }
+
+    /* Because we're forcing the tabs toolbar to be [brighttext] to
+     * get white toolbar button icons, we need to manually set the
+     * correct color for the tab hover state for the light theme. */
+    .tabbrowser-tab:hover > .tab-stack > .tab-background:not([selected=true]):-moz-lwtheme-darktext {
+      background-color: rgba(0,0,0,.1) !important;
+    }
+    .tabbrowser-tab:hover > .tab-stack > .tab-background > .tab-line:not([selected=true]):-moz-lwtheme-darktext {
+      background-color: rgba(0,0,0,.2) !important;
+    }
   }
 }
 
 @media (-moz-windows-glass) {
   /* Set to full fill-opacity to improve visibility of toolbar buttons on aero glass. */
   #TabsToolbar {
     --toolbarbutton-icon-fill-opacity: 1;
   }
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -914,17 +914,19 @@ rust_unlock_unstable =
 ifdef MOZ_RUST_SIMD
 rust_unlock_unstable += RUSTC_BOOTSTRAP=1
 endif
 
 ifdef MOZ_USING_SCCACHE
 sccache_wrap := RUSTC_WRAPPER='$(CCACHE)'
 endif
 
+ifdef MOZ_DEBUG_SYMBOLS
 default_rustflags += -C debuginfo=2
+endif
 
 # We use the + prefix to pass down the jobserver fds to cargo, but we
 # don't use the prefix when make -n is used, so that cargo doesn't run
 # in that case)
 define RUN_CARGO
 $(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env $(environment_cleaner) $(rust_unlock_unstable) $(rustflags_override) $(sccache_wrap) \
 	CARGO_TARGET_DIR=$(CARGO_TARGET_DIR) \
 	RUSTC=$(RUSTC) \
--- a/devtools/client/inspector/rules/test/browser_rules_original-source-link.js
+++ b/devtools/client/inspector/rules/test/browser_rules_original-source-link.js
@@ -73,11 +73,11 @@ function editorSelected(editor) {
   is(line, 3, "cursor is at correct line number in original source");
 }
 
 function verifyLinkText(text, view) {
   info("Verifying that the rule-view stylesheet link is " + text);
   let label = getRuleViewLinkByIndex(view, 1)
     .querySelector(".ruleview-rule-source-label");
   return waitForSuccess(function* () {
-    return label.textContent == text;
+    return label.textContent == text && label.getAttribute("title") === URL_ROOT + text;
   }, "Link text changed to display correct location: " + text);
 }
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -304,27 +304,30 @@ RuleEditor.prototype = {
     }
 
     this._currentLocation = {
       url,
       line,
       column
     };
 
-    let title = CssLogic.shortSource({href: displayURL});
+    let sourceTextContent = CssLogic.shortSource({href: displayURL});
+    let title = displayURL ? displayURL : sourceTextContent;
     if (line > 0) {
+      sourceTextContent += ":" + line;
       title += ":" + line;
     }
     if (this.rule.mediaText) {
+      sourceTextContent += " @" + this.rule.mediaText;
       title += " @" + this.rule.mediaText;
     }
 
     let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
     sourceLabel.setAttribute("title", title);
-    sourceLabel.textContent = title;
+    sourceLabel.textContent = sourceTextContent;
   },
 
   updateSourceLink: function () {
     if (this.rule.isSystem) {
       let sourceLabel = this.element.querySelector(".ruleview-rule-source-label");
       let title = this.rule.title;
       let sourceHref = (this.rule.sheet && this.rule.sheet.href) ?
           this.rule.sheet.href : title;
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -9,17 +9,17 @@ const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("../actions/index");
-const { getFormDataSections } = require("../utils/request-utils");
+const { updateFormDataSections } = require("../utils/request-utils");
 const { getSelectedRequest } = require("../selectors/index");
 
 // Components
 const SplitBox = createFactory(require("devtools/client/shared/components/splitter/SplitBox"));
 const NetworkDetailsPanel = createFactory(require("./NetworkDetailsPanel"));
 const RequestList = createFactory(require("./RequestList"));
 const Toolbar = createFactory(require("./Toolbar"));
 const { div } = DOM;
@@ -49,42 +49,17 @@ const MonitorPanel = createClass({
     };
   },
 
   componentDidMount() {
     MediaQueryList.addListener(this.onLayoutChange);
   },
 
   componentWillReceiveProps(nextProps) {
-    let {
-      request = {},
-      updateRequest,
-    } = nextProps;
-    let {
-      formDataSections,
-      requestHeaders,
-      requestHeadersFromUploadStream,
-      requestPostData,
-    } = request;
-
-    if (!formDataSections && requestHeaders &&
-        requestHeadersFromUploadStream && requestPostData) {
-      getFormDataSections(
-        requestHeaders,
-        requestHeadersFromUploadStream,
-        requestPostData,
-        this.props.connector.getLongString,
-      ).then((newFormDataSections) => {
-        updateRequest(
-          request.id,
-          { formDataSections: newFormDataSections },
-          true,
-        );
-      });
-    }
+    updateFormDataSections(nextProps);
   },
 
   componentWillUnmount() {
     MediaQueryList.removeListener(this.onLayoutChange);
 
     let { clientWidth, clientHeight } = findDOMNode(this.refs.endPanel) || {};
 
     if (this.state.isVerticalSpliter && clientWidth) {
@@ -103,40 +78,41 @@ const MonitorPanel = createClass({
     });
   },
 
   render() {
     let {
       connector,
       isEmpty,
       networkDetailsOpen,
+      openLink,
       sourceMapService,
-      openLink
     } = this.props;
 
     let initialWidth = Services.prefs.getIntPref(
         "devtools.netmonitor.panes-network-details-width");
     let initialHeight = Services.prefs.getIntPref(
         "devtools.netmonitor.panes-network-details-height");
+
     return (
       div({ className: "monitor-panel" },
         Toolbar(),
         SplitBox({
           className: "devtools-responsive-container",
           initialWidth: `${initialWidth}px`,
           initialHeight: `${initialHeight}px`,
           minSize: "50px",
           maxSize: "80%",
           splitterSize: "1px",
           startPanel: RequestList({ isEmpty, connector }),
           endPanel: networkDetailsOpen && NetworkDetailsPanel({
             ref: "endPanel",
             connector,
+            openLink,
             sourceMapService,
-            openLink,
           }),
           endPanelCollapsed: !networkDetailsOpen,
           endPanelControl: true,
           vert: this.state.isVerticalSpliter,
         }),
       )
     );
   }
--- a/devtools/client/netmonitor/src/components/ParamsPanel.js
+++ b/devtools/client/netmonitor/src/components/ParamsPanel.js
@@ -1,22 +1,26 @@
 /* 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,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
 const { L10N } = require("../utils/l10n");
 const { getUrlQuery, parseQueryString, parseFormData } = require("../utils/request-utils");
 const { sortObjectKeys } = require("../utils/sort-utils");
+const { updateFormDataSections } = require("../utils/request-utils");
+const Actions = require("../actions/index");
 
 // Components
 const PropertiesView = createFactory(require("./PropertiesView"));
 
 const { div } = DOM;
 
 const JSON_SCOPE_NAME = L10N.getStr("jsonScopeName");
 const PARAMS_EMPTY_TEXT = L10N.getStr("paramsEmptyText");
@@ -26,93 +30,106 @@ const PARAMS_POST_PAYLOAD = L10N.getStr(
 const PARAMS_QUERY_STRING = L10N.getStr("paramsQueryString");
 const SECTION_NAMES = [
   JSON_SCOPE_NAME,
   PARAMS_FORM_DATA,
   PARAMS_POST_PAYLOAD,
   PARAMS_QUERY_STRING,
 ];
 
-/*
+/**
  * Params panel component
  * Displays the GET parameters and POST data of a request
  */
-function ParamsPanel({
-  openLink,
-  request,
-}) {
-  let {
-    formDataSections,
-    mimeType,
-    requestPostData,
-    url,
-  } = request;
-  let postData = requestPostData ? requestPostData.postData.text : null;
-  let query = getUrlQuery(url);
+const ParamsPanel = createClass({
+  displayName: "ParamsPanel",
+
+  propTypes: {
+    connector: PropTypes.object.isRequired,
+    openLink: PropTypes.func,
+    request: PropTypes.object.isRequired,
+    updateRequest: PropTypes.func.isRequired,
+  },
+
+  componentDidMount() {
+    updateFormDataSections(this.props);
+  },
+
+  componentWillReceiveProps(nextProps) {
+    updateFormDataSections(nextProps);
+  },
+
+  render() {
+    let {
+      openLink,
+      request
+    } = this.props;
+    let {
+      formDataSections,
+      mimeType,
+      requestPostData,
+      url,
+    } = request;
+    let postData = requestPostData ? requestPostData.postData.text : null;
+    let query = getUrlQuery(url);
+
+    if (!formDataSections && !postData && !query) {
+      return div({ className: "empty-notice" },
+        PARAMS_EMPTY_TEXT
+      );
+    }
+
+    let object = {};
+    let json;
 
-  if (!formDataSections && !postData && !query) {
-    return div({ className: "empty-notice" },
-      PARAMS_EMPTY_TEXT
+    // Query String section
+    if (query) {
+      object[PARAMS_QUERY_STRING] = getProperties(parseQueryString(query));
+    }
+
+    // Form Data section
+    if (formDataSections && formDataSections.length > 0) {
+      let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
+      object[PARAMS_FORM_DATA] = getProperties(parseFormData(sections));
+    }
+
+    // Request payload section
+    if (formDataSections && formDataSections.length === 0 && postData) {
+      try {
+        json = JSON.parse(postData);
+      } catch (error) {
+        // Continue regardless of parsing error
+      }
+
+      if (json) {
+        object[JSON_SCOPE_NAME] = sortObjectKeys(json);
+      } else {
+        object[PARAMS_POST_PAYLOAD] = {
+          EDITOR_CONFIG: {
+            text: postData,
+            mode: mimeType.replace(/;.+/, ""),
+          },
+        };
+      }
+    } else {
+      postData = "";
+    }
+
+    return (
+      div({ className: "panel-container" },
+        PropertiesView({
+          object,
+          filterPlaceHolder: PARAMS_FILTER_TEXT,
+          sectionNames: SECTION_NAMES,
+          openLink,
+        })
+      )
     );
   }
-
-  let object = {};
-  let json;
-
-  // Query String section
-  if (query) {
-    object[PARAMS_QUERY_STRING] = getProperties(parseQueryString(query));
-  }
-
-  // Form Data section
-  if (formDataSections && formDataSections.length > 0) {
-    let sections = formDataSections.filter((str) => /\S/.test(str)).join("&");
-    object[PARAMS_FORM_DATA] = getProperties(parseFormData(sections));
-  }
-
-  // Request payload section
-  if (formDataSections && formDataSections.length === 0 && postData) {
-    try {
-      json = JSON.parse(postData);
-    } catch (error) {
-      // Continue regardless of parsing error
-    }
-
-    if (json) {
-      object[JSON_SCOPE_NAME] = sortObjectKeys(json);
-    } else {
-      object[PARAMS_POST_PAYLOAD] = {
-        EDITOR_CONFIG: {
-          text: postData,
-          mode: mimeType.replace(/;.+/, ""),
-        },
-      };
-    }
-  } else {
-    postData = "";
-  }
-
-  return (
-    div({ className: "panel-container" },
-      PropertiesView({
-        object,
-        filterPlaceHolder: PARAMS_FILTER_TEXT,
-        sectionNames: SECTION_NAMES,
-        openLink,
-      })
-    )
-  );
-}
-
-ParamsPanel.displayName = "ParamsPanel";
-
-ParamsPanel.propTypes = {
-  request: PropTypes.object.isRequired,
-  openLink: PropTypes.func,
-};
+});
 
 /**
  * Mapping array to dict for TreeView usage.
  * Since TreeView only support Object(dict) format.
  * This function also deal with duplicate key case
  * (for multiple selection and query params with same keys)
  *
  * @param {Object[]} arr - key-value pair array like query or form params
@@ -128,9 +145,13 @@ function getProperties(arr) {
       map[obj.name].push(obj.value);
     } else {
       map[obj.name] = obj.value;
     }
     return map;
   }, {}));
 }
 
-module.exports = ParamsPanel;
+module.exports = connect(null,
+  (dispatch) => ({
+    updateRequest: (id, data, batch) => dispatch(Actions.updateRequest(id, data, batch)),
+  }),
+)(ParamsPanel);
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -67,17 +67,17 @@ function TabboxPanel({
         title: COOKIES_TITLE,
       },
         CookiesPanel({ request, openLink }),
       ),
       TabPanel({
         id: PANELS.PARAMS,
         title: PARAMS_TITLE,
       },
-        ParamsPanel({ request, openLink }),
+        ParamsPanel({ connector, openLink, request }),
       ),
       TabPanel({
         id: PANELS.RESPONSE,
         title: RESPONSE_TITLE,
       },
         ResponsePanel({ request, openLink }),
       ),
       TabPanel({
@@ -86,17 +86,17 @@ function TabboxPanel({
       },
         TimingsPanel({ request }),
       ),
       request.cause && request.cause.stacktrace && request.cause.stacktrace.length > 0 &&
       TabPanel({
         id: PANELS.STACK_TRACE,
         title: STACK_TRACE_TITLE,
       },
-        StackTracePanel({ request, sourceMapService, openLink, connector }),
+        StackTracePanel({ connector, openLink, request, sourceMapService }),
       ),
       request.securityState && request.securityState !== "insecure" &&
       TabPanel({
         id: PANELS.SECURITY,
         title: SECURITY_TITLE,
       },
         SecurityPanel({ request, openLink }),
       ),
--- a/devtools/client/netmonitor/src/reducers/requests.js
+++ b/devtools/client/netmonitor/src/reducers/requests.js
@@ -1,27 +1,29 @@
 /* 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 I = require("devtools/client/shared/vendor/immutable");
-const { getUrlDetails } = require("../utils/request-utils");
+const {
+  getUrlDetails,
+  processNetworkUpdates,
+} = require("../utils/request-utils");
 const {
   ADD_REQUEST,
   CLEAR_REQUESTS,
   CLONE_SELECTED_REQUEST,
   OPEN_NETWORK_DETAILS,
   REMOVE_SELECTED_CUSTOM_REQUEST,
   SELECT_REQUEST,
   SEND_CUSTOM_REQUEST,
   TOGGLE_RECORDING,
   UPDATE_REQUEST,
-  UPDATE_PROPS,
 } = require("../constants");
 
 const Request = I.Record({
   id: null,
   // Set to true in case of a request that's being edited as part of "edit and resend"
   isCustom: false,
   // Request properties - at the beginning, they are unknown and are gradually filled in
   startedMillis: undefined,
@@ -185,40 +187,18 @@ function requestsReducer(state = new Req
       let { requests, lastEndedMillis } = state;
 
       let updatedRequest = requests.get(action.id);
       if (!updatedRequest) {
         return state;
       }
 
       updatedRequest = updatedRequest.withMutations(request => {
-        for (let [key, value] of Object.entries(action.data)) {
-          if (!UPDATE_PROPS.includes(key)) {
-            continue;
-          }
-
-          request[key] = value;
-
-          switch (key) {
-            case "url":
-              // Compute the additional URL details
-              request.urlDetails = getUrlDetails(value);
-              break;
-            case "totalTime":
-              request.endedMillis = request.startedMillis + value;
-              lastEndedMillis = Math.max(lastEndedMillis, request.endedMillis);
-              break;
-            case "requestPostData":
-              request.requestHeadersFromUploadStream = {
-                headers: [],
-                headersSize: 0,
-              };
-              break;
-          }
-        }
+        let values = processNetworkUpdates(action.data);
+        request = Object.assign(request, values);
       });
 
       return state.withMutations(st => {
         st.requests = requests.set(updatedRequest.id, updatedRequest);
         st.lastEndedMillis = lastEndedMillis;
       });
     }
 
--- a/devtools/client/netmonitor/src/utils/request-utils.js
+++ b/devtools/client/netmonitor/src/utils/request-utils.js
@@ -1,16 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-disable mozilla/reject-some-requires */
 
 "use strict";
 
+const {
+  UPDATE_PROPS,
+} = require("devtools/client/netmonitor/src/constants");
+
 const CONTENT_MIME_TYPE_ABBREVIATIONS = {
   "ecmascript": "js",
   "javascript": "js",
   "x-javascript": "js"
 };
 
 /**
  * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
@@ -384,16 +388,79 @@ function getResponseHeader(item, header)
   for (let responseHeader of responseHeaders.headers) {
     if (responseHeader.name.toLowerCase() == header) {
       return responseHeader.value;
     }
   }
   return null;
 }
 
+/**
+ * Extracts any urlencoded form data sections from a POST request.
+ */
+function updateFormDataSections(props) {
+  let {
+    connector,
+    request = {},
+    updateRequest,
+  } = props;
+  let {
+    formDataSections,
+    requestHeaders,
+    requestHeadersFromUploadStream,
+    requestPostData,
+  } = request;
+
+  if (!formDataSections && requestHeaders &&
+      requestHeadersFromUploadStream && requestPostData) {
+    getFormDataSections(
+      requestHeaders,
+      requestHeadersFromUploadStream,
+      requestPostData,
+      connector.getLongString,
+    ).then((newFormDataSections) => {
+      updateRequest(
+        request.id,
+        { formDataSections: newFormDataSections },
+        true,
+      );
+    });
+  }
+}
+
+/**
+ * This helper function is used for additional processing of
+ * incoming network update packets. It's used by Network and
+ * Console panel reducers.
+ */
+function processNetworkUpdates(request) {
+  let result = {};
+  for (let [key, value] of Object.entries(request)) {
+    if (UPDATE_PROPS.includes(key)) {
+      result[key] = value;
+
+      switch (key) {
+        case "securityInfo":
+          result.securityState = value.state;
+          break;
+        case "totalTime":
+          result.totalTime = request.totalTime;
+          break;
+        case "requestPostData":
+          result.requestHeadersFromUploadStream = {
+            headers: [],
+            headersSize: 0,
+          };
+          break;
+      }
+    }
+  }
+  return result;
+}
+
 module.exports = {
   decodeUnicodeBase64,
   getFormDataSections,
   fetchHeaders,
   formDataURI,
   writeHeaderText,
   decodeUnicodeUrl,
   getAbbreviatedMimeType,
@@ -406,11 +473,13 @@ module.exports = {
   getUrlBaseNameWithQuery,
   getUrlDetails,
   getUrlHost,
   getUrlHostName,
   getUrlQuery,
   getUrlScheme,
   parseQueryString,
   parseFormData,
+  updateFormDataSections,
+  processNetworkUpdates,
   propertiesEqual,
   ipToLong,
 };
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -441,17 +441,17 @@ html .jsterm-stack-node {
 
 textarea.jsterm-input-node,
 textarea.jsterm-complete-node {
   width: 100%;
   border: none;
   margin: 0;
   background-color: transparent;
   resize: none;
-  font-size: var(--theme-toolbar-font-size);
+  font-size: inherit;
   line-height: 16px;
   overflow-x: hidden;
   /* Set padding for console input on textarea to make sure it is included in
      scrollHeight that is used when resizing JSTerminal's input. */
   padding: 4px 0;
   padding-inline-start: 20px;
 }
 
@@ -1128,18 +1128,18 @@ a.learn-more-link.webconsole-learn-more-
   flex: 1;
   direction: ltr;
   overflow: auto;
   -moz-user-select: text;
   position: relative;
 }
 
 html,
-body,
-#app-wrapper {
+body {
+  display: block !important; /* Until Bug 1409413 removes the accidental display: flex */
   height: 100%;
   margin: 0;
   padding: 0;
 }
 
 body {
   overflow: hidden;
 }
--- a/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
+++ b/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
@@ -103,17 +103,19 @@ function NetworkEventMessage({
   // API consumed by Net monitor UI components. Most of the method
   // are not needed in context of the Console panel (atm) and thus
   // let's just provide empty implementation.
   // Individual methods might be implemented step by step as needed.
   let connector = {
     viewSourceInDebugger: (url, line) => {
       serviceContainer.onViewSourceInDebugger({url, line});
     },
-    getLongString: () => {},
+    getLongString: (grip) => {
+      return serviceContainer.getLongString(grip);
+    },
     getTabTarget: () => {},
     getNetworkRequest: () => {},
     sendHTTPRequest: () => {},
     setPreferences: () => {},
     triggerActivity: () => {},
   };
 
   // Only render the attachment if the network-event is
--- a/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
+++ b/devtools/client/webconsole/new-console-output/new-console-output-wrapper.js
@@ -69,32 +69,37 @@ NewConsoleOutputWrapper.prototype = {
       let selection = this.document.defaultView.getSelection();
       if (selection && !selection.isCollapsed) {
         return;
       }
 
       this.jsterm.focus();
     });
 
+    let { hud } = this.jsterm;
+
     const serviceContainer = {
       attachRefToHud,
       emitNewMessage: (node, messageId, timeStamp) => {
         this.jsterm.hud.emit("new-messages", new Set([{
           node,
           messageId,
           timeStamp,
         }]));
       },
-      hudProxy: this.jsterm.hud.proxy,
+      hudProxy: hud.proxy,
       openLink: url => {
-        this.jsterm.hud.owner.openLink(url);
+        hud.owner.openLink(url);
       },
       createElement: nodename => {
         return this.document.createElement(nodename);
       },
+      getLongString: (grip) => {
+        return hud.proxy.webConsoleClient.getString(grip);
+      },
     };
 
     // Set `openContextMenu` this way so, `serviceContainer` variable
     // is available in the current scope and we can pass it into
     // `createContextMenu` method.
     serviceContainer.openContextMenu = (e, message) => {
       let { screenX, screenY, target } = e;
 
@@ -228,18 +233,25 @@ NewConsoleOutputWrapper.prototype = {
   dispatchTimestampsToggle: function (enabled) {
     store.dispatch(actions.timestampsToggle(enabled));
   },
 
   dispatchMessageUpdate: function (message, res) {
     // network-message-updated will emit when all the update message arrives.
     // Since we can't ensure the order of the network update, we check
     // that networkInfo.updates has all we need.
+    // Note that 'requestPostData' is sent only for POST requests, so we need
+    // to count with that.
     const NUMBER_OF_NETWORK_UPDATE = 8;
-    if (res.networkInfo.updates.length === NUMBER_OF_NETWORK_UPDATE) {
+    let expectedLength = NUMBER_OF_NETWORK_UPDATE;
+    if (res.networkInfo.updates.indexOf("requestPostData") != -1) {
+      expectedLength++;
+    }
+
+    if (res.networkInfo.updates.length === expectedLength) {
       this.batchedMessageUpdates({ res, message });
     }
   },
 
   dispatchRequestUpdate: function (id, data) {
     this.batchedRequestUpdates({ id, data });
   },
 
--- a/devtools/client/webconsole/new-console-output/reducers/messages.js
+++ b/devtools/client/webconsole/new-console-output/reducers/messages.js
@@ -18,19 +18,23 @@ const {
   FILTERS,
   MESSAGE_TYPE,
   MESSAGE_SOURCE,
 } = constants;
 const { getGripPreviewItems } = require("devtools/client/shared/components/reps/reps");
 const { getSourceNames } = require("devtools/client/shared/source-utils");
 
 const {
-  UPDATE_PROPS
+  UPDATE_REQUEST,
 } = require("devtools/client/netmonitor/src/constants");
 
+const {
+  processNetworkUpdates,
+} = require("devtools/client/netmonitor/src/utils/request-utils");
+
 const MessageState = Immutable.Record({
   // List of all the messages added to the console.
   messagesById: Immutable.OrderedMap(),
   // Array of the visible messages.
   visibleMessages: [],
   // Object for the filtered messages.
   filteredMessagesCount: getDefaultFiltersCounter(),
   // List of the message ids which are opened.
@@ -269,44 +273,24 @@ function messages(state = new MessageSta
     case constants.NETWORK_MESSAGE_UPDATE:
       return state.set(
         "networkMessagesUpdateById",
         Object.assign({}, networkMessagesUpdateById, {
           [action.message.id]: action.message
         })
       );
 
+    case UPDATE_REQUEST:
     case constants.NETWORK_UPDATE_REQUEST: {
       let request = networkMessagesUpdateById[action.id];
       if (!request) {
         return state;
       }
 
-      let values = {};
-      for (let [key, value] of Object.entries(action.data)) {
-        if (UPDATE_PROPS.includes(key)) {
-          values[key] = value;
-
-          switch (key) {
-            case "securityInfo":
-              values.securityState = value.state;
-              break;
-            case "totalTime":
-              values.totalTime = request.totalTime;
-              break;
-            case "requestPostData":
-              values.requestHeadersFromUploadStream = {
-                headers: [],
-                headersSize: 0,
-              };
-              break;
-          }
-        }
-      }
-
+      let values = processNetworkUpdates(action.data);
       newState = state.set(
         "networkMessagesUpdateById",
         Object.assign({}, networkMessagesUpdateById, {
           [action.id]: Object.assign({}, request, values)
         })
       );
 
       return newState;
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -190,39 +190,35 @@ skip-if = true # Bug 1406060
 skip-if = true # Bug 1406060
 [browser_console_webconsole_ctrlw_close_tab.js]
 skip-if = true # Bug 1406060
 [browser_console_webconsole_iframe_messages.js]
 skip-if = true # Bug 1406060
 [browser_console_webconsole_private_browsing.js]
 skip-if = true #	Bug 1403188
 # old console skip-if = e10s # Bug 1042253 - webconsole e10s tests
+[browser_jsterm_add_edited_input_to_history.js]
+[browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js]
 [browser_jsterm_copy_command.js]
 skip-if = true
 subsuite = clipboard
 # old console skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_jsterm_dollar.js]
 [browser_jsterm_history_persist.js]
 [browser_jsterm_inspect.js]
 [browser_jsterm_no_autocompletion_on_defined_variables.js]
 [browser_jsterm_no_input_and_tab_key_pressed.js]
 [browser_jsterm_no_input_change_and_tab_key_pressed.js]
 [browser_netmonitor_shows_reqs_in_webconsole.js]
-[browser_webconsole.js]
-skip-if = true #	Bug 1404829
-[browser_webconsole_add_edited_input_to_history.js]
-skip-if = true # Bug 1408915
 [browser_webconsole_allow_mixedcontent_securityerrors.js]
 tags = mcb
 skip-if = true #	Bug 1403452
 # old console skip-if = (os == 'win' && bits == 64) # Bug 1390001
 [browser_webconsole_assert.js]
 skip-if = true #	Bug 1403458
-[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
-skip-if = true # Bug 1408916
 [browser_webconsole_autocomplete_JSTerm_helpers.js]
 skip-if = true # Bug 1408917
 [browser_webconsole_autocomplete_accessibility.js]
 skip-if = true # Bug 1408918
 [browser_webconsole_autocomplete_and_selfxss.js]
 subsuite = clipboard
 skip-if = true #	Bug 1404850
 # old console skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
rename from devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_add_edited_input_to_history.js
rename to devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_add_edited_input_to_history.js
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_add_edited_input_to_history.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_add_edited_input_to_history.js
@@ -7,26 +7,27 @@
 // lost after navigating in history.
 // See https://bugzilla.mozilla.org/show_bug.cgi?id=817834
 
 "use strict";
 
 const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 817834";
 
 add_task(function* () {
-  yield loadTab(TEST_URI);
-
-  let hud = yield openConsole();
-
+  let hud = yield openNewTabAndConsole(TEST_URI);
+  // Clearing history that might have been set in previous tests.
+  yield hud.jsterm.clearHistory();
   testEditedInputHistory(hud);
+  yield hud.jsterm.clearHistory();
 });
 
-function testEditedInputHistory(HUD) {
-  let jsterm = HUD.jsterm;
+function testEditedInputHistory(hud) {
+  let jsterm = hud.jsterm;
   let inputNode = jsterm.inputNode;
+
   ok(!jsterm.getInputValue(), "jsterm.getInputValue() is empty");
   is(inputNode.selectionStart, 0);
   is(inputNode.selectionEnd, 0);
 
   jsterm.setInputValue('"first item"');
   EventUtils.synthesizeKey("VK_UP", {});
   is(jsterm.getInputValue(), '"first item"', "null test history up");
   EventUtils.synthesizeKey("VK_DOWN", {});
rename from devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
rename to devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_jsterm_autocomplete-properties-with-non-alphanumeric-names.js
@@ -2,46 +2,38 @@
 /* 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";
 
 // Test that properties starting with underscores or dollars can be
 // autocompleted (bug 967468).
-
-add_task(function* () {
-  const TEST_URI = "data:text/html;charset=utf8,test autocompletion with " +
-                   "$ or _";
-  yield loadTab(TEST_URI);
-
-  function* autocomplete(term) {
-    let deferred = defer();
+const TEST_URI = "data:text/html;charset=utf8,test autocompletion with $ or _";
 
-    jsterm.setInputValue(term);
-    jsterm.complete(jsterm.COMPLETE_HINT_ONLY, deferred.resolve);
-
-    yield deferred.promise;
+add_task(async function () {
+  let { jsterm } = await openNewTabAndConsole(TEST_URI);
 
-    ok(popup.itemCount > 0,
-       "There's " + popup.itemCount + " suggestions for '" + term + "'");
-  }
-
-  let { jsterm } = yield openConsole();
-  let popup = jsterm.autocompletePopup;
-
-  yield jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}");
+  await jsterm.execute("var testObject = {$$aaab: '', $$aaac: ''}");
 
   // Should work with bug 967468.
-  yield autocomplete("Object.__d");
-  yield autocomplete("testObject.$$a");
+  await testAutocomplete(jsterm, "Object.__d");
+  await testAutocomplete(jsterm, "testObject.$$a");
 
   // Here's when things go wrong in bug 967468.
-  yield autocomplete("Object.__de");
-  yield autocomplete("testObject.$$aa");
+  await testAutocomplete(jsterm, "Object.__de");
+  await testAutocomplete(jsterm, "testObject.$$aa");
 
   // Should work with bug 1207868.
-  yield jsterm.execute("let foobar = {a: ''}; const blargh = {a: 1};");
-  yield autocomplete("foobar");
-  yield autocomplete("blargh");
-  yield autocomplete("foobar.a");
-  yield autocomplete("blargh.a");
+  await jsterm.execute("let foobar = {a: ''}; const blargh = {a: 1};");
+  await testAutocomplete(jsterm, "foobar");
+  await testAutocomplete(jsterm, "blargh");
+  await testAutocomplete(jsterm, "foobar.a");
+  await testAutocomplete(jsterm, "blargh.a");
 });
+
+async function testAutocomplete(jsterm, inputString) {
+  jsterm.setInputValue(inputString);
+  await new Promise(resolve => jsterm.complete(jsterm.COMPLETE_HINT_ONLY, resolve));
+
+  let popup = jsterm.autocompletePopup;
+  ok(popup.itemCount > 0, `There's ${popup.itemCount} suggestions for '${inputString}'`);
+}
deleted file mode 100644
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* -*- 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";
-
-// See Bug 611795.
-
-const TEST_URI = 'data:text/html;charset=utf-8,<div style="-moz-opacity:0;">' +
-                 'test repeated css warnings</div><p style="-moz-opacity:0">' +
-                 "hi</p>";
-var hud;
-
-/**
- * Unit test for bug 611795:
- * Repeated CSS messages get collapsed into one.
- */
-
-add_task(function* () {
-  yield loadTab(TEST_URI);
-
-  hud = yield openConsole();
-  hud.jsterm.clearOutput(true);
-
-  BrowserReload();
-  yield loadBrowser(gBrowser.selectedBrowser);
-
-  yield onContentLoaded();
-  yield testConsoleLogRepeats();
-
-  hud = null;
-});
-
-function onContentLoaded() {
-  let cssWarning = "Unknown property \u2018-moz-opacity\u2019.  Declaration dropped.";
-
-  return waitForMessages({
-    webconsole: hud,
-    messages: [{
-      text: cssWarning,
-      category: CATEGORY_CSS,
-      severity: SEVERITY_WARNING,
-      repeats: 2,
-    }],
-  });
-}
-
-function testConsoleLogRepeats() {
-  let jsterm = hud.jsterm;
-
-  jsterm.clearOutput();
-
-  jsterm.setInputValue("for (let i = 0; i < 10; ++i) console.log('this is a " +
-                       "line of reasonably long text that I will use to " +
-                       "verify that the repeated text node is of an " +
-                       "appropriate size.');");
-  jsterm.execute();
-
-  return waitForMessages({
-    webconsole: hud,
-    messages: [{
-      text: "this is a line of reasonably long text",
-      category: CATEGORY_WEBDEV,
-      severity: SEVERITY_LOG,
-      repeats: 10,
-    }],
-  });
-}
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_network_messages_expand.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_network_messages_expand.js
@@ -1,45 +1,49 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const TEST_URI = "data:text/html;charset=utf8,Test that clicking on a network message " +
-                 "in the console toggles the HTTP inspection.";
-
 const TEST_FILE = "test-network-request.html";
 const TEST_PATH = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/";
+const TEST_URI = TEST_PATH + TEST_FILE;
 
 const NET_PREF = "devtools.webconsole.filter.net";
 const XHR_PREF = "devtools.webconsole.filter.netxhr";
 
-Services.prefs.setBoolPref(NET_PREF, true);
+Services.prefs.setBoolPref(NET_PREF, false);
 Services.prefs.setBoolPref(XHR_PREF, true);
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref(NET_PREF);
   Services.prefs.clearUserPref(XHR_PREF);
 });
 
 add_task(async function task() {
   const hud = await openNewTabAndConsole(TEST_URI);
 
   const currentTab = gBrowser.selectedTab;
   let target = TargetFactory.forTab(currentTab);
   let toolbox = gDevTools.getToolbox(target);
 
-  const documentUrl = TEST_PATH + TEST_FILE;
-  await loadDocument(documentUrl);
-  info("Document loaded.");
+  // Fire an XHR POST request.
+  await ContentTask.spawn(gBrowser.selectedBrowser, null, function () {
+    content.wrappedJSObject.testXhrPost();
+  });
 
-  let messageNode = await waitFor(() => findMessage(hud, documentUrl));
+  info("XHR executed");
+
+  await waitForRequestUpdates(toolbox);
+
+  let xhrUrl = TEST_PATH + "test-data.json";
+  let messageNode = await waitFor(() => findMessage(hud, xhrUrl));
   let urlNode = messageNode.querySelector(".url");
   info("Network message found.");
 
-  let updates = waitForNetworkUpdates(toolbox);
+  let updates = waitForPayloadReady(toolbox);
 
   // Expand network log
   urlNode.click();
 
   await updates;
   await testNetworkMessage(messageNode);
 });
 
@@ -56,44 +60,61 @@ async function testNetworkMessage(messag
   ok(responseTab, "Response tab is available");
   ok(timingsTab, "Timings tab is available");
 
   // Headers tab should be selected by default, so just check its content.
   let headersContent = messageNode.querySelector(
     "#headers-panel .headers-overview");
   ok(headersContent, "Headers content is available");
 
+  // Select Params tab and check the content. CodeMirror initialization
+  // is delayed to prevent UI freeze, so wait for a little while.
+  paramsTab.click();
+  let paramsPanel = messageNode.querySelector("#params-panel");
+  await waitForSourceEditor(paramsPanel);
+  let paramsContent = messageNode.querySelector(
+    "#params-panel .panel-container .CodeMirror");
+  ok(paramsContent, "Params content is available");
+  ok(paramsContent.textContent.includes("Hello world!"), "Post body is correct");
+
   // Select Response tab and check the content. CodeMirror initialization
-  // is delayed  to prevent UI freeze, so wait for a little while.
+  // is delayed, so again wait for a little while.
   responseTab.click();
-  await waitForSourceEditor(messageNode);
+  let responsePanel = messageNode.querySelector("#response-panel");
+  await waitForSourceEditor(responsePanel);
   let responseContent = messageNode.querySelector(
     "#response-panel .editor-row-container .CodeMirror");
   ok(responseContent, "Response content is available");
   ok(responseContent.textContent, "Response text is available");
 
   // Select Timings tab and check the content.
   timingsTab.click();
   let timingsContent = messageNode.querySelector(
     "#timings-panel .timings-container .timings-label");
   ok(timingsContent, "Timings content is available");
   ok(timingsContent.textContent, "Timings text is available");
 }
 
-async function waitForNetworkUpdates(toolbox) {
-  let panel = toolbox.getCurrentPanel();
-  let hud = panel.hud;
-  let ui = hud.ui;
-
+async function waitForPayloadReady(toolbox) {
+  let {ui} = toolbox.getCurrentPanel().hud;
   return new Promise(resolve => {
     ui.jsterm.hud.on("network-request-payload-ready", () => {
       info("network-request-payload-ready received");
       resolve();
     });
   });
 }
 
-async function waitForSourceEditor(messageNode) {
+async function waitForSourceEditor(panel) {
   return waitUntil(() => {
-    return !!messageNode.querySelector(
-      "#response-panel .editor-row-container .CodeMirror");
+    return !!panel.querySelector(".CodeMirror");
   });
 }
+
+async function waitForRequestUpdates(toolbox) {
+  let {ui} = toolbox.getCurrentPanel().hud;
+  return new Promise(resolve => {
+    ui.jsterm.hud.on("network-message-updated", () => {
+      info("network-message-updated received");
+      resolve();
+    });
+  });
+}
--- a/devtools/client/webconsole/new-console-output/test/store/messages.test.js
+++ b/devtools/client/webconsole/new-console-output/test/store/messages.test.js
@@ -40,17 +40,17 @@ describe("Message reducer:", () => {
       const message = stubPreparedMessages.get("console.log('foobar', 'test')");
       dispatch(actions.messageAdd(packet));
 
       const messages = getAllMessagesById(getState());
 
       expect(messages.first()).toEqual(message);
     });
 
-    it("increments repeat on a repeating message", () => {
+    it("increments repeat on a repeating log message", () => {
       const key1 = "console.log('foobar', 'test')";
       const { dispatch, getState } = setupStore([key1, key1]);
 
       const packet = clonePacket(stubPackets.get(key1));
 
       // Repeat ID must be the same even if the timestamp is different.
       packet.message.timeStamp = 1;
       dispatch(actions.messageAdd(packet));
@@ -60,16 +60,56 @@ describe("Message reducer:", () => {
       const messages = getAllMessagesById(getState());
 
       expect(messages.size).toBe(1);
 
       const repeat = getAllRepeatById(getState());
       expect(repeat[messages.first().id]).toBe(4);
     });
 
+    it("increments repeat on a repeating css message", () => {
+      const key1 = "Unknown property ‘such-unknown-property’.  Declaration dropped.";
+      const { dispatch, getState } = setupStore([key1, key1]);
+
+      const packet = clonePacket(stubPackets.get(key1));
+
+      // Repeat ID must be the same even if the timestamp is different.
+      packet.pageError.timeStamp = 1;
+      dispatch(actions.messageAdd(packet));
+      packet.pageError.timeStamp = 2;
+      dispatch(actions.messageAdd(packet));
+
+      const messages = getAllMessagesById(getState());
+
+      expect(messages.size).toBe(1);
+
+      const repeat = getAllRepeatById(getState());
+      expect(repeat[messages.first().id]).toBe(4);
+    });
+
+    it("increments repeat on a repeating error message", () => {
+      const key1 = "ReferenceError: asdf is not defined";
+      const { dispatch, getState } = setupStore([key1, key1]);
+
+      const packet = clonePacket(stubPackets.get(key1));
+
+      // Repeat ID must be the same even if the timestamp is different.
+      packet.pageError.timeStamp = 1;
+      dispatch(actions.messageAdd(packet));
+      packet.pageError.timeStamp = 2;
+      dispatch(actions.messageAdd(packet));
+
+      const messages = getAllMessagesById(getState());
+
+      expect(messages.size).toBe(1);
+
+      const repeat = getAllRepeatById(getState());
+      expect(repeat[messages.first().id]).toBe(4);
+    });
+
     it("does not increment repeat after closing a group", () => {
       const logKey = "console.log('foobar', 'test')";
       const { getState } = setupStore([
         logKey,
         logKey,
         "console.group('bar')",
         logKey,
         logKey,
--- a/devtools/client/webconsole/test/browser_webconsole_view_source.js
+++ b/devtools/client/webconsole/test/browser_webconsole_view_source.js
@@ -9,26 +9,16 @@
 // order to have it opened in the standard View Source window.
 
 "use strict";
 
 const TEST_URI = "https://example.com/browser/devtools/client/webconsole/" +
                  "test/test-mixedcontent-securityerrors.html";
 
 add_task(function* () {
-  yield actuallyTest();
-});
-
-add_task(function* () {
-  Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", false);
-  yield actuallyTest();
-  Services.prefs.clearUserPref("devtools.debugger.new-debugger-frontend");
-});
-
-var actuallyTest = Task.async(function*() {
   yield loadTab(TEST_URI);
   let hud = yield openConsole(null);
   info("console opened");
 
   let [result] = yield waitForMessages({
     webconsole: hud,
     messages: [{
       text: "Blocked loading mixed active content",
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -74,20 +74,20 @@ ShadowRoot::ShadowRoot(Element* aElement
   // Add the ShadowRoot as a mutation observer on the host to watch
   // for mutations because the insertion points in this ShadowRoot
   // may need to be updated when the host children are modified.
   GetHost()->AddMutationObserver(this);
 }
 
 ShadowRoot::~ShadowRoot()
 {
-  if (GetHost()) {
-    // mPoolHost may have been unlinked or a new ShadowRoot may have been
-    // creating, making this one obsolete.
-    GetHost()->RemoveMutationObserver(this);
+  if (auto* host = GetHost()) {
+    // mHost may have been unlinked or a new ShadowRoot may have been
+    // created, making this one obsolete.
+    host->RemoveMutationObserver(this);
   }
 
   UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
   // nsINode destructor expects mSubtreeRoot == this.
   SetSubtreeRootPointer(this);
 }
 
@@ -109,18 +109,17 @@ ShadowRoot::FromNode(nsINode* aNode)
   return nullptr;
 }
 
 void
 ShadowRoot::StyleSheetChanged()
 {
   mProtoBinding->FlushSkinSheets();
 
-  nsIPresShell* shell = OwnerDoc()->GetShell();
-  if (shell) {
+  if (nsIPresShell* shell = OwnerDoc()->GetShell()) {
     OwnerDoc()->BeginUpdate(UPDATE_STYLE);
     shell->RecordShadowStyleChange(this);
     OwnerDoc()->EndUpdate(UPDATE_STYLE);
   }
 }
 
 void
 ShadowRoot::InsertSheet(StyleSheet* aSheet,
@@ -234,162 +233,178 @@ ShadowRoot::RemoveInsertionPoint(HTMLCon
   mInsertionPoints.RemoveElement(aInsertionPoint);
 }
 
 void
 ShadowRoot::RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
                                      nsTArray<nsIContent*>& aDestInsertionPoints)
 {
   // Remove the insertion point from the destination insertion points.
-  // Also remove all succeeding insertion points because it is no longer
-  // possible for the content to be distributed into deeper node trees.
+  //
+  // Note that while it sounds tempting to just remove all the insertion points
+  // after it too, since they're usually after in tree position, it may not be
+  // the case when we're redistributing after new insertion points have been
+  // bound to the tree before aInsertionPoint, see bug 1409088.
   int32_t index = aDestInsertionPoints.IndexOf(aInsertionPoint);
 
   // It's possible that we already removed the insertion point while processing
-  // other insertion point removals.
+  // other insertion point removals / fallback content redistribution (which
+  // does DestInsertionPoints().Clear()).
   if (index >= 0) {
-    aDestInsertionPoints.SetLength(index);
+    aDestInsertionPoints.RemoveElementAt(index);
   }
 }
 
 void
+ShadowRoot::DistributionChanged()
+{
+  // FIXME(emilio): We could be more granular in a bunch of cases.
+  auto* host = GetHost();
+  if (!host || !host->IsInComposedDoc()) {
+    return;
+  }
+
+  auto* shell = OwnerDoc()->GetShell();
+  if (!shell) {
+    return;
+  }
+
+  // FIXME(emilio): Rename this to DestroyFramesForAndRestyle?
+  shell->DestroyFramesFor(host);
+}
+
+const HTMLContentElement*
 ShadowRoot::DistributeSingleNode(nsIContent* aContent)
 {
   // Find the insertion point to which the content belongs.
-  HTMLContentElement* insertionPoint = nullptr;
-  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
-    if (mInsertionPoints[i]->Match(aContent)) {
-      if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
+  HTMLContentElement* foundInsertionPoint = nullptr;
+  for (HTMLContentElement* insertionPoint : mInsertionPoints) {
+    if (insertionPoint->Match(aContent)) {
+      if (insertionPoint->MatchedNodes().Contains(aContent)) {
         // Node is already matched into the insertion point. We are done.
-        return;
+        return insertionPoint;
       }
 
       // Matching may cause the insertion point to drop fallback content.
-      if (mInsertionPoints[i]->MatchedNodes().IsEmpty() &&
-          static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
+      if (insertionPoint->MatchedNodes().IsEmpty() &&
+          insertionPoint->HasChildren()) {
         // This match will cause the insertion point to drop all fallback
         // content and used matched nodes instead. Give up on the optimization
         // and just distribute all nodes.
         DistributeAllNodes();
-        return;
+        MOZ_ASSERT(insertionPoint->MatchedNodes().Contains(aContent));
+        return insertionPoint;
       }
-      insertionPoint = mInsertionPoints[i];
+      foundInsertionPoint = insertionPoint;
       break;
     }
   }
 
+  if (!foundInsertionPoint) {
+    return nullptr;
+  }
+
   // Find the index into the insertion point.
-  if (insertionPoint) {
-    nsCOMArray<nsIContent>& matchedNodes = insertionPoint->MatchedNodes();
-    // Find the appropriate position in the matched node list for the
-    // newly distributed content.
-    bool isIndexFound = false;
-    ExplicitChildIterator childIterator(GetHost());
-    for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
-      // Seek through the host's explicit children until the inserted content
-      // is found or when the current matched node is reached.
-      if (childIterator.Seek(aContent, matchedNodes[i])) {
-        // aContent was found before the current matched node.
-        insertionPoint->InsertMatchedNode(i, aContent);
-        isIndexFound = true;
-        break;
-      }
-    }
-
-    if (!isIndexFound) {
-      // We have still not found an index in the insertion point,
-      // thus it must be at the end.
-      MOZ_ASSERT(childIterator.Seek(aContent, nullptr),
-                 "Trying to match a node that is not a candidate to be matched");
-      insertionPoint->AppendMatchedNode(aContent);
-    }
-
-    // Handle the case where the parent of the insertion point has a ShadowRoot.
-    // The node distributed into the insertion point must be reprojected to the
-    // insertion points of the parent's ShadowRoot.
-    ShadowRoot* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
-    if (parentShadow) {
-      parentShadow->DistributeSingleNode(aContent);
+  nsCOMArray<nsIContent>& matchedNodes = foundInsertionPoint->MatchedNodes();
+  // Find the appropriate position in the matched node list for the
+  // newly distributed content.
+  bool isIndexFound = false;
+  ExplicitChildIterator childIterator(GetHost());
+  for (uint32_t i = 0; i < matchedNodes.Length(); i++) {
+    // Seek through the host's explicit children until the inserted content
+    // is found or when the current matched node is reached.
+    if (childIterator.Seek(aContent, matchedNodes[i])) {
+      // aContent was found before the current matched node.
+      foundInsertionPoint->InsertMatchedNode(i, aContent);
+      isIndexFound = true;
+      break;
     }
   }
+
+  if (!isIndexFound) {
+    // We have still not found an index in the insertion point,
+    // thus it must be at the end.
+    MOZ_ASSERT(childIterator.Seek(aContent, nullptr),
+               "Trying to match a node that is not a candidate to be matched");
+    foundInsertionPoint->AppendMatchedNode(aContent);
+  }
+
+  return foundInsertionPoint;
 }
 
-void
+const HTMLContentElement*
 ShadowRoot::RemoveDistributedNode(nsIContent* aContent)
 {
   // Find insertion point containing the content and remove the node.
-  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
-    if (mInsertionPoints[i]->MatchedNodes().Contains(aContent)) {
-      // Removing the matched node may cause the insertion point to use
-      // fallback content.
-      if (mInsertionPoints[i]->MatchedNodes().Length() == 1 &&
-          static_cast<nsINode*>(mInsertionPoints[i])->GetFirstChild()) {
-        // Removing the matched node will cause fallback content to be
-        // used instead. Give up optimization and distribute all nodes.
-        DistributeAllNodes();
-        return;
-      }
+  for (HTMLContentElement* insertionPoint : mInsertionPoints) {
+    if (!insertionPoint->MatchedNodes().Contains(aContent)) {
+      continue;
+    }
 
-      mInsertionPoints[i]->RemoveMatchedNode(aContent);
+    // Removing the matched node may cause the insertion point to use
+    // fallback content.
+    if (insertionPoint->MatchedNodes().Length() == 1 &&
+        insertionPoint->HasChildren()) {
+      // Removing the matched node will cause fallback content to be
+      // used instead. Give up optimization and distribute all nodes.
+      DistributeAllNodes();
+      return insertionPoint;
+    }
 
-      // Handle the case where the parent of the insertion point has a ShadowRoot.
-      // The removed node needs to be removed from the insertion points of the
-      // parent's ShadowRoot.
-      ShadowRoot* parentShadow = mInsertionPoints[i]->GetParent()->GetShadowRoot();
-      if (parentShadow) {
-        parentShadow->RemoveDistributedNode(aContent);
-      }
+    insertionPoint->RemoveMatchedNode(aContent);
+    return insertionPoint;
+  }
 
-      break;
-    }
-  }
+  return nullptr;
 }
 
 void
 ShadowRoot::DistributeAllNodes()
 {
   // Create node pool.
   nsTArray<nsIContent*> nodePool;
   ExplicitChildIterator childIterator(GetHost());
   for (nsIContent* content = childIterator.GetNextChild(); content;
        content = childIterator.GetNextChild()) {
     nodePool.AppendElement(content);
   }
 
   nsTArray<ShadowRoot*> shadowsToUpdate;
 
-  for (uint32_t i = 0; i < mInsertionPoints.Length(); i++) {
-    mInsertionPoints[i]->ClearMatchedNodes();
+  for (HTMLContentElement* insertionPoint : mInsertionPoints) {
+    insertionPoint->ClearMatchedNodes();
     // Assign matching nodes from node pool.
     for (uint32_t j = 0; j < nodePool.Length(); j++) {
-      if (mInsertionPoints[i]->Match(nodePool[j])) {
-        mInsertionPoints[i]->AppendMatchedNode(nodePool[j]);
+      if (insertionPoint->Match(nodePool[j])) {
+        insertionPoint->AppendMatchedNode(nodePool[j]);
         nodePool.RemoveElementAt(j--);
       }
     }
 
     // Keep track of instances where the content insertion point is distributed
     // (parent of insertion point has a ShadowRoot).
-    nsIContent* insertionParent = mInsertionPoints[i]->GetParent();
+    nsIContent* insertionParent = insertionPoint->GetParent();
     MOZ_ASSERT(insertionParent, "The only way for an insertion point to be in the"
                                 "mInsertionPoints array is to be a descendant of a"
                                 "ShadowRoot, in which case, it should have a parent");
 
     // If the parent of the insertion point has a ShadowRoot, the nodes distributed
     // to the insertion point must be reprojected to the insertion points of the
     // parent's ShadowRoot.
     ShadowRoot* parentShadow = insertionParent->GetShadowRoot();
     if (parentShadow && !shadowsToUpdate.Contains(parentShadow)) {
       shadowsToUpdate.AppendElement(parentShadow);
     }
   }
 
-  for (uint32_t i = 0; i < shadowsToUpdate.Length(); i++) {
-    shadowsToUpdate[i]->DistributeAllNodes();
+  for (ShadowRoot* shadow : shadowsToUpdate) {
+    shadow->DistributeAllNodes();
   }
+
+  DistributionChanged();
 }
 
 void
 ShadowRoot::GetInnerHTML(nsAString& aInnerHTML)
 {
   GetMarkup(false, aInnerHTML);
 }
 
@@ -440,115 +455,163 @@ ShadowRoot::StyleSheets()
 
 /**
  * Returns whether the web components pool population algorithm
  * on the host would contain |aContent|. This function ignores
  * insertion points in the pool, thus should only be used to
  * test nodes that have not yet been distributed.
  */
 bool
-ShadowRoot::IsPooledNode(nsIContent* aContent, nsIContent* aContainer,
-                         nsIContent* aHost)
+ShadowRoot::IsPooledNode(nsIContent* aContent) const
 {
   if (nsContentUtils::IsContentInsertionPoint(aContent)) {
     // Insertion points never end up in the pool.
     return false;
   }
 
-  if (aContainer == aHost &&
-      nsContentUtils::IsInSameAnonymousTree(aContainer, aContent)) {
+  auto* host = GetHost();
+  auto* container = aContent->GetParent();
+  if (container == host && !aContent->IsRootOfAnonymousSubtree()) {
     // Children of the host will end up in the pool. We check to ensure
     // that the content is in the same anonymous tree as the container
     // because anonymous content may report its container as the host
     // but it may not be in the host's child list.
     return true;
   }
 
-  if (aContainer) {
+  if (auto* content = HTMLContentElement::FromContentOrNull(container)) {
     // Fallback content will end up in pool if its parent is a child of the host.
-    HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
-    return content && content->IsInsertionPoint() &&
+    return content->IsInsertionPoint() &&
            content->MatchedNodes().IsEmpty() &&
-           aContainer->GetParentNode() == aHost;
+           container->GetParentNode() == host;
   }
 
   return false;
 }
 
 void
 ShadowRoot::AttributeChanged(nsIDocument* aDocument,
                              Element* aElement,
                              int32_t aNameSpaceID,
                              nsAtom* aAttribute,
                              int32_t aModType,
                              const nsAttrValue* aOldValue)
 {
-  if (!IsPooledNode(aElement, aElement->GetParent(), GetHost())) {
+  if (!IsPooledNode(aElement)) {
     return;
   }
 
   // Attributes may change insertion point matching, find its new distribution.
-  RemoveDistributedNode(aElement);
-  DistributeSingleNode(aElement);
+  //
+  // FIXME(emilio): What about state changes?
+  if (!RedistributeElement(aElement)) {
+    return;
+  }
+
+  if (!aElement->IsInComposedDoc()) {
+    return;
+  }
+
+  auto* shell = OwnerDoc()->GetShell();
+  if (!shell) {
+    return;
+  }
+
+  shell->DestroyFramesFor(aElement);
+}
+
+bool
+ShadowRoot::RedistributeElement(Element* aElement)
+{
+  auto* oldInsertionPoint = RemoveDistributedNode(aElement);
+  auto* newInsertionPoint = DistributeSingleNode(aElement);
+
+  if (oldInsertionPoint == newInsertionPoint) {
+    if (oldInsertionPoint) {
+      if (auto* shadow = oldInsertionPoint->GetParent()->GetShadowRoot()) {
+        return shadow->RedistributeElement(aElement);
+      }
+    }
+
+    return false;
+  }
+
+  while (oldInsertionPoint) {
+    // Handle the case where the parent of the insertion point has a ShadowRoot.
+    // The node distributed into the insertion point must be reprojected to the
+    // insertion points of the parent's ShadowRoot.
+    auto* shadow = oldInsertionPoint->GetParent()->GetShadowRoot();
+    if (!shadow) {
+      break;
+    }
+
+    oldInsertionPoint = shadow->RemoveDistributedNode(aElement);
+  }
+
+  while (newInsertionPoint) {
+    // Handle the case where the parent of the insertion point has a ShadowRoot.
+    // The node distributed into the insertion point must be reprojected to the
+    // insertion points of the parent's ShadowRoot.
+    auto* shadow = newInsertionPoint->GetParent()->GetShadowRoot();
+    if (!shadow) {
+      break;
+    }
+
+    newInsertionPoint = shadow->DistributeSingleNode(aElement);
+  }
+
+  return true;
 }
 
 void
 ShadowRoot::ContentAppended(nsIDocument* aDocument,
                             nsIContent* aContainer,
                             nsIContent* aFirstNewContent)
 {
-  if (mInsertionPointChanged) {
-    DistributeAllNodes();
-    mInsertionPointChanged = false;
-    return;
-  }
-
-  // Watch for new nodes added to the pool because the node
-  // may need to be added to an insertion point.
-  nsIContent* currentChild = aFirstNewContent;
-  while (currentChild) {
-    // Add insertion point to destination insertion points of fallback content.
-    if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
-      HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
-      if (content && content->MatchedNodes().IsEmpty()) {
-        currentChild->DestInsertionPoints().AppendElement(aContainer);
-      }
-    }
-
-    if (IsPooledNode(currentChild, aContainer, GetHost())) {
-      DistributeSingleNode(currentChild);
-    }
-
-    currentChild = currentChild->GetNextSibling();
+  for (nsIContent* content = aFirstNewContent;
+       content;
+       content = content->GetNextSibling()) {
+    ContentInserted(aDocument, aContainer, aFirstNewContent);
   }
 }
 
 void
 ShadowRoot::ContentInserted(nsIDocument* aDocument,
                             nsIContent* aContainer,
                             nsIContent* aChild)
 {
   if (mInsertionPointChanged) {
     DistributeAllNodes();
     mInsertionPointChanged = false;
     return;
   }
 
+  // Add insertion point to destination insertion points of fallback content.
+  if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
+    HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
+    if (content && content->MatchedNodes().IsEmpty()) {
+      aChild->DestInsertionPoints().AppendElement(aContainer);
+    }
+  }
+
   // Watch for new nodes added to the pool because the node
   // may need to be added to an insertion point.
-  if (IsPooledNode(aChild, aContainer, GetHost())) {
-    // Add insertion point to destination insertion points of fallback content.
-    if (nsContentUtils::IsContentInsertionPoint(aContainer)) {
-      HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
-      if (content && content->MatchedNodes().IsEmpty()) {
-        aChild->DestInsertionPoints().AppendElement(aContainer);
+  if (IsPooledNode(aChild)) {
+    auto* insertionPoint = DistributeSingleNode(aChild);
+    while (insertionPoint) {
+      // Handle the case where the parent of the insertion point has a ShadowRoot.
+      // The node distributed into the insertion point must be reprojected to the
+      // insertion points of the parent's ShadowRoot.
+      auto* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
+      if (!parentShadow) {
+        break;
       }
+
+      insertionPoint = parentShadow->DistributeSingleNode(aChild);
     }
-
-    DistributeSingleNode(aChild);
   }
 }
 
 void
 ShadowRoot::ContentRemoved(nsIDocument* aDocument,
                            nsIContent* aContainer,
                            nsIContent* aChild,
                            nsIContent* aPreviousSibling)
@@ -565,18 +628,31 @@ ShadowRoot::ContentRemoved(nsIDocument* 
     HTMLContentElement* content = HTMLContentElement::FromContent(aContainer);
     if (content && content->MatchedNodes().IsEmpty()) {
       aChild->DestInsertionPoints().Clear();
     }
   }
 
   // Watch for node that is removed from the pool because
   // it may need to be removed from an insertion point.
-  if (IsPooledNode(aChild, aContainer, GetHost())) {
-    RemoveDistributedNode(aChild);
+  if (IsPooledNode(aChild)) {
+    auto* insertionPoint = RemoveDistributedNode(aChild);
+    while (insertionPoint) {
+      // Handle the case where the parent of the insertion point has a
+      // ShadowRoot.
+      //
+      // The removed node needs to be removed from the insertion points of the
+      // parent's ShadowRoot.
+      auto* parentShadow = insertionPoint->GetParent()->GetShadowRoot();
+      if (!parentShadow) {
+        break;
+      }
+
+      insertionPoint = parentShadow->RemoveDistributedNode(aChild);
+    }
   }
 }
 
 nsresult
 ShadowRoot::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                   bool aPreallocateChildren) const
 {
   *aResult = nullptr;
--- a/dom/base/ShadowRoot.h
+++ b/dom/base/ShadowRoot.h
@@ -49,44 +49,67 @@ public:
   void RemoveFromIdTable(Element* aElement, nsAtom* aId);
   void InsertSheet(StyleSheet* aSheet, nsIContent* aLinkingContent);
   void RemoveSheet(StyleSheet* aSheet);
   bool ApplyAuthorStyles();
   void SetApplyAuthorStyles(bool aApplyAuthorStyles);
   StyleSheetList* StyleSheets();
 
   /**
-   * Distributes a single explicit child of the pool host to the content
-   * insertion points in this ShadowRoot.
-   */
-  void DistributeSingleNode(nsIContent* aContent);
-
-  /**
-   * Removes a single explicit child of the pool host from the content
-   * insertion points in this ShadowRoot.
-   */
-  void RemoveDistributedNode(nsIContent* aContent);
-
-  /**
    * Distributes all the explicit children of the pool host to the content
    * insertion points in this ShadowRoot.
    */
   void DistributeAllNodes();
 
+private:
+  /**
+   * Distributes a single explicit child of the pool host to the content
+   * insertion points in this ShadowRoot.
+   *
+   * Returns the insertion point the element is distributed to after this call.
+   *
+   * Note that this doesn't handle distributing the node in the insertion point
+   * parent's shadow root.
+   */
+  const HTMLContentElement* DistributeSingleNode(nsIContent* aContent);
+
+  /**
+   * Removes a single explicit child of the pool host from the content
+   * insertion points in this ShadowRoot.
+   *
+   * Returns the old insertion point, if any.
+   *
+   * Note that this doesn't handle removing the node in the returned insertion
+   * point parent's shadow root.
+   */
+  const HTMLContentElement* RemoveDistributedNode(nsIContent* aContent);
+
+  /**
+   * Redistributes a node of the pool, and returns whether the distribution
+   * changed.
+   */
+  bool RedistributeElement(Element*);
+
+  /**
+   * Called when we redistribute content after insertion points have changed.
+   */
+  void DistributionChanged();
+
+  bool IsPooledNode(nsIContent* aChild) const;
+
+public:
   void AddInsertionPoint(HTMLContentElement* aInsertionPoint);
   void RemoveInsertionPoint(HTMLContentElement* aInsertionPoint);
 
   void SetInsertionPointChanged() { mInsertionPointChanged = true; }
 
   void SetAssociatedBinding(nsXBLBinding* aBinding) { mAssociatedBinding = aBinding; }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  static bool IsPooledNode(nsIContent* aChild, nsIContent* aContainer,
-                           nsIContent* aHost);
   static ShadowRoot* FromNode(nsINode* aNode);
 
   static void RemoveDestInsertionPoint(nsIContent* aInsertionPoint,
                                        nsTArray<nsIContent*>& aDestInsertionPoints);
 
   // WebIDL methods.
   Element* GetElementById(const nsAString& aElementId);
   already_AddRefed<nsContentList>
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -7830,17 +7830,17 @@ fail-if = (os == 'mac' && os_version == 
 [generated/test_conformance__glsl__misc__non-ascii-comments.vert.html]
 [generated/test_conformance__glsl__misc__non-ascii.vert.html]
 [generated/test_conformance__glsl__misc__re-compile-re-link.html]
 fail-if = (os == 'android' && android_version == '10')
 [generated/test_conformance__glsl__misc__sequence-operator-returns-constant.html]
 [generated/test_conformance__glsl__misc__shader-precision-format-obeyed.html]
 [generated/test_conformance__glsl__misc__shader-struct-scope.html]
 [generated/test_conformance__glsl__misc__shader-uniform-packing-restrictions.html]
-skip-if = (os == 'android')
+skip-if = (os == 'android') || (os == 'win' && os_version == '6.1' && debug)
 [generated/test_conformance__glsl__misc__shader-varying-packing-restrictions.html]
 [generated/test_conformance__glsl__misc__shader-with-256-character-define.html]
 [generated/test_conformance__glsl__misc__shader-with-256-character-identifier.frag.html]
 [generated/test_conformance__glsl__misc__shader-with-257-character-define.html]
 [generated/test_conformance__glsl__misc__shader-with-257-character-identifier.frag.html]
 [generated/test_conformance__glsl__misc__shader-with-_webgl-identifier.vert.html]
 [generated/test_conformance__glsl__misc__shader-with-arbitrary-indexing.frag.html]
 [generated/test_conformance__glsl__misc__shader-with-arbitrary-indexing.vert.html]
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -528,17 +528,18 @@ fail-if = (os == 'android' && android_ve
 skip-if = (os == 'android')
 ####################
 # Timeouts
 [generated/test_conformance__context__context-release-upon-reload.html]
 skip-if = (os == 'android')
 [generated/test_conformance__context__context-release-with-workers.html]
 skip-if = (os == 'android')
 [generated/test_conformance__glsl__misc__shader-uniform-packing-restrictions.html]
-skip-if = (os == 'android')
+# Frequent timeout on win7 debug.
+skip-if = (os == 'android') || (os == 'win' && os_version == '6.1' && debug)
 [generated/test_conformance__glsl__bugs__complex-glsl-does-not-crash.html]
 skip-if = (os == 'android')
 
 [generated/test_conformance__glsl__misc__shader-with-non-reserved-words.html]
 fail-if = (os == 'android')
 # (TODO) Generates results after calling finish()
 skip-if = 1
 
--- a/dom/html/HTMLContentElement.cpp
+++ b/dom/html/HTMLContentElement.cpp
@@ -242,28 +242,26 @@ HTMLContentElement::AfterSetAttr(int32_t
             mValidSelector = false;
             mSelectorList = nullptr;
             break;
           }
           selectors = selectors->mNext;
         }
       }
 
-      ShadowRoot* containingShadow = GetContainingShadow();
-      if (containingShadow) {
+      if (ShadowRoot* containingShadow = GetContainingShadow()) {
         containingShadow->DistributeAllNodes();
       }
     } else {
       // The select attribute was removed. This insertion point becomes
       // a universal selector.
       mValidSelector = true;
       mSelectorList = nullptr;
 
-      ShadowRoot* containingShadow = GetContainingShadow();
-      if (containingShadow) {
+      if (ShadowRoot* containingShadow = GetContainingShadow()) {
         containingShadow->DistributeAllNodes();
       }
     }
   }
 
   return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
                                             aOldValue, aSubjectPrincipal, aNotify);
 }
--- a/dom/html/HTMLContentElement.h
+++ b/dom/html/HTMLContentElement.h
@@ -24,24 +24,17 @@ public:
   explicit HTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLContentElement,
                                            nsGenericHTMLElement)
 
-  static HTMLContentElement* FromContent(nsIContent* aContent)
-  {
-    if (aContent->IsHTMLContentElement()) {
-      return static_cast<HTMLContentElement*>(aContent);
-    }
-
-    return nullptr;
-  }
+  NS_IMPL_FROMCONTENT_HELPER(HTMLContentElement, IsHTMLContentElement())
 
   virtual bool IsHTMLContentElement() const override { return true; }
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   virtual nsIDOMNode* AsDOMNode() override { return this; }
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1394,16 +1394,19 @@ public:
       mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("error"));
       if (mOwner->ReadyState() == HAVE_NOTHING &&
           aErrorCode == MEDIA_ERR_ABORTED) {
         // https://html.spec.whatwg.org/multipage/embedded-content.html#media-data-processing-steps-list
         // "If the media data fetching process is aborted by the user"
         mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("abort"));
         mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_EMPTY);
         mOwner->DispatchAsyncEvent(NS_LITERAL_STRING("emptied"));
+        if (mOwner->mDecoder) {
+          mOwner->ShutdownDecoder();
+        }
       } else if (aErrorCode == MEDIA_ERR_SRC_NOT_SUPPORTED) {
         mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_NO_SOURCE);
       } else {
         mOwner->ChangeNetworkState(nsIDOMHTMLMediaElement::NETWORK_IDLE);
       }
     }
   }
 
--- a/dom/html/test/test_fullscreen-api.html
+++ b/dom/html/test/test_fullscreen-api.html
@@ -27,17 +27,16 @@ SimpleTest.requestFlakyTimeout("untriage
 // run in an iframe, which by default will not have the allowfullscreen
 // attribute set, so full-screen won't work.
 var gTestWindows = [
   "file_fullscreen-multiple.html",
   "file_fullscreen-rollback.html",
   "file_fullscreen-esc-exit.html",
   "file_fullscreen-denied.html",
   "file_fullscreen-api.html",
-  "file_fullscreen-plugins.html",
   "file_fullscreen-hidden.html",
   "file_fullscreen-svg-element.html",
   "file_fullscreen-navigation.html",
   "file_fullscreen-scrollbar.html",
   "file_fullscreen-selector.html",
   "file_fullscreen-top-layer.html",
   "file_fullscreen-backdrop.html",
   "file_fullscreen-nested.html",
@@ -63,16 +62,21 @@ function nextTest() {
 
 var gLinuxE10sSkipList = [
   { "test": "file_fullscreen-plugins.html", "reason": "bug 1330553" },
   { "test": "file_fullscreen-api.html", "reason": "bug 1332040" },
   { "test": "file_fullscreen-scrollbar.html", "reason": "bug 1350875" }
 ];
 
 function shouldSkipTest(test) {
+  if (SpecialPowers.Cc["@mozilla.org/gfx/info;1"].getService(SpecialPowers.Ci.nsIGfxInfo).isHeadless &&
+      test == "file_fullscreen-plugins.html") {
+    todo(false, `${test} skipped due to bug 1409805`);
+    return true;
+  }
   if (!SpecialPowers.isMainProcess() &&
       navigator.platform.indexOf('Linux') >= 0) {
     for (let item of gLinuxE10sSkipList) {
       if (item.test == test) {
         todo(false, `${test} skipped due to ${item.reason}`);
         return true;
       }
     }
--- a/dom/media/test/manifest.js
+++ b/dom/media/test/manifest.js
@@ -250,19 +250,16 @@ var gPlayTests = [
   { name:"bug1377278.webm", type:"video/webm", duration:4.0 },
 
   // Test playback of a WebM file with non-zero start time.
   { name:"split.webm", type:"video/webm", duration:1.967 },
 
   // Test playback of a WebM file with resolution changes.
   { name:"resolution-change.webm", type:"video/webm", duration:6.533 },
 
-  // Test playback of a raw file
-  { name:"seek.yuv", type:"video/x-raw-yuv", duration:1.833 },
-
   // A really short, low sample rate, single channel file. This tests whether
   // we can handle playing files when only push very little audio data to the
   // hardware.
   { name:"spacestorm-1000Hz-100ms.ogg", type:"audio/ogg", duration:0.099 },
 
   // Opus data in an ogg container
   { name:"detodos-short.opus", type:"audio/ogg; codecs=opus", duration:0.22 },
   // Opus data in a webm container
@@ -347,18 +344,16 @@ var gSeekToNextFrameTests = [
   { name:"multiple-bos.ogg", type:"video/ogg", duration:0.431 },
   // Test playback/metadata work after a redirect
   { name:"redirect.sjs?domain=mochi.test:8888&file=320x240.ogv",
     type:"video/ogg", duration:0.266 },
   // Test playback of a webm file
   { name:"seek-short.webm", type:"video/webm", duration:0.23 },
   // Test playback of a WebM file with non-zero start time.
   { name:"split.webm", type:"video/webm", duration:1.967 },
-  // Test playback of a raw file
-  { name:"seek.yuv", type:"video/x-raw-yuv", duration:1.833 },
 
   { name:"gizmo-short.mp4", type:"video/mp4", duration:0.27 },
 
   // Test playback of a MP4 file with a non-zero start time (and audio starting
   // a second later).
   { name:"bipbop-lateaudio.mp4", type:"video/mp4" },
 ];
 
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -539,17 +539,16 @@ support-files =
   seek.ogv
   seek.ogv^headers^
   seek-short.ogv
   seek-short.ogv^headers^
   seek.webm
   seek.webm^headers^
   seek-short.webm
   seek-short.webm^headers^
-  seek.yuv
   seek_support.js
   seekLies.sjs
   seek_with_sound.ogg^headers^
   sequential.vtt
   short-cenc.mp4
   sine.webm
   sine.webm^headers^
   sintel-short-clearkey-subsample-encrypted-audio.webm
deleted file mode 100644
index 69485e8e1bd15c06f7e93ae389e393238c266e63..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/dom/tests/mochitest/webcomponents/mochitest.ini
+++ b/dom/tests/mochitest/webcomponents/mochitest.ini
@@ -46,12 +46,12 @@ skip-if = true # disabled - See bug 1390
 [test_template.html]
 [test_template_xhtml.html]
 [test_template_custom_elements.html]
 [test_shadowroot.html]
 [test_shadowroot_inert_element.html]
 [test_shadowroot_style.html]
 [test_shadowroot_style_order.html]
 [test_style_fallback_content.html]
-skip-if = stylo # bug 1409088
+skip-if = stylo # Bug 1410170
 [test_unresolved_pseudo_class.html]
 [test_link_prefetch.html]
 [test_bug1269155.html]
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
@@ -8,16 +8,21 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <div class="tall" id="bodydiv"></div>
 <div id="container"></div>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
+
+if (!SpecialPowers.DOMWindowUtils.isStyledByServo) {
+  SimpleTest.expectAssertions(3, 3); // GeckoRestyleManager stuff.
+}
+
 // Create ShadowRoot.
 var container = document.getElementById("container");
 var elem = document.createElement("div");
 container.appendChild(elem); // Put ShadowRoot host in document.
 var root = elem.createShadowRoot();
 
 // A style element that will be appended into the ShadowRoot.
 var shadowStyle = document.createElement("style");
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -208,24 +208,25 @@ WebRenderCommandBuilder::CreateWebRender
       // If we're going to create a new layer data for this item, stash the
       // ASR so that if we recurse into a sublist they will know where to stop
       // walking up their ASR chain when building scroll metadata.
       if (forceNewLayerData) {
         mAsrStack.push_back(asr);
       }
     }
 
-    // ensure the scope of ScrollingLayersHelper is maintained
-    ScrollingLayersHelper clip(item, aBuilder, aSc, mClipIdCache, apzEnabled);
+    { // ensure the scope of ScrollingLayersHelper is maintained
+      ScrollingLayersHelper clip(item, aBuilder, aSc, mClipIdCache, apzEnabled);
 
-    // Note: this call to CreateWebRenderCommands can recurse back into
-    // this function if the |item| is a wrapper for a sublist.
-    if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
-                                       aDisplayListBuilder)) {
-      PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
+      // Note: this call to CreateWebRenderCommands can recurse back into
+      // this function if the |item| is a wrapper for a sublist.
+      if (!item->CreateWebRenderCommands(aBuilder, aResources, aSc, mManager,
+                                         aDisplayListBuilder)) {
+        PushItemAsImage(item, aBuilder, aResources, aSc, aDisplayListBuilder);
+      }
     }
 
     if (apzEnabled) {
       if (forceNewLayerData) {
         // Pop the thing we pushed before the recursion, so the topmost item on
         // the stack is enclosing display item's ASR (or the stack is empty)
         mAsrStack.pop_back();
         const ActiveScrolledRoot* stopAtAsr =
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -247,19 +247,21 @@ WebRenderLayerManager::EndTransactionWit
                                                   nsDisplayListBuilder* aDisplayListBuilder)
 {
   MOZ_ASSERT(aDisplayList && aDisplayListBuilder);
   WrBridge()->RemoveExpiredFontKeys();
 
   AUTO_PROFILER_TRACING("Paint", "RenderLayers");
   mTransactionIncomplete = false;
 
-  if (gfxPrefs::LayersDump()) {
-    this->Dump();
-  }
+#if 0
+  // Useful for debugging, it dumps the display list *before* we try to build
+  // WR commands from it
+  nsFrame::PrintDisplayList(aDisplayListBuilder, *aDisplayList);
+#endif
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   WrBridge()->BeginTransaction();
   DiscardCompositorAnimations();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
--- a/ipc/chromium/src/base/process_util_mac.mm
+++ b/ipc/chromium/src/base/process_util_mac.mm
@@ -8,16 +8,17 @@
 #include <fcntl.h>
 #include <spawn.h>
 #include <sys/wait.h>
 
 #include <string>
 
 #include "base/eintr_wrapper.h"
 #include "base/logging.h"
+#include "mozilla/ScopeExit.h"
 
 namespace {
 
 static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG");
 
 }  // namespace
 
 namespace base {
@@ -37,42 +38,40 @@ bool LaunchApp(const std::vector<std::st
   bool retval = true;
 
   char* argv_copy[argv.size() + 1];
   for (size_t i = 0; i < argv.size(); i++) {
     argv_copy[i] = const_cast<char*>(argv[i].c_str());
   }
   argv_copy[argv.size()] = NULL;
 
-  // Make sure we don't leak any FDs to the child process by marking all FDs
-  // as close-on-exec.
-  SetAllFDsToCloseOnExec();
-
   EnvironmentArray vars = BuildEnvironmentArray(env_vars_to_set);
 
   posix_spawn_file_actions_t file_actions;
   if (posix_spawn_file_actions_init(&file_actions) != 0) {
     return false;
   }
+  auto file_actions_guard = mozilla::MakeScopeExit([&file_actions] {
+    posix_spawn_file_actions_destroy(&file_actions);
+  });
 
   // Turn fds_to_remap array into a set of dup2 calls.
   for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin();
        it != fds_to_remap.end();
        ++it) {
     int src_fd = it->first;
     int dest_fd = it->second;
 
     if (src_fd == dest_fd) {
       int flags = fcntl(src_fd, F_GETFD);
       if (flags != -1) {
         fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC);
       }
     } else {
       if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) {
-        posix_spawn_file_actions_destroy(&file_actions);
         return false;
       }
     }
   }
 
   // Set up the CPU preference array.
   cpu_type_t cpu_types[1];
   switch (arch) {
@@ -90,38 +89,50 @@ bool LaunchApp(const std::vector<std::st
       break;
   }
 
   // Initialize spawn attributes.
   posix_spawnattr_t spawnattr;
   if (posix_spawnattr_init(&spawnattr) != 0) {
     return false;
   }
+  auto spawnattr_guard = mozilla::MakeScopeExit([&spawnattr] {
+    posix_spawnattr_destroy(&spawnattr);
+  });
 
   // Set spawn attributes.
   size_t attr_count = 1;
   size_t attr_ocount = 0;
   if (posix_spawnattr_setbinpref_np(&spawnattr, attr_count, cpu_types, &attr_ocount) != 0 ||
       attr_ocount != attr_count) {
-    posix_spawnattr_destroy(&spawnattr);
+    return false;
+  }
+
+  // Prevent the child process from inheriting any file descriptors
+  // that aren't named in `file_actions`.  (This is an Apple-specific
+  // extension to posix_spawn.)
+  if (posix_spawnattr_setflags(&spawnattr, POSIX_SPAWN_CLOEXEC_DEFAULT) != 0) {
     return false;
   }
 
+  // Exempt std{in,out,err} from being closed by POSIX_SPAWN_CLOEXEC_DEFAULT.
+  for (int fd = 0; fd <= STDERR_FILENO; ++fd) {
+    if (posix_spawn_file_actions_addinherit_np(&file_actions, fd) != 0) {
+      return false;
+    }
+  }
+
   int pid = 0;
   int spawn_succeeded = (posix_spawnp(&pid,
                                       argv_copy[0],
                                       &file_actions,
                                       &spawnattr,
                                       argv_copy,
                                       vars.get()) == 0);
 
-  posix_spawn_file_actions_destroy(&file_actions);
-
-  posix_spawnattr_destroy(&spawnattr);
-
   bool process_handle_valid = pid > 0;
   if (!spawn_succeeded || !process_handle_valid) {
     retval = false;
   } else {
     gProcessLog.print("==> process %d launched child process %d\n",
                       GetCurrentProcId(), pid);
     if (wait)
       HANDLE_EINTR(waitpid(pid, 0, 0));
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -1202,19 +1202,40 @@ ServoRestyleManager::DoProcessPendingRes
   styleSet->MaybeGCRuleTree();
 
   // Note: We are in the scope of |animationsWithDestroyedFrame|, so
   //       |mAnimationsWithDestroyedFrame| is still valid.
   MOZ_ASSERT(mAnimationsWithDestroyedFrame);
   mAnimationsWithDestroyedFrame->StopAnimationsForElementsWithoutFrames();
 }
 
+#ifdef DEBUG
+static void
+VerifyFlatTree(const nsIContent& aContent)
+{
+  StyleChildrenIterator iter(&aContent);
+
+  for (auto* content = iter.GetNextChild();
+       content;
+       content = iter.GetNextChild()) {
+    MOZ_ASSERT(content->GetFlattenedTreeParentNodeForStyle() == &aContent);
+    VerifyFlatTree(*content);
+  }
+}
+#endif
+
 void
 ServoRestyleManager::ProcessPendingRestyles()
 {
+#ifdef DEBUG
+  if (auto* root = mPresContext->Document()->GetRootElement()) {
+    VerifyFlatTree(*root);
+  }
+#endif
+
   DoProcessPendingRestyles(ServoTraversalFlags::Empty);
 }
 
 void
 ServoRestyleManager::ProcessAllPendingAttributeAndStateInvalidations()
 {
   if (mSnapshots.IsEmpty()) {
     return;
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1404789-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<script>
+  // Test for content redistribution outside of the document.
+  // Passes if it doesn't assert.
+  let host = document.createElement('div');
+  host.innerHTML = "<div id='foo'></div>";
+
+  let shadowRoot = host.createShadowRoot();
+  shadowRoot.innerHTML = "<content select='#foo'></content>";
+
+  host.firstElementChild.removeAttribute('id');
+
+  // Move to the document, do the same.
+  document.documentElement.appendChild(host);
+  host.firstElementChild.setAttribute('id', 'foo');
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1404789-2.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<iframe style="display: none" src="1404789-1.html"></iframe>
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1409088.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<div></div>
+<script>
+let host = document.querySelector('div');
+let shadow = host.createShadowRoot();
+
+// Append two divs and three spans into host.
+host.innerHTML = '<div></div><span></span><div></div><span></span><span></span>';
+shadow.innerHTML = '<content select="div" id="divpoint"></content><content select="div, span" id="allpoint"></content>';
+
+let divPoint = shadow.getElementById("divpoint");
+let allPoint = shadow.getElementById("allpoint");
+
+shadow.removeChild(allPoint);
+shadow.insertBefore(allPoint, divPoint);
+</script>
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -502,10 +502,13 @@ load 1397398-1.html
 load 1397398-2.html
 load 1397398-3.html
 load 1398500.html
 load 1400438-1.html
 load 1400599-1.html
 load 1401739.html
 load 1401840.html
 load 1402476.html
+load 1404789-1.html
+load 1404789-2.html
 load 1406562.html
+load 1409088.html
 load 1409147.html
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -7262,16 +7262,23 @@ nsCSSFrameConstructor::CheckBitsForLazyF
     "constructed lazily should have frames");
   NS_ASSERTION(!needsFrameBitSet, "Ancestors of nodes with frames to be "
     "constructed lazily should not have NEEDS_FRAME bit set");
 }
 #endif
 
 // For inserts aChild should be valid, for appends it should be null.
 // Returns true if this operation can be lazy, false if not.
+//
+// FIXME(emilio, bug 1410020): This function assumes that the flattened tree
+// parent of all the appended children is the same, which, afaict, is not
+// necessarily true.
+//
+// But we disable lazy frame construction for shadow trees... We should fix
+// that, too.
 bool
 nsCSSFrameConstructor::MaybeConstructLazily(Operation aOperation,
                                             nsIContent* aContainer,
                                             nsIContent* aChild)
 {
   // XXXmats no lazy frames for display:contents direct descendants yet
   // (bug 979782).
   if (mPresShell->GetPresContext()->IsChrome() || !aContainer ||
@@ -7296,16 +7303,22 @@ nsCSSFrameConstructor::MaybeConstructLaz
       if (child->IsXULElement()) {
         return false;
       }
     }
   }
 
   // We can construct lazily; just need to set suitable bits in the content
   // tree.
+  nsIContent* parent = aChild->GetFlattenedTreeParent();
+  if (!parent) {
+    // Not part of the flat tree, nothing to do.
+    return true;
+  }
+
 
   // Set NODE_NEEDS_FRAME on the new nodes.
   if (aOperation == CONTENTINSERT) {
     NS_ASSERTION(!aChild->GetPrimaryFrame() ||
                  aChild->GetPrimaryFrame()->GetContent() != aChild,
                  //XXX the aChild->GetPrimaryFrame()->GetContent() != aChild
                  // check is needed due to bug 135040. Remove it once that's
                  // fixed.
@@ -7320,17 +7333,16 @@ nsCSSFrameConstructor::MaybeConstructLaz
                    // fixed.
                    "setting NEEDS_FRAME on a node that already has a frame?");
       child->SetFlags(NODE_NEEDS_FRAME);
     }
   }
 
   // Walk up the tree setting the NODE_DESCENDANTS_NEED_FRAMES bit as we go.
   // We need different handling for servo given the scoped restyle roots.
-  nsIContent* parent = aChild->GetFlattenedTreeParent();
   CheckBitsForLazyFrameConstruction(parent);
 
   if (mozilla::GeckoRestyleManager* geckoRM = RestyleManager()->GetAsGecko()) {
     while (parent && !parent->HasFlag(NODE_DESCENDANTS_NEED_FRAMES)) {
       parent->SetFlags(NODE_DESCENDANTS_NEED_FRAMES);
       parent = parent->GetFlattenedTreeParent();
     }
     geckoRM->PostRestyleEventForLazyConstruction();
@@ -7639,64 +7651,45 @@ nsCSSFrameConstructor::ContentAppended(n
     // Just ignore tree tags, anyway we don't create any frames for them.
     if (tag == nsGkAtoms::treechildren ||
         tag == nsGkAtoms::treeitem ||
         tag == nsGkAtoms::treerow)
       return;
   }
 #endif // MOZ_XUL
 
-  bool isNewShadowTreeContent =
-    aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
-    !aContainer->IsInNativeAnonymousSubtree() &&
-    !aFirstNewContent->IsInNativeAnonymousSubtree();
-
-  if (!isNewShadowTreeContent) {
-    // See comment in ContentRangeInserted for why this is necessary.
-    if (!GetContentInsertionFrameFor(aContainer) &&
-        !aContainer->IsActiveChildrenElement()) {
-      // We're punting on frame construction because there's no container frame.
-      // The Servo-backed style system handles this case like the lazy frame
-      // construction case, except when we're already constructing frames, in
-      // which case we shouldn't need to do anything else.
-      if (aContainer->IsStyledByServo() &&
-          aInsertionKind == InsertionKind::Async) {
-        LazilyStyleNewChildRange(aFirstNewContent, nullptr);
-      }
-      return;
-    }
-
-    if (aInsertionKind == InsertionKind::Async &&
-        MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
-      if (aContainer->IsStyledByServo()) {
-        LazilyStyleNewChildRange(aFirstNewContent, nullptr);
-      }
-      return;
-    }
+  // See comment in ContentRangeInserted for why this is necessary.
+  if (!GetContentInsertionFrameFor(aContainer) &&
+      !aContainer->IsActiveChildrenElement()) {
+    // We're punting on frame construction because there's no container frame.
+    // The Servo-backed style system handles this case like the lazy frame
+    // construction case, except when we're already constructing frames, in
+    // which case we shouldn't need to do anything else.
+    if (aContainer->IsStyledByServo() &&
+        aInsertionKind == InsertionKind::Async) {
+      LazilyStyleNewChildRange(aFirstNewContent, nullptr);
+    }
+    return;
+  }
+
+  if (aInsertionKind == InsertionKind::Async &&
+      MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
+    if (aContainer->IsStyledByServo()) {
+      LazilyStyleNewChildRange(aFirstNewContent, nullptr);
+    }
+    return;
   }
 
   // We couldn't construct lazily. Make Servo eagerly traverse the new content
   // if needed (when aInsertionKind == InsertionKind::Sync, we know that the
   // styles are up-to-date already).
   if (aInsertionKind == InsertionKind::Async && aContainer->IsStyledByServo()) {
     StyleNewChildRange(aFirstNewContent, nullptr);
   }
 
-  if (isNewShadowTreeContent) {
-    // Recreate frames if content is appended into a ShadowRoot
-    // because children of ShadowRoot are rendered in place of children
-    // of the host.
-    //XXXsmaug This is super unefficient!
-    nsIContent* bindingParent = aContainer->GetBindingParent();
-    LAYOUT_PHASE_TEMP_EXIT();
-    RecreateFramesForContent(bindingParent, aInsertionKind);
-    LAYOUT_PHASE_TEMP_REENTER();
-    return;
-  }
-
   LAYOUT_PHASE_TEMP_EXIT();
   InsertionPoint insertion =
     GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr,
                            aInsertionKind);
   nsContainerFrame*& parentFrame = insertion.mParentFrame;
   LAYOUT_PHASE_TEMP_REENTER();
   if (!parentFrame) {
     return;
@@ -7787,18 +7780,20 @@ nsCSSFrameConstructor::ContentAppended(n
   }
 
   // Create some new frames
   //
   // We use the provided tree match context, or create a new one on the fly
   // otherwise.
   Maybe<TreeMatchContext> matchContext;
   if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
+    // We use GetParentElementCrossingShadowRoot to handle the case where
+    // aContainer is a ShadowRoot.
     matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
-    matchContext->InitAncestors(aContainer->AsElement());
+    matchContext->InitAncestors(aFirstNewContent->GetParentElementCrossingShadowRoot());
   }
   nsFrameConstructorState state(mPresShell,
                                 matchContext.ptrOr(aProvidedTreeMatchContext),
                                 GetAbsoluteContainingBlock(parentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(parentFrame, ABS_POS),
                                 containingBlock);
 
   LayoutFrameType frameType = parentFrame->Type();
@@ -8124,71 +8119,55 @@ nsCSSFrameConstructor::ContentRangeInser
       accService->ContentRangeInserted(mPresShell, aContainer,
                                        aStartChild, aEndChild);
     }
 #endif
 
     return;
   }
 
-  bool isNewShadowTreeContent =
-    aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
-    !aContainer->IsInNativeAnonymousSubtree() &&
-    (!aStartChild || !aStartChild->IsInNativeAnonymousSubtree()) &&
-    (!aEndChild || !aEndChild->IsInNativeAnonymousSubtree());
-
-  if (!isNewShadowTreeContent) {
-    nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
-    // The xbl:children element won't have a frame, but default content can have the children as
-    // a parent. While its uncommon to change the structure of the default content itself, a label,
-    // for example, can be reframed by having its value attribute set or removed.
-    if (!parentFrame && !aContainer->IsActiveChildrenElement()) {
-      // We're punting on frame construction because there's no container frame.
-      // The Servo-backed style system handles this case like the lazy frame
-      // construction case, except when we're already constructing frames, in
-      // which case we shouldn't need to do anything else.
-      if (aContainer->IsStyledByServo() &&
-          aInsertionKind == InsertionKind::Async) {
-        LazilyStyleNewChildRange(aStartChild, aEndChild);
-      }
-      return;
-    }
-
-    // Otherwise, we've got parent content. Find its frame.
-    NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
-                 GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
-
-    if (aInsertionKind == InsertionKind::Async &&
-        MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
-      if (aContainer->IsStyledByServo()) {
-        LazilyStyleNewChildRange(aStartChild, aEndChild);
-      }
-      return;
-    }
+  nsContainerFrame* parentFrame = GetContentInsertionFrameFor(aContainer);
+  // The xbl:children element won't have a frame, but default content can have the children as
+  // a parent. While its uncommon to change the structure of the default content itself, a label,
+  // for example, can be reframed by having its value attribute set or removed.
+  if (!parentFrame &&
+      !(aContainer->IsActiveChildrenElement() ||
+        ShadowRoot::FromNode(aContainer))) {
+    // We're punting on frame construction because there's no container frame.
+    // The Servo-backed style system handles this case like the lazy frame
+    // construction case, except when we're already constructing frames, in
+    // which case we shouldn't need to do anything else.
+    if (aContainer->IsStyledByServo() &&
+        aInsertionKind == InsertionKind::Async) {
+      LazilyStyleNewChildRange(aStartChild, aEndChild);
+    }
+    return;
+  }
+
+  MOZ_ASSERT_IF(ShadowRoot::FromNode(aContainer), !parentFrame);
+
+  // Otherwise, we've got parent content. Find its frame.
+  NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
+               GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
+
+  if (aInsertionKind == InsertionKind::Async &&
+      MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
+    if (aContainer->IsStyledByServo()) {
+      LazilyStyleNewChildRange(aStartChild, aEndChild);
+    }
+    return;
   }
 
   // We couldn't construct lazily. Make Servo eagerly traverse the new content
   // if needed (when aInsertionKind == InsertionKind::Sync, we know that the
   // styles are up-to-date already).
   if (aInsertionKind == InsertionKind::Async && aContainer->IsStyledByServo()) {
     StyleNewChildRange(aStartChild, aEndChild);
   }
 
-  if (isNewShadowTreeContent) {
-    // Recreate frames if content is inserted into a ShadowRoot
-    // because children of ShadowRoot are rendered in place of
-    // the children of the host.
-    //XXXsmaug This is super unefficient!
-    nsIContent* bindingParent = aContainer->GetBindingParent();
-    LAYOUT_PHASE_TEMP_EXIT();
-    RecreateFramesForContent(bindingParent, aInsertionKind);
-    LAYOUT_PHASE_TEMP_REENTER();
-    return;
-  }
-
   InsertionPoint insertion;
   if (isSingleInsert) {
     // See if we have an XBL insertion point. If so, then that's our
     // real parent frame; if not, then the frame hasn't been built yet
     // and we just bail.
     insertion = GetInsertionPoint(aContainer, aStartChild);
   } else {
     // Get our insertion point. If we need to issue single ContentInserted's
@@ -8273,18 +8252,20 @@ nsCSSFrameConstructor::ContentRangeInser
     RecreateFramesForContent(insertion.mParentFrame->GetContent(),
                              aInsertionKind);
     LAYOUT_PHASE_TEMP_REENTER();
     return;
   }
 
   Maybe<TreeMatchContext> matchContext;
   if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
+    // We use GetParentElementCrossingShadowRoot to handle the case where
+    // aContainer is a ShadowRoot.
     matchContext.emplace(mDocument, TreeMatchContext::ForFrameConstruction);
-    matchContext->InitAncestors(aContainer ? aContainer->AsElement() : nullptr);
+    matchContext->InitAncestors(aStartChild->GetParentElementCrossingShadowRoot());
   }
   nsFrameConstructorState state(mPresShell,
                                 matchContext.ptrOr(aProvidedTreeMatchContext),
                                 GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
                                 GetFloatContainingBlock(insertion.mParentFrame),
                                 do_AddRef(aFrameState));
 
@@ -8743,30 +8724,16 @@ nsCSSFrameConstructor::ContentRemoved(ns
       if (firstChild && firstChild->GetContent() == aChild) {
         isRoot = true;
         childFrame = firstChild;
         NS_ASSERTION(!childFrame->GetNextSibling(), "How did that happen?");
       }
     }
   }
 
-  if (aContainer && aContainer->HasFlag(NODE_IS_IN_SHADOW_TREE) &&
-      !aContainer->IsInNativeAnonymousSubtree() &&
-      !aChild->IsInNativeAnonymousSubtree()) {
-    // Recreate frames if content is removed from a ShadowRoot because it may
-    // contain an insertion point which can change how the host is rendered.
-    //
-    // XXXsmaug This is super unefficient!
-    nsIContent* bindingParent = aContainer->GetBindingParent();
-    LAYOUT_PHASE_TEMP_EXIT();
-    RecreateFramesForContent(bindingParent, InsertionKind::Async);
-    LAYOUT_PHASE_TEMP_REENTER();
-    return true;
-  }
-
   if (childFrame) {
     InvalidateCanvasIfNeeded(mPresShell, aChild);
 
     // See whether we need to remove more than just childFrame
     LAYOUT_PHASE_TEMP_EXIT();
     if (MaybeRecreateContainerForFrameRemoval(childFrame)) {
       LAYOUT_PHASE_TEMP_REENTER();
       return true;
@@ -9572,18 +9539,21 @@ nsCSSFrameConstructor::GetInsertionPoint
     // We've got an explicit insertion child. Check to see if it's
     // anonymous.
     if (aChild->GetBindingParent() == aContainer) {
       // This child content is anonymous. Don't use the insertion
       // point, since that's only for the explicit kids.
       return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
     }
 
-    if (nsContentUtils::HasDistributedChildren(aContainer)) {
-      // The container distributes nodes, use the frame of the flattened tree parent.
+    if (nsContentUtils::HasDistributedChildren(aContainer) ||
+        ShadowRoot::FromNode(aContainer)) {
+      // The container distributes nodes or is a shadow root, use the frame of
+      // the flattened tree parent.
+      //
       // It may be the case that the node is distributed but not matched to any
       // insertion points, so there is no flattened parent.
       nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
       if (flattenedParent) {
         return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
                               flattenedParent);
       }
       return InsertionPoint();
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -219,17 +219,17 @@ public:
     , mText(text)
     , mFontMetrics(fm)
     , mPoint(point)
     , mListStyleType(listStyleType)
   {
     MOZ_ASSERT(IsTextType());
   }
 
-  void
+  bool
   CreateWebRenderCommands(nsDisplayItem* aItem,
                           wr::DisplayListBuilder& aBuilder,
                           wr::IpcResourceUpdateQueue& aResources,
                           const layers::StackingContextHelper& aSc,
                           mozilla::layers::WebRenderLayerManager* aManager,
                           nsDisplayListBuilder* aDisplayListBuilder);
 
   DrawResult
@@ -264,37 +264,42 @@ public:
            mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN &&
            mListStyleType != NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED &&
            !mText.IsEmpty();
   }
 
   bool
   BuildGlyphForText(nsDisplayItem* aItem, bool disableSubpixelAA);
 
+  void
+  PaintTextToContext(nsIFrame* aFrame,
+                     gfxContext* aCtx,
+                     bool aDisableSubpixelAA);
+
   bool
   IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags);
 
 private:
-  void
+  bool
   CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
                                   wr::DisplayListBuilder& aBuilder,
                                   wr::IpcResourceUpdateQueue& aResources,
                                   const layers::StackingContextHelper& aSc,
                                   mozilla::layers::WebRenderLayerManager* aManager,
                                   nsDisplayListBuilder* aDisplayListBuilder);
 
-  void
+  bool
   CreateWebRenderCommandsForPath(nsDisplayItem* aItem,
                                  wr::DisplayListBuilder& aBuilder,
                                  wr::IpcResourceUpdateQueue& aResources,
                                  const layers::StackingContextHelper& aSc,
                                  mozilla::layers::WebRenderLayerManager* aManager,
                                  nsDisplayListBuilder* aDisplayListBuilder);
 
-  void
+  bool
   CreateWebRenderCommandsForText(nsDisplayItem* aItem,
                                  wr::DisplayListBuilder& aBuilder,
                                  wr::IpcResourceUpdateQueue& aResources,
                                  const layers::StackingContextHelper& aSc,
                                  mozilla::layers::WebRenderLayerManager* aManager,
                                  nsDisplayListBuilder* aDisplayListBuilder);
 
 private:
@@ -317,34 +322,34 @@ private:
   nsPoint mPoint;
   RefPtr<ScaledFont> mFont;
   nsTArray<layers::GlyphArray> mGlyphs;
 
   // Store the type of list-style-type.
   int32_t mListStyleType;
 };
 
-void
+bool
 BulletRenderer::CreateWebRenderCommands(nsDisplayItem* aItem,
                                         wr::DisplayListBuilder& aBuilder,
                                         wr::IpcResourceUpdateQueue& aResources,
                                         const layers::StackingContextHelper& aSc,
                                         mozilla::layers::WebRenderLayerManager* aManager,
                                         nsDisplayListBuilder* aDisplayListBuilder)
 {
   if (IsImageType()) {
-    CreateWebRenderCommandsForImage(aItem, aBuilder, aResources,
+    return CreateWebRenderCommandsForImage(aItem, aBuilder, aResources,
                                     aSc, aManager, aDisplayListBuilder);
   } else if (IsPathType()) {
-    CreateWebRenderCommandsForPath(aItem, aBuilder, aResources,
+    return CreateWebRenderCommandsForPath(aItem, aBuilder, aResources,
                                    aSc, aManager, aDisplayListBuilder);
   } else {
     MOZ_ASSERT(IsTextType());
-    CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
-                                   aManager, aDisplayListBuilder);
+    return CreateWebRenderCommandsForText(aItem, aBuilder, aResources, aSc,
+                                          aManager, aDisplayListBuilder);
   }
 }
 
 DrawResult
 BulletRenderer::Paint(gfxContext& aRenderingContext, nsPoint aPt,
                       const nsRect& aDirtyRect, uint32_t aFlags,
                       bool aDisableSubpixelAA, nsIFrame* aFrame)
 {
@@ -372,28 +377,17 @@ BulletRenderer::Paint(gfxContext& aRende
       drawTarget->Fill(mPath, ColorPattern(ToDeviceColor(mColor)));
       break;
     default:
       MOZ_CRASH("unreachable");
     }
   }
 
   if (IsTextType()) {
-    DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
-    DrawTargetAutoDisableSubpixelAntialiasing
-      disable(drawTarget, aDisableSubpixelAA);
-
-    aRenderingContext.SetColor(Color::FromABGR(mColor));
-
-    nsPresContext* presContext = aFrame->PresContext();
-    if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
-      presContext->SetBidiEnabled();
-    }
-    nsLayoutUtils::DrawString(aFrame, *mFontMetrics, &aRenderingContext,
-                              mText.get(), mText.Length(), mPoint);
+    PaintTextToContext(aFrame, &aRenderingContext, aDisableSubpixelAA);
   }
 
   return DrawResult::SUCCESS;
 }
 
 bool
 BulletRenderer::BuildGlyphForText(nsDisplayItem* aItem, bool disableSubpixelAA)
 {
@@ -402,31 +396,17 @@ BulletRenderer::BuildGlyphForText(nsDisp
   RefPtr<DrawTarget> screenTarget = gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
   RefPtr<DrawTargetCapture> capture =
     Factory::CreateCaptureDrawTarget(screenTarget->GetBackendType(),
                                      IntSize(),
                                      screenTarget->GetFormat());
 
   RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(capture);
 
-  {
-    DrawTargetAutoDisableSubpixelAntialiasing
-      disable(capture, disableSubpixelAA);
-
-    captureCtx->SetColor(
-      Color::FromABGR(mColor));
-
-    nsPresContext* presContext = aItem->Frame()->PresContext();
-    if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
-      presContext->SetBidiEnabled();
-    }
-
-    nsLayoutUtils::DrawString(aItem->Frame(), *mFontMetrics, captureCtx,
-                              mText.get(), mText.Length(), mPoint);
-  }
+  PaintTextToContext(aItem->Frame(), captureCtx, disableSubpixelAA);
 
   layers::GlyphArray* g = mGlyphs.AppendElement();
   std::vector<Glyph> glyphs;
   Color color;
   if (!capture->ContainsOnlyColoredGlyphs(mFont, color, glyphs)) {
     mFont = nullptr;
     mGlyphs.Clear();
     return false;
@@ -434,116 +414,132 @@ BulletRenderer::BuildGlyphForText(nsDisp
 
   g->glyphs().SetLength(glyphs.size());
   PodCopy(g->glyphs().Elements(), glyphs.data(), glyphs.size());
   g->color() = color;
 
   return true;
 }
 
+void
+BulletRenderer::PaintTextToContext(nsIFrame* aFrame,
+                                   gfxContext* aCtx,
+                                   bool aDisableSubpixelAA)
+{
+  MOZ_ASSERT(IsTextType());
+
+  DrawTarget* drawTarget = aCtx->GetDrawTarget();
+  DrawTargetAutoDisableSubpixelAntialiasing
+    disable(drawTarget, aDisableSubpixelAA);
+
+  aCtx->SetColor(Color::FromABGR(mColor));
+
+  nsPresContext* presContext = aFrame->PresContext();
+  if (!presContext->BidiEnabled() && HasRTLChars(mText)) {
+    presContext->SetBidiEnabled();
+  }
+  nsLayoutUtils::DrawString(aFrame, *mFontMetrics, aCtx,
+                            mText.get(), mText.Length(), mPoint);
+}
+
 bool
 BulletRenderer::IsImageContainerAvailable(layers::LayerManager* aManager, uint32_t aFlags)
 {
   MOZ_ASSERT(IsImageType());
 
   return mImage->IsImageContainerAvailable(aManager, aFlags);
 }
 
-void
+bool
 BulletRenderer::CreateWebRenderCommandsForImage(nsDisplayItem* aItem,
                                                 wr::DisplayListBuilder& aBuilder,
                                                 wr::IpcResourceUpdateQueue& aResources,
                                                 const layers::StackingContextHelper& aSc,
                                                 mozilla::layers::WebRenderLayerManager* aManager,
                                                 nsDisplayListBuilder* aDisplayListBuilder)
 {
-  MOZ_ASSERT(IsImageType());
-
-  if (!mImage) {
-     return;
-  }
+  MOZ_RELEASE_ASSERT(IsImageType());
+  MOZ_RELEASE_ASSERT(mImage);
 
   uint32_t flags = aDisplayListBuilder->ShouldSyncDecodeImages() ?
                    imgIContainer::FLAG_SYNC_DECODE :
                    imgIContainer::FLAG_NONE;
 
+  // FIXME: is this check always redundant with the next one?
+  if (IsImageContainerAvailable(aManager, flags)) {
+    return false;
+  }
+
   RefPtr<layers::ImageContainer> container =
     mImage->GetImageContainer(aManager, flags);
   if (!container) {
-    return;
+    return false;
   }
 
   gfx::IntSize size;
   Maybe<wr::ImageKey> key = aManager->CommandBuilder().CreateImageKey(aItem, container, aBuilder, aResources,
                                                                       aSc, size, Nothing());
   if (key.isNothing()) {
-    return;
+    return true;  // Nothing to do
   }
 
   const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(mDest, appUnitsPerDevPixel);
   wr::LayoutRect dest = aSc.ToRelativeLayoutRect(destRect);
 
   aBuilder.PushImage(dest,
                      dest,
                      !aItem->BackfaceIsHidden(),
                      wr::ImageRendering::Auto,
                      key.value());
+
+  return true;
 }
 
-void
+bool
 BulletRenderer::CreateWebRenderCommandsForPath(nsDisplayItem* aItem,
                                                wr::DisplayListBuilder& aBuilder,
                                                wr::IpcResourceUpdateQueue& aResources,
                                                const layers::StackingContextHelper& aSc,
                                                mozilla::layers::WebRenderLayerManager* aManager,
                                                nsDisplayListBuilder* aDisplayListBuilder)
 {
   MOZ_ASSERT(IsPathType());
 
   if (!aManager->CommandBuilder().PushItemAsImage(aItem, aBuilder, aResources, aSc, aDisplayListBuilder)) {
     NS_WARNING("Fail to create WebRender commands for Bullet path.");
+    return false;
   }
+
+  return true;
 }
 
-void
+bool
 BulletRenderer::CreateWebRenderCommandsForText(nsDisplayItem* aItem,
                                                wr::DisplayListBuilder& aBuilder,
                                                wr::IpcResourceUpdateQueue& aResources,
                                                const layers::StackingContextHelper& aSc,
                                                mozilla::layers::WebRenderLayerManager* aManager,
                                                nsDisplayListBuilder* aDisplayListBuilder)
 {
   MOZ_ASSERT(IsTextType());
-  MOZ_ASSERT(mFont);
-  MOZ_ASSERT(!mGlyphs.IsEmpty());
 
-  const int32_t appUnitsPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   bool dummy;
-  LayoutDeviceRect destRect = LayoutDeviceRect::FromAppUnits(
-          aItem->GetBounds(aDisplayListBuilder, &dummy), appUnitsPerDevPixel);
-  wr::LayoutRect wrDestRect = aSc.ToRelativeLayoutRect(destRect);
-
-  nsTArray<wr::GlyphInstance> wrGlyphs;
+  nsRect bounds = aItem->GetBounds(aDisplayListBuilder, &dummy);
 
-  for (layers::GlyphArray& glyphArray : mGlyphs) {
-    const auto& glyphs = glyphArray.glyphs();
-    wrGlyphs.SetLength(glyphs.Length());
+  if (bounds.IsEmpty()) {
+    return true;
+  }
 
-    for (size_t j = 0; j < glyphs.Length(); j++) {
-      wrGlyphs[j].index = glyphs[j].mIndex;
-      wrGlyphs[j].point = aSc.ToRelativeLayoutPoint(
-              LayoutDevicePoint::FromUnknownPoint(glyphs[j].mPosition));
-    }
+  RefPtr<TextDrawTarget> textDrawer = new TextDrawTarget(aBuilder, aSc, aManager, aItem, bounds);
+  RefPtr<gfxContext> captureCtx = gfxContext::CreateOrNull(textDrawer);
+  PaintTextToContext(aItem->Frame(), captureCtx, aItem);
+  textDrawer->TerminateShadows();
 
-    aManager->WrBridge()->PushGlyphs(aBuilder, wrGlyphs, mFont,
-                                     wr::ToColorF(glyphArray.color().value()),
-                                     aSc, wrDestRect, wrDestRect,
-                                     !aItem->BackfaceIsHidden());
-  }
+  return !textDrawer->HasUnsupportedFeatures();
 }
 
 class nsDisplayBullet final : public nsDisplayItem {
 public:
   nsDisplayBullet(nsDisplayListBuilder* aBuilder, nsBulletFrame* aFrame)
     : nsDisplayItem(aBuilder, aFrame)
   {
     MOZ_COUNT_CTOR(nsDisplayBullet);
@@ -674,27 +670,29 @@ nsDisplayBullet::BuildLayer(nsDisplayLis
 
 bool
 nsDisplayBullet::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                                          wr::IpcResourceUpdateQueue& aResources,
                                          const StackingContextHelper& aSc,
                                          mozilla::layers::WebRenderLayerManager* aManager,
                                          nsDisplayListBuilder* aDisplayListBuilder)
 {
-  ContainerLayerParameters parameter;
-  if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
+  // FIXME: avoid needing to make this target if we're drawing text
+  // (non-trivial refactor of all this code)
+  RefPtr<gfxContext> screenRefCtx = gfxContext::CreateOrNull(
+    gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget().get());
+  Maybe<BulletRenderer> br = static_cast<nsBulletFrame*>(mFrame)->
+    CreateBulletRenderer(*screenRefCtx, ToReferenceFrame());
+
+  if (!br) {
     return false;
   }
 
-  if (!mBulletRenderer)
-    return false;
-
-  mBulletRenderer->CreateWebRenderCommands(this, aBuilder, aResources, aSc,
-                                           aManager, aDisplayListBuilder);
-  return true;
+  return br->CreateWebRenderCommands(this, aBuilder, aResources, aSc,
+                                     aManager, aDisplayListBuilder);
 }
 
 void nsDisplayBullet::Paint(nsDisplayListBuilder* aBuilder,
                             gfxContext* aCtx)
 {
   uint32_t flags = imgIContainer::FLAG_NONE;
   if (aBuilder->ShouldSyncDecodeImages()) {
     flags |= imgIContainer::FLAG_SYNC_DECODE;
--- a/layout/reftests/counter-style/reftest.list
+++ b/layout/reftests/counter-style/reftest.list
@@ -1,35 +1,35 @@
 == system-cyclic.html     system-cyclic-ref.html
-fails-if(webrender) == system-fixed.html      system-fixed-ref.html
-fails-if(webrender) == system-symbolic.html   system-symbolic-ref.html
+== system-fixed.html      system-fixed-ref.html
+== system-symbolic.html   system-symbolic-ref.html
 == system-alphabetic.html system-alphabetic-ref.html
 == system-numeric.html    system-numeric-ref.html
-fails-if(webrender) == system-additive.html   system-additive-ref.html
+== system-additive.html   system-additive-ref.html
 == system-extends.html    system-extends-ref.html
 == system-cyclic-invalid.html     system-common-invalid-ref.html
 == system-fixed-invalid.html      system-common-invalid2-ref.html
 == system-symbolic-invalid.html   system-common-invalid-ref.html
 == system-alphabetic-invalid.html system-common-invalid2-ref.html
 == system-numeric-invalid.html    system-common-invalid2-ref.html
 == system-additive-invalid.html   system-common-invalid-ref.html
 == system-extends-invalid.html    system-extends-invalid-ref.html
 == descriptor-negative.html descriptor-negative-ref.html
 == descriptor-prefix.html   descriptor-prefix-ref.html
-fails-if(webrender) == descriptor-suffix.html   descriptor-suffix-ref.html
+== descriptor-suffix.html   descriptor-suffix-ref.html
 == descriptor-range.html    descriptor-range-ref.html
 == descriptor-pad.html      descriptor-pad-ref.html
 == descriptor-fallback.html descriptor-fallback-ref.html
 == descriptor-symbols.html  descriptor-symbols-ref.html
 == descriptor-negative-invalid.html descriptor-negative-invalid-ref.html
 == descriptor-prefix-invalid.html   descriptor-prefix-invalid-ref.html
 == descriptor-suffix-invalid.html   descriptor-suffix-invalid-ref.html
 == descriptor-range-invalid.html    descriptor-range-invalid-ref.html
 == descriptor-pad-invalid.html      descriptor-pad-invalid-ref.html
 == descriptor-fallback.html         descriptor-fallback-ref.html
 == descriptor-symbols-invalid.html  descriptor-symbols-invalid-ref.html
 == name-case-sensitivity.html       name-case-sensitivity-ref.html
-fails-if(webrender) == dependent-builtin.html           dependent-builtin-ref.html
+== dependent-builtin.html           dependent-builtin-ref.html
 == redefine-builtin.html            redefine-builtin-ref.html
 == redefine-attr-mapping.html       redefine-attr-mapping-ref.html
 == disclosure-styles.html           disclosure-styles-ref.html
 == symbols-function.html            symbols-function-ref.html
 == symbols-function-invalid.html    symbols-function-invalid-ref.html
--- a/layout/reftests/counters/reftest.list
+++ b/layout/reftests/counters/reftest.list
@@ -63,17 +63,17 @@
 fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-reset-integer-range.html counter-reset-integer-range-ref.html # bug 989718
 == counter-ua-limits-00.html counter-ua-limits-00-ref.html
 == counter-ua-limits-01.html counter-ua-limits-01-ref.html
 fails-if(xulRuntime.XPCOMABI.match(/arm/)) == counter-ua-limits-02.html counter-ua-limits-02-ref.html # bug 989718
 == counter-ua-limits-03.html counter-ua-limits-03-ref.html
 == counter-ua-limits-list-00.html counter-ua-limits-list-00-ref.html
 == counter-ua-limits-list-01.html counter-ua-limits-list-01-ref.html
 == multiple-thai-counters.html multiple-thai-counters-ref.html
-fails-if(webrender) == counter-suffix.html counter-suffix-ref.html
+== counter-suffix.html counter-suffix-ref.html
 == counter-cjk-decimal.html counter-cjk-decimal-ref.html
 == counter-japanese-informal.html counter-japanese-informal-ref.html
 == counter-japanese-formal.html counter-japanese-formal-ref.html
 == counter-korean-hangul-formal.html counter-korean-hangul-formal-ref.html
 == counter-korean-hanja-informal.html counter-korean-hanja-informal-ref.html
 == counter-korean-hanja-formal.html counter-korean-hanja-formal-ref.html
 == counter-simp-chinese-informal.html counter-simp-chinese-informal-ref.html
 == counter-simp-chinese-formal.html counter-simp-chinese-formal-ref.html
--- a/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html
+++ b/layout/reftests/css-display/display-contents-shadow-dom-1-ref.html
@@ -37,14 +37,12 @@ span { color:blue; }
     <div>A<span>b</span>c</div>
     <div><span>b c</span>d</div>
     <div><span>a </span>b</div>
     <div><b>One</b><i>Two</i></div>
     <div><b>One</b><i>Two</i></div>
     <div><b>One</b><i>Two</i></div>
     <div><b>One</b><i>Two</i></div>
     <b style="color:blue">One</b><i style="color:blue">Two</i>Three
-    <span style="color:green">R</span>
     <div></div>
     <b style="color:green">V</b>
-    <!-- <b style="color:green">Y</b> -->
   </body>
 </html>
--- a/layout/reftests/css-display/display-contents-shadow-dom-1.html
+++ b/layout/reftests/css-display/display-contents-shadow-dom-1.html
@@ -41,22 +41,20 @@ div.after::after {content: " Y";}
     <div id="hostJ"></div>
     <span id="hostK"></span>
     <div id="hostL"></div>
     <div id="hostM"><i>Two</i><b>One</b></div>
     <div id="hostN"><i class="c">Two</i><b>One</b></div>
     <div id="hostO"><i>Two</i><b class="c">One</b></div>
     <div id="hostP"><i class="c">Two</i><b class="c">One</b></div>
     <div id="hostQ" class="c" style="color:blue"><i class="c">Two</i><b class="c">One</b></div>Three
-    <span id="hostR"><style scoped>:scope{color:green}</style></span>
     <div id="hostS" class="c"><span class="c">S</span></div>
     <div id="hostT" class="c">T</div>
     <div id="hostU"><span class="c">U</span></div>
     <div id="hostV" class="c" style="color:red"><b class="c" style="color:inherit">V</b></div>
-    <!-- TODO(bug 1021572?) <div id="hostY" class="c" style="color:red"><b>Y</b></div> -->
 
    <script>
       function shadow(id) {
         return document.getElementById(id).createShadowRoot();
       }
       function span(s) {
         var e = document.createElement("span");
         var t = document.createTextNode(s);
@@ -98,18 +96,16 @@ div.after::after {content: " Y";}
         shadow("hostJ").innerHTML = 'A<content select=".b"></content>';
         shadow("hostK").innerHTML = '<content select=".b"></content>';
         shadow("hostL").innerHTML = '<content select=".b"></content>';
         shadow("hostM").innerHTML = '<content select="b"></content><content select="i"></content>';
         shadow("hostN").innerHTML = '<content select="b"></content><content select="i"></content>';
         shadow("hostO").innerHTML = '<content select="b"></content><content select="i"></content>';
         shadow("hostP").innerHTML = '<content select="b"></content><content select="i"></content>';
         shadow("hostQ").innerHTML = '<content select="b"></content><content select="i"></content>';
-        shadow("hostR").innerHTML = '<content select="span"></content>';
-        // TODO(bug 1021572?) shadow("hostY").innerHTML = '<content select="b"><style scoped>:scope{color:green}</style></content>';
       }
 
       function tweak() {
         document.body.offsetHeight;
 
         host1.appendChild(span("1"));
         host2.appendChild(text("2"));
         host3.appendChild(span("3"));
@@ -207,18 +203,16 @@ div.after::after {content: " Y";}
         inner.className = "before";
         var e = contents(inner);
         e.className = "b";
         hostL.appendChild(e);
         var e = span("b");
         e.className = "b";
         hostL.appendChild(e);
 
-        hostR.appendChild(span("R"));
-
         document.body.offsetHeight;
         setTimeout(function() {
             shadow("hostS");
             shadow("hostT");
             shadow("hostU");
             shadow("hostV").innerHTML = '<z style="color:green"><content select="b"></content></z>';
 
             document.body.offsetHeight;
--- a/layout/reftests/css-display/reftest.list
+++ b/layout/reftests/css-display/reftest.list
@@ -12,17 +12,17 @@ fails-if(styloVsGecko||stylo) pref(layou
 fuzzy-if(winWidget,12,100) skip-if(styloVsGecko||stylo) pref(layout.css.scoped-style.enabled,true) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-ref.html
 == display-contents-tables.xhtml display-contents-tables-ref.xhtml
 == display-contents-tables-2.xhtml display-contents-tables-ref.xhtml
 == display-contents-tables-3.xhtml display-contents-tables-3-ref.xhtml
 == display-contents-visibility-hidden.html display-contents-visibility-hidden-ref.html
 == display-contents-visibility-hidden-2.html display-contents-visibility-hidden-ref.html
 == display-contents-495385-2d.html display-contents-495385-2d-ref.html
 fuzzy-if(Android,7,3935) == display-contents-xbl.xhtml display-contents-xbl-ref.html
-fuzzy-if(Android,7,1186) skip-if(stylo||styloVsGecko) pref(dom.webcomponents.enabled,true) pref(layout.css.scoped-style.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html # stylo: bug 1409086
+fuzzy-if(Android,7,1186) pref(dom.webcomponents.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html
 fails-if(styloVsGecko) == display-contents-xbl-2.xul display-contents-xbl-2-ref.xul # bug 1408235
 fails-if(styloVsGecko) == display-contents-xbl-3.xul display-contents-xbl-3-ref.xul # bug 1408235
 skip == display-contents-xbl-4.xul display-contents-xbl-4-ref.xul # fails (not just asserts) due to bug 1089223
 asserts(0-1) fuzzy-if(Android,8,3216) == display-contents-fieldset.html display-contents-fieldset-ref.html # bug 1089223
 fails-if(styloVsGecko) == display-contents-xbl-5.xul display-contents-xbl-3-ref.xul # bug 1408235
 fails-if(!stylo) == display-contents-xbl-6.xhtml display-contents-xbl-6-ref.html # bug 1345809
 == display-contents-xbl-7.xhtml display-contents-xbl-7-ref.html
 == display-contents-list-item-child.html display-contents-list-item-child-ref.html
--- a/layout/reftests/forms/legend/reftest.list
+++ b/layout/reftests/forms/legend/reftest.list
@@ -1,4 +1,4 @@
 == legend.html legend-ref.html
-fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == shadow-dom.html shadow-dom-ref.html # stylo: bug 1409086
+fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) == shadow-dom.html shadow-dom-ref.html
 == 1273433.html 1273433-ref.html
 fails-if(styloVsGecko||stylo) == 1339287.html 1339287-ref.html
--- a/layout/reftests/forms/legend/shadow-dom.html
+++ b/layout/reftests/forms/legend/shadow-dom.html
@@ -69,32 +69,39 @@ div.after::after {content: " Y";}
         host2.appendChild(legend("2"));
         host3.appendChild(legend("3"));
         host4.appendChild(legend("4"));
 
         var e = legend("5");
         e.style.display = "contents";
         host5.appendChild(e);
 
+        document.body.offsetHeight;
+
         host6.appendChild(legend("6"));
 
         var e = legend("L");
         e.className = "b";
         host7.appendChild(e);
         var e = legend("7");
         e.className = "c";
+
+        document.body.offsetHeight;
+
         host7.appendChild(e);
 
         var e = legend("L");
         e.className = "b after";
         host8.appendChild(e);
         var e = legend("8");
         e.className = "c  before";
         host8.appendChild(e);
 
+        document.body.offsetHeight;
+
         var e = legend("L2");
         e.className = "b before after";
         host9.appendChild(e);
         var e = contents(legend(" L3"));
         e.className = "b before after";
         host9.appendChild(e);
         var e = legend("9");
         e.className = "c  before";
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<template id="tmpl">
+  <div style="display: table">
+    Some text
+    <span style="display: table-cell">something</span>
+    More text
+  </div>
+</template>
+<div id="host"></div>
+<script>
+  let shadowRoot = document.getElementById("host").createShadowRoot();
+  let tmpl = document.getElementById("tmpl");
+  shadowRoot.appendChild(document.importNode(tmpl.content, true));
+  document.body.offsetTop;
+  shadowRoot.firstElementChild.querySelector("span").remove();
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-2.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<template id="tmpl">
+  <div style="display: block">
+    Some text
+    More text
+  </div>
+</template>
+<div id="host"></div>
+<script>
+  let shadowRoot = document.getElementById("host").createShadowRoot();
+  let tmpl = document.getElementById("tmpl");
+  shadowRoot.appendChild(document.importNode(tmpl.content, true));
+  document.body.offsetTop;
+  shadowRoot.firstElementChild.style.display = "table";
+</script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/webcomponents/reframe-shadow-child-ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<div style="display: table">
+  Some text
+  More text
+</div>
--- a/layout/reftests/webcomponents/reftest.list
+++ b/layout/reftests/webcomponents/reftest.list
@@ -1,15 +1,17 @@
 pref(dom.webcomponents.enabled,true) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
 pref(dom.webcomponents.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.html
 pref(dom.webcomponents.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html
 pref(dom.webcomponents.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html
 pref(dom.webcomponents.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
 pref(dom.webcomponents.enabled,true) == basic-insertion-point-1.html basic-insertion-point-1-ref.html
 pref(dom.webcomponents.enabled,true) == basic-insertion-point-2.html basic-insertion-point-2-ref.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == fallback-content-1.html fallback-content-1-ref.html # stylo: bug 1409088
+pref(dom.webcomponents.enabled,true) == fallback-content-1.html fallback-content-1-ref.html
 pref(dom.webcomponents.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
 pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
+pref(dom.webcomponents.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
 pref(dom.webcomponents.enabled,true) fuzzy-if(Android,2,7) == input-transition-1.html input-transition-1-ref.html
 pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo||styloVsGecko) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html # bug 1409136
+pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
 pref(dom.webcomponents.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
+pref(dom.webcomponents.enabled,true) == reframe-shadow-child-1.html reframe-shadow-child-ref.html
+pref(dom.webcomponents.enabled,true) == reframe-shadow-child-2.html reframe-shadow-child-ref.html
--- a/layout/reftests/writing-mode/reftest.list
+++ b/layout/reftests/writing-mode/reftest.list
@@ -26,17 +26,17 @@ fuzzy-if(azureSkia,255,2700) == 1090168-
 == 1096224-1b.html 1096224-1-ref.html
 fails == 1102175-1a.html 1102175-1-ref.html
 == 1102175-1b.html 1102175-1-ref.html
 == 1103613-1.html 1103613-1-ref.html
 == 1105268-1-min-max-dimensions.html 1105268-1-min-max-dimensions-ref.html
 == 1105268-2-min-max-dimensions.html 1105268-2-min-max-dimensions-ref.html
 == 1106669-1-intrinsic-for-container.html 1106669-1-intrinsic-for-container-ref.html
 == 1108923-1-percentage-margins.html 1108923-1-percentage-margins-ref.html
-fails-if(webrender) == 1111944-1-list-marker.html 1111944-1-list-marker-ref.html
+== 1111944-1-list-marker.html 1111944-1-list-marker-ref.html
 fuzzy(116,94) fuzzy-if(winWidget,135,124) HTTP(..) == 1115916-1-vertical-metrics.html 1115916-1-vertical-metrics-ref.html
 == 1117210-1-vertical-baseline-snap.html 1117210-1-vertical-baseline-snap-ref.html
 random-if(webrender) == 1117227-1-text-overflow.html 1117227-1-text-overflow-ref.html
 == 1122366-1-margin-collapse.html 1122366-1-margin-collapse-ref.html
 == 1124636-1-fieldset-max-height.html 1124636-1-fieldset-max-height-ref.html
 == 1124636-2-fieldset-min-height.html 1124636-2-fieldset-min-height-ref.html
 
 == ua-style-sheet-margin-1.html ua-style-sheet-margin-1-ref.html
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -105,17 +105,17 @@ load 894245-1.html
 load 915440.html
 load 927734-1.html
 load 930270-1.html
 load 930270-2.html
 load 945048-1.html
 load 972199-1.html
 load 989965-1.html
 load 992333-1.html
-pref(dom.webcomponents.enabled,true) skip-if(stylo) load 1017798-1.html # bug 1409086
+pref(dom.webcomponents.enabled,true) load 1017798-1.html # bug 1409086
 load 1028514-1.html
 load 1066089-1.html
 load 1074651-1.html
 load 1135534.html
 pref(dom.webcomponents.enabled,true) load 1089463-1.html
 pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html
 pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html
 load 1153693-1.html
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -634,19 +634,24 @@ ClearKeySessionManager::Decrypt(const In
 
   Buffer* buffer = mHost->Allocate(aBuffer.data_size);
   assert(buffer != nullptr);
   assert(buffer->Data() != nullptr);
   assert(buffer->Capacity() >= aBuffer.data_size);
 
   memcpy(buffer->Data(), aBuffer.data, aBuffer.data_size);
 
-  Status status = mDecryptionManager->Decrypt(buffer->Data(),
-                                              buffer->Size(),
-                                              CryptoMetaData(&aBuffer));
+  Status status = Status::kSuccess;
+  // According to the comment `If |iv_size| = 0, the data is unencrypted.`
+  // Use iv_size to determine if the sample is encrypted.
+  if (aBuffer.iv_size != 0) {
+    status = mDecryptionManager->Decrypt(buffer->Data(),
+                                         buffer->Size(),
+                                         CryptoMetaData(&aBuffer));
+  }
 
   aDecryptedBlock->SetDecryptedBuffer(buffer);
   aDecryptedBlock->SetTimestamp(aBuffer.timestamp);
 
   return status;
 }
 
 void
--- a/media/mtransport/transportlayerice.cpp
+++ b/media/mtransport/transportlayerice.cpp
@@ -94,16 +94,27 @@ TransportLayerIce::TransportLayerIce(con
 
 TransportLayerIce::~TransportLayerIce() {
   // No need to do anything here, since we use smart pointers
 }
 
 void TransportLayerIce::SetParameters(RefPtr<NrIceCtx> ctx,
                                       RefPtr<NrIceMediaStream> stream,
                                       int component) {
+  // Stream could be null in the case of some badly written js that causes
+  // us to be in an ICE restart case, but not have valid streams due to
+  // not calling PeerConnectionMedia::EnsureTransports if
+  // PeerConnectionImpl::SetSignalingState_m thinks the conditions were
+  // not correct.  We also solved a case where an incoming answer was
+  // incorrectly beginning an ICE restart when the offer did not indicate one.
+  if (!stream) {
+    MOZ_ASSERT(false);
+    return;
+  }
+
   // If SetParameters is called and we already have a stream_, this means
   // we're handling an ICE restart.  We need to hold the old stream until
   // we know the new stream is working.
   if (stream_ && !old_stream_ && (stream_ != stream)) {
     // Here we leave the old stream's signals connected until we don't need
     // it anymore.  They will be disconnected if ice restart is successful.
     old_stream_ = stream_;
     MOZ_MTLOG(ML_INFO, LAYER_INFO << "SetParameters save old stream("
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -722,16 +722,17 @@ JsepSessionImpl::GetRemoteIds(const Sdp&
   return rv;
 }
 
 nsresult
 JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
                              std::string* offer)
 {
   mLastError.clear();
+  mLocalIceIsRestarting = options.mIceRestart.isSome() && *(options.mIceRestart);
 
   if (mState != kJsepStateStable) {
     JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState));
     return NS_ERROR_UNEXPECTED;
   }
 
   // Undo track assignments from a previous call to CreateOffer
   // (ie; if the track has not been negotiated yet, it doesn't necessarily need
@@ -1989,16 +1990,25 @@ JsepSessionImpl::ValidateRemoteDescripti
 
     if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
       JSEP_SET_ERROR("Remote description changes the media type of m-line "
                      << i);
       return NS_ERROR_INVALID_ARG;
     }
 
     bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
+
+    // Detect bad answer ICE restart when offer doesn't request ICE restart
+    if (mIsOfferer && differ && !mLocalIceIsRestarting) {
+      JSEP_SET_ERROR("Remote description indicates ICE restart but offer did not "
+                     "request ICE restart (new remote description changes either "
+                     "the ice-ufrag or ice-pwd)");
+      return NS_ERROR_INVALID_ARG;
+    }
+
     // Detect whether all the creds are the same or all are different
     if (!iceCredsDiffer.isSome()) {
       // for the first msection capture whether creds are different or same
       iceCredsDiffer = mozilla::Some(differ);
     } else if (iceCredsDiffer.isSome() && *iceCredsDiffer != differ) {
       // subsequent msections must match the first sections
       JSEP_SET_ERROR("Partial ICE restart is unsupported at this time "
                      "(new remote description changes either the ice-ufrag "
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
@@ -29,16 +29,17 @@ public:
 class JsepSessionImpl : public JsepSession
 {
 public:
   JsepSessionImpl(const std::string& name, UniquePtr<JsepUuidGenerator> uuidgen)
       : JsepSession(name),
         mIsOfferer(false),
         mWasOffererLastTime(false),
         mIceControlling(false),
+        mLocalIceIsRestarting(false),
         mRemoteIsIceLite(false),
         mRemoteIceIsRestarting(false),
         mBundlePolicy(kBundleBalanced),
         mSessionId(0),
         mSessionVersion(0),
         mUuidGen(Move(uuidgen)),
         mSdpHelper(&mLastError)
   {
@@ -317,16 +318,17 @@ private:
   std::vector<RefPtr<JsepTransport> > mOldTransports;
   std::vector<JsepTrackPair> mNegotiatedTrackPairs;
 
   bool mIsOfferer;
   bool mWasOffererLastTime;
   bool mIceControlling;
   std::string mIceUfrag;
   std::string mIcePwd;
+  bool mLocalIceIsRestarting;
   bool mRemoteIsIceLite;
   bool mRemoteIceIsRestarting;
   std::vector<std::string> mIceOptions;
   JsepBundlePolicy mBundlePolicy;
   std::vector<JsepDtlsFingerprint> mDtlsFingerprints;
   uint64_t mSessionId;
   uint64_t mSessionVersion;
   std::vector<SdpExtmapAttributeList::Extmap> mAudioRtpExtensions;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -269,16 +269,23 @@ PeerConnectionMedia::StunAddrsHandler::O
 {
   CSFLogInfo(LOGTAG, "%s: receiving (%d) stun addrs", __FUNCTION__,
                                                       (int)addrs.Length());
   if (pcm_) {
     pcm_->mStunAddrs = addrs;
     pcm_->mLocalAddrsCompleted = true;
     pcm_->mStunAddrsRequest = nullptr;
     pcm_->FlushIceCtxOperationQueueIfReady();
+    // If parent process returns 0 STUN addresses, change ICE connection
+    // state to failed.
+    if (!pcm_->mStunAddrs.Length()) {
+      pcm_->SignalIceConnectionStateChange(pcm_->mIceCtxHdlr->ctx().get(),
+                                           NrIceCtx::ICE_CTX_FAILED);
+    }
+
     pcm_ = nullptr;
   }
 }
 
 PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
     : mParent(parent),
       mParentHandle(parent->GetHandle()),
       mParentName(parent->GetName()),
@@ -951,16 +958,28 @@ PeerConnectionMedia::EnsureIceGathering_
   if (mProxyServer) {
     mIceCtxHdlr->ctx()->SetProxyServer(*mProxyServer);
   } else if (aProxyOnly) {
     IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
                               NrIceCtx::ICE_CTX_GATHER_COMPLETE);
     return;
   }
 
+  // Make sure we don't call NrIceCtx::StartGathering if we're in e10s mode
+  // and we received no STUN addresses from the parent process.  In the
+  // absence of previously provided STUN addresses, StartGathering will
+  // attempt to gather them (as in non-e10s mode), and this will cause a
+  // sandboxing exception in e10s mode.
+  if (!mStunAddrs.Length() && XRE_IsContentProcess()) {
+    CSFLogInfo(LOGTAG,
+               "%s: No STUN addresses returned from parent process",
+               __FUNCTION__);
+    return;
+  }
+
   // Belt and suspenders - in e10s mode, the call below to SetStunAddrs
   // needs to have the proper flags set on ice ctx.  For non-e10s,
   // setting those flags happens in StartGathering.  We could probably
   // just set them here, and only do it here.
   mIceCtxHdlr->ctx()->SetCtxFlags(aDefaultRouteOnly, aProxyOnly);
 
   if (mStunAddrs.Length()) {
     mIceCtxHdlr->ctx()->SetStunAddrs(mStunAddrs);
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/SyncPreference.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/SyncPreference.java
@@ -59,26 +59,32 @@ class SyncPreference extends Preference 
                         // http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/preference/Preference.java#562
                         setIcon(null);
                         setIcon(R.drawable.sync_avatar_default);
                 }
             });
             return;
         }
 
-        // Update title from account email.
+        final ExtendedJSONObject profileJSON = fxAccount.getProfileJSON();
+        final String displayName = profileJSON != null ? profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_USERNAME) : null;
+
+        // Update title from account email/display name.
         ThreadUtils.postToUiThread(new Runnable() {
             @Override
             public void run() {
-                setTitle(fxAccount.getEmail());
-                setSummary("");
+                if (!TextUtils.isEmpty(displayName)) {
+                    setTitle(displayName);
+                } else {
+                    setTitle(R.string.pref_sync_default_title);
+                }
+                setSummary(fxAccount.getEmail());
             }
         });
 
-        final ExtendedJSONObject profileJSON = fxAccount.getProfileJSON();
         if (profileJSON == null) {
             return;
         }
 
         // Avatar URI empty, return early.
         final String avatarURI = profileJSON.getString(FxAccountConstants.KEY_PROFILE_JSON_AVATAR);
         if (TextUtils.isEmpty(avatarURI)) {
             return;
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -358,16 +358,17 @@
 <!ENTITY pref_zoom_force_enabled "Always enable zoom">
 <!ENTITY pref_zoom_force_enabled_summary "Force override so you can zoom any page">
 <!ENTITY pref_voice_input "Voice input">
 <!ENTITY pref_voice_input_summary2 "Allow voice dictation in the URL bar">
 <!ENTITY pref_qrcode_enabled "QR code reader">
 <!ENTITY pref_qrcode_enabled_summary2 "Allow QR scanner in the URL bar">
 
 <!ENTITY pref_use_master_password "Use master password">
+<!ENTITY pref_sync_default_title "Firefox Account">
 <!ENTITY pref_sync2 "Sign in">
 <!ENTITY pref_sync_summary2 "Sync your tabs, bookmarks, logins, history">
 <!ENTITY pref_search_suggestions "Show search suggestions">
 <!ENTITY pref_history_search_suggestions "Show search history">
 <!ENTITY pref_import_options "Import options">
 <!ENTITY pref_import_android_summary "Import bookmarks and history from the native browser">
 <!-- Localization note (pref_private_data_openTabs): Open tabs is an option in
      the Clear Private Data  dialog and refers to currently open tabs. -->
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -255,16 +255,17 @@
   <string name="pref_zoom_force_enabled_summary">&pref_zoom_force_enabled_summary;</string>
   <string name="pref_voice_input">&pref_voice_input;</string>
   <string name="pref_voice_input_summary">&pref_voice_input_summary2;</string>
   <string name="pref_qrcode_enabled">&pref_qrcode_enabled;</string>
   <string name="pref_qrcode_enabled_summary">&pref_qrcode_enabled_summary2;</string>
   <string name="pref_restore">&pref_restore_tabs;</string>
   <string name="pref_restore_always">&pref_restore_always;</string>
   <string name="pref_restore_quit">&pref_restore_quit;</string>
+  <string name="pref_sync_default_title">&pref_sync_default_title;</string>
   <string name="pref_sync">&pref_sync2;</string>
   <string name="pref_sync_summary">&pref_sync_summary2;</string>
   <string name="pref_search_suggestions">&pref_search_suggestions;</string>
   <string name="pref_history_search_suggestions">&pref_history_search_suggestions;</string>
   <string name="pref_private_data_openTabs">&pref_private_data_openTabs;</string>
   <string name="pref_private_data_history2">&pref_private_data_history2;</string>
   <string name="pref_private_data_searchHistory">&pref_private_data_searchHistory;</string>
   <string name="pref_private_data_formdata2">&pref_private_data_formdata2;</string>
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/ConfigurableServer15Repository.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/ConfigurableServer15Repository.java
@@ -23,43 +23,46 @@ import org.mozilla.gecko.sync.stage.Serv
  *
  */
 public class ConfigurableServer15Repository extends Server15Repository {
   private final String sortOrder;
   private final long batchLimit;
   private final ServerSyncStage.MultipleBatches multipleBatches;
   private final ServerSyncStage.HighWaterMark highWaterMark;
   private final boolean forceFullFetch;
+  private final boolean abortOnStoreFailure;
   public ConfigurableServer15Repository(
           String collection,
           long syncDeadline,
           String storageURL,
           AuthHeaderProvider authHeaderProvider,
           InfoCollections infoCollections,
           InfoConfiguration infoConfiguration,
           long batchLimit,
           String sort,
           ServerSyncStage.MultipleBatches multipleBatches,
           ServerSyncStage.HighWaterMark highWaterMark,
           RepositoryStateProvider stateProvider,
-          boolean forceFullFetch) throws URISyntaxException {
+          boolean forceFullFetch,
+          boolean abortOnStoreFailure) throws URISyntaxException {
     super(
             collection,
             syncDeadline,
             storageURL,
             authHeaderProvider,
             infoCollections,
             infoConfiguration,
             stateProvider
     );
     this.batchLimit = batchLimit;
     this.sortOrder  = sort;
     this.multipleBatches = multipleBatches;
     this.highWaterMark = highWaterMark;
     this.forceFullFetch = forceFullFetch;
+    this.abortOnStoreFailure = abortOnStoreFailure;
 
     // Sanity check: let's ensure we're configured correctly. At this point in time, it doesn't make
     // sense to use H.W.M. with a non-persistent state provider. This might change if we start retrying
     // during a download in case of 412s.
     if (!stateProvider.isPersistent() && highWaterMark.equals(ServerSyncStage.HighWaterMark.Enabled)) {
       throw new IllegalArgumentException("Can not use H.W.M. with NonPersistentRepositoryStateProvider");
     }
   }
@@ -84,9 +87,14 @@ public class ConfigurableServer15Reposit
     return highWaterMark.equals(ServerSyncStage.HighWaterMark.Enabled);
   }
 
   @Override
   public boolean getFullFetchForced() {
     return forceFullFetch;
   }
 
+  @Override
+  public boolean getAbortOnStoreFailure() {
+    return abortOnStoreFailure;
+  }
+
 }
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15Repository.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15Repository.java
@@ -110,16 +110,20 @@ public class Server15Repository extends 
   public boolean getAllowHighWaterMark() {
     return false;
   }
 
   public boolean getFullFetchForced() {
     return false;
   }
 
+  public boolean getAbortOnStoreFailure() {
+    return false;
+  }
+
   /**
    * A point in time by which this repository's session must complete fetch and store operations.
    * Particularly pertinent for batching downloads performed by the session (should we fetch
    * another batch?) and buffered repositories (do we have enough time to merge what we've downloaded?).
    */
   public long getSyncDeadline() {
     return syncDeadlineMillis;
   }
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15RepositorySession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/Server15RepositorySession.java
@@ -38,17 +38,17 @@ public class Server15RepositorySession e
   @Override
   public void setStoreDelegate(RepositorySessionStoreDelegate storeDelegate) {
     super.setStoreDelegate(storeDelegate);
 
     // Now that we have the delegate, we can initialize our uploader.
     this.uploader = new BatchingUploader(
             this, storeWorkQueue, storeDelegate, Uri.parse(serverRepository.collectionURI.toString()),
             serverRepository.getCollectionLastModified(), serverRepository.getInfoConfiguration(),
-            serverRepository.authHeaderProvider);
+            serverRepository.authHeaderProvider, serverRepository.getAbortOnStoreFailure());
   }
 
   private void fetchSince(long timestamp, RepositorySessionFetchRecordsDelegate delegate) {
     BatchingDownloaderController.resumeFetchSinceIfPossible(
             this.downloader,
             this.serverRepository.stateProvider,
             delegate,
             timestamp,
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploader.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploader.java
@@ -100,25 +100,33 @@ public class BatchingUploader {
     // string containing the data of the record."
     // Sync Storage servers place a hard limit on how large a payload _field_ might be, and so we
     // maintain this limit for a single sanity check.
     private final long maxPayloadFieldBytes;
 
     // Set if this channel should ignore further calls to process.
     private volatile boolean aborted = false;
 
+    // Whether or not we should set aborted if there are any issues with the record.
+    // This is used to prevent corruption with bookmark records, as uploading
+    // only a subset of the bookmarks is very likely to cause corruption, (e.g.
+    // uploading a parent without its children or vice versa).
+    @VisibleForTesting
+    protected final boolean shouldFailBatchOnFailure;
+
     public BatchingUploader(
             final RepositorySession repositorySession, final ExecutorService workQueue,
             final RepositorySessionStoreDelegate sessionStoreDelegate, final Uri baseCollectionUri,
             final Long localCollectionLastModified, final InfoConfiguration infoConfiguration,
-            final AuthHeaderProvider authHeaderProvider) {
+            final AuthHeaderProvider authHeaderProvider, final boolean shouldAbortOnFailure) {
         this.repositorySession = repositorySession;
         this.sessionStoreDelegate = sessionStoreDelegate;
         this.collectionUri = baseCollectionUri;
         this.authHeaderProvider = authHeaderProvider;
+        this.shouldFailBatchOnFailure = shouldAbortOnFailure;
 
         this.uploaderMeta = new UploaderMeta(
                 payloadLock, infoConfiguration.maxTotalBytes, infoConfiguration.maxTotalRecords);
         this.payload = new Payload(
                 payloadLock, infoConfiguration.maxPostBytes, infoConfiguration.maxPostRecords);
 
         this.payloadDispatcher = createPayloadDispatcher(workQueue, localCollectionLastModified);
 
@@ -228,42 +236,36 @@ public class BatchingUploader {
         payloadDispatcher.finalizeQueue(uploaderMeta.needToCommit(), new Runnable() {
             @Override
             public void run() {
                 flush(true, true);
             }
         });
     }
 
-    // We fail the batch for bookmark records because uploading only a subset of bookmark records is
-    // very likely to cause corruption (e.g. uploading a parent without its children or vice versa).
-    @VisibleForTesting
-    /* package-local */ boolean shouldFailBatchOnFailure(Record record) {
-        return record instanceof BookmarkRecord;
-    }
 
     /* package-local */ void setLastStoreTimestamp(AtomicLong lastModifiedTimestamp) {
         repositorySession.setLastStoreTimestamp(lastModifiedTimestamp.get());
     }
 
     /* package-local */ void finished() {
         sessionStoreDelegate.deferredStoreDelegate(executor).onStoreCompleted();
     }
 
     // Common handling for marking a record failure and calling our delegate's onRecordStoreFailed.
     private void failRecordStore(final Exception e, final Record record, boolean sizeOverflow) {
         // There are three cases we're handling here. See bug 1362206 for some rationale here.
-        // 1. If `record` is not a bookmark and it failed sanity checks for reasons other than
+        // 1. shouldFailBatchOnFailure is false, and it failed sanity checks for reasons other than
         //    "it's too large" (say, `record`'s json is 0 bytes),
         //     - Then mark record's store as 'failed' and continue uploading
-        // 2. If `record` is not a bookmark and it failed sanity checks because it's too large,
+        // 2. shouldFailBatchOnFailure is false, and it failed sanity checks because it's too large,
         //     - Continue uploading, and don't fail synchronization because of this one.
-        // 3. If `record` *is* a bookmark, and it failed for any reason
+        // 3. shouldFailBatchOnFailure is true, and it failed for any reason
         //     - Stop uploading.
-        if (shouldFailBatchOnFailure(record)) {
+        if (shouldFailBatchOnFailure) {
             // case 3
             Logger.debug(LOG_TAG, "Batch failed with exception: " + e.toString());
             // Start ignoring records, and send off to our delegate that we failed.
             aborted = true;
             executor.execute(new PayloadDispatcher.NonPayloadContextRunnable() {
                 @Override
                 public void run() {
                     sessionStoreDelegate.onRecordStoreFailed(e, record.guid);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/BookmarksServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/BookmarksServerSyncStage.java
@@ -77,17 +77,18 @@ public class BookmarksServerSyncStage ex
             session.getAuthHeaderProvider(),
             session.config.infoCollections,
             session.config.infoConfiguration,
             BOOKMARKS_BATCH_LIMIT,
             BOOKMARKS_SORT,
             getAllowedMultipleBatches(),
             getAllowedToUseHighWaterMark(),
             getRepositoryStateProvider(),
-            false
+            false,
+            true
     );
   }
 
   @Override
   protected Repository getLocalRepository() {
     return new BufferingMiddlewareRepository(
             session.getSyncDeadline(),
             new MemoryBufferStorage(),
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/FormHistoryServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/FormHistoryServerSyncStage.java
@@ -71,16 +71,17 @@ public class FormHistoryServerSyncStage 
             session.getAuthHeaderProvider(),
             session.config.infoCollections,
             session.config.infoConfiguration,
             FORM_HISTORY_BATCH_LIMIT,
             FORM_HISTORY_SORT,
             getAllowedMultipleBatches(),
             getAllowedToUseHighWaterMark(),
             getRepositoryStateProvider(),
+            false,
             false
     );
   }
 
   @Override
   protected Repository getLocalRepository() {
     return new BufferingMiddlewareRepository(
             session.getSyncDeadline(),
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/HistoryServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/HistoryServerSyncStage.java
@@ -89,16 +89,17 @@ public class HistoryServerSyncStage exte
             session.getAuthHeaderProvider(),
             session.config.infoCollections,
             session.config.infoConfiguration,
             HISTORY_BATCH_LIMIT,
             HISTORY_SORT,
             getAllowedMultipleBatches(),
             getAllowedToUseHighWaterMark(),
             getRepositoryStateProvider(),
+            false,
             false
     );
   }
 
   @Override
   protected RecordFactory getRecordFactory() {
     return new HistoryRecordFactory();
   }
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/RecentHistoryServerSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/RecentHistoryServerSyncStage.java
@@ -90,17 +90,19 @@ public class RecentHistoryServerSyncStag
                 session.getAuthHeaderProvider(),
                 session.config.infoCollections,
                 session.config.infoConfiguration,
                 HISTORY_BATCH_LIMIT,
                 HISTORY_SORT,
                 getAllowedMultipleBatches(),
                 getAllowedToUseHighWaterMark(),
                 getRepositoryStateProvider(),
-                false);
+                false,
+                false
+        );
     }
 
     /**
      * This stage is only enabled if full history session is enabled and did not complete a sync yet.
      */
     @Override
     public boolean isEnabled() throws MetaGlobalException {
         final boolean historyStageEnabled = super.isEnabled();
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ValidateBookmarksSyncStage.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/stage/ValidateBookmarksSyncStage.java
@@ -111,17 +111,18 @@ public class ValidateBookmarksSyncStage 
                 session.getAuthHeaderProvider(),
                 session.config.infoCollections,
                 session.config.infoConfiguration,
                 BOOKMARKS_BATCH_LIMIT,
                 BOOKMARKS_SORT,
                 getAllowedMultipleBatches(),
                 getAllowedToUseHighWaterMark(),
                 getRepositoryStateProvider(),
-                true
+                true,
+                false // We never do any storing, so this is irrelevant
         );
     }
 
     @Override
     protected Repository getLocalRepository() {
         TelemetryStageCollector bookmarkCollector =
                 this.telemetryStageCollector.getSyncCollector().collectorFor("bookmarks");
         return new BookmarksValidationRepository(session.getClientsDelegate(), bookmarkCollector);
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploaderTest.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/BatchingUploaderTest.java
@@ -606,35 +606,28 @@ public class BatchingUploaderTest {
             return new Runnable() {
                 @Override
                 public void run() {}
             };
         }
     }
 
     class MockUploader extends BatchingUploader {
-        boolean abortOnRecordFail;
         MockUploader(final RepositorySession repositorySession, final ExecutorService workQueue,
                      final RepositorySessionStoreDelegate sessionStoreDelegate, final Uri baseCollectionUri,
                      final Long localCollectionLastModified, final InfoConfiguration infoConfiguration,
                      final AuthHeaderProvider authHeaderProvider, final boolean abortOnRecordFail) {
             super(repositorySession, workQueue, sessionStoreDelegate, baseCollectionUri,
-                    localCollectionLastModified, infoConfiguration, authHeaderProvider);
-            this.abortOnRecordFail = abortOnRecordFail;
+                    localCollectionLastModified, infoConfiguration, authHeaderProvider, abortOnRecordFail);
         }
 
         @Override
         PayloadDispatcher createPayloadDispatcher(ExecutorService workQueue, Long localCollectionLastModified) {
             return new MockPayloadDispatcher(workQueue, this, localCollectionLastModified);
         }
-
-        @Override
-        boolean shouldFailBatchOnFailure(Record r) {
-            return abortOnRecordFail;
-        }
     }
 
     private Server15Repository makeConstrainedRepository(boolean firstSync) {
         InfoCollections infoCollections;
         if (firstSync) {
             infoCollections = new InfoCollections() {
                 @Override
                 public Long getTimestamp(String collection) {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegateTest.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/repositories/uploaders/PayloadUploadDelegateTest.java
@@ -137,17 +137,18 @@ public class PayloadUploadDelegateTest {
                 null,
                 new BatchingUploader(
                         repositorySession,
                         null,
                         sessionStoreDelegate,
                         Uri.parse("https://example.com"),
                         0L,
                         mock(InfoConfiguration.class),
-                        mock(AuthHeaderProvider.class)
+                        mock(AuthHeaderProvider.class),
+                        false
                 )
         );
 
         authHeaderProvider = mock(AuthHeaderProvider.class);
     }
 
     @Test
     public void testHandleRequestSuccessNonSuccess() {
--- a/security/apps/moz.build
+++ b/security/apps/moz.build
@@ -32,17 +32,17 @@ if CONFIG['GNU_CXX']:
     # Gecko headers aren't warning-free enough for us to enable these warnings.
     CXXFLAGS += [
         '-Wno-unused-parameter',
     ]
 
 test_ssl_path = '/security/manager/ssl/tests/unit'
 
 headers_arrays_certs = [
-    ('xpcshell.inc', 'xpcshellRoot', test_ssl_path + '/test_signed_apps/trusted_ca1.der'),
+    ('xpcshell.inc', 'xpcshellRoot', test_ssl_path + '/test_signed_apps/xpcshellTestRoot.der'),
     ('addons-public.inc', 'addonsPublicRoot', 'addons-public.crt'),
     ('addons-stage.inc', 'addonsStageRoot', 'addons-stage.crt'),
     ('privileged-package-root.inc', 'privilegedPackageRoot', 'privileged-package-root.der'),
 ]
 
 for header, array_name, cert in headers_arrays_certs:
     GENERATED_FILES += [header]
     h = GENERATED_FILES[header]
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -838,35 +838,16 @@ CertVerifier::VerifyCert(CERTCertificate
                             EndEntityOrCA::MustBeEndEntity,
                             KeyUsage::keyAgreement, // ECDH/DH
                             KeyPurposeId::id_kp_emailProtection,
                             CertPolicyId::anyPolicy, stapledOCSPResponse);
       }
       break;
     }
 
-    case certificateUsageObjectSigner: {
-      NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
-                                       mOCSPCache, pinArg, ocspGETConfig,
-                                       mOCSPTimeoutSoft, mOCSPTimeoutHard,
-                                       mCertShortLifetimeInDays,
-                                       pinningDisabled, MIN_RSA_BITS_WEAK,
-                                       ValidityCheckingMode::CheckingOff,
-                                       SHA1Mode::Allowed,
-                                       NetscapeStepUpPolicy::NeverMatch,
-                                       originAttributes, builtChain, nullptr,
-                                       nullptr);
-      rv = BuildCertChain(trustDomain, certDER, time,
-                          EndEntityOrCA::MustBeEndEntity,
-                          KeyUsage::digitalSignature,
-                          KeyPurposeId::id_kp_codeSigning,
-                          CertPolicyId::anyPolicy, stapledOCSPResponse);
-      break;
-    }
-
     default:
       rv = Result::FATAL_ERROR_INVALID_ARGS;
   }
 
   if (rv != Success) {
     return rv;
   }
 
--- a/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
+++ b/security/manager/locales/en-US/chrome/pipnss/pipnss.properties
@@ -41,17 +41,16 @@ Fips140SlotDescription=FIPS 140 Cryptogr
 # 32
 InternalToken=Software Security Device
 # End of size restriction.
 VerifySSLClient=SSL Client Certificate
 VerifySSLServer=SSL Server Certificate
 VerifySSLCA=SSL Certificate Authority
 VerifyEmailSigner=Email Signer Certificate
 VerifyEmailRecip=Email Recipient Certificate
-VerifyObjSign=Object Signer
 HighGrade=High Grade
 MediumGrade=Medium Grade
 # LOCALIZATION NOTE (nick_template): $1s is the common name from a cert (e.g. "Mozilla"), $2s is the CA name (e.g. VeriSign)
 nick_template=%1$s’s %2$s ID
 #These are the strings set for the ASN1 objects in a certificate.
 CertDumpCertificate=Certificate
 CertDumpVersion=Version
 # LOCALIZATION NOTE (CertDumpVersionValue): %S is a version number (e.g. "3" in "Version 3")
--- a/security/manager/pki/resources/content/certViewer.js
+++ b/security/manager/pki/resources/content/certViewer.js
@@ -87,38 +87,35 @@ function setWindowName() {
 }
 
 // Certificate usages we care about in the certificate viewer.
 const certificateUsageSSLClient              = 0x0001;
 const certificateUsageSSLServer              = 0x0002;
 const certificateUsageSSLCA                  = 0x0008;
 const certificateUsageEmailSigner            = 0x0010;
 const certificateUsageEmailRecipient         = 0x0020;
-const certificateUsageObjectSigner           = 0x0040;
 
 // A map from the name of a certificate usage to the value of the usage.
 // Useful for printing debugging information and for enumerating all supported
 // usages.
 const certificateUsages = {
   certificateUsageSSLClient,
   certificateUsageSSLServer,
   certificateUsageSSLCA,
   certificateUsageEmailSigner,
   certificateUsageEmailRecipient,
-  certificateUsageObjectSigner,
 };
 
 // Map of certificate usage name to localization identifier.
 const certificateUsageToStringBundleName = {
   certificateUsageSSLClient: "VerifySSLClient",
   certificateUsageSSLServer: "VerifySSLServer",
   certificateUsageSSLCA: "VerifySSLCA",
   certificateUsageEmailSigner: "VerifyEmailSigner",
   certificateUsageEmailRecipient: "VerifyEmailRecip",
-  certificateUsageObjectSigner: "VerifyObjSign",
 };
 
 const PRErrorCodeSuccess = 0;
 
 const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
 const SEC_ERROR_EXPIRED_CERTIFICATE                     = SEC_ERROR_BASE + 11;
 const SEC_ERROR_REVOKED_CERTIFICATE                     = SEC_ERROR_BASE + 12;
 const SEC_ERROR_UNKNOWN_ISSUER                          = SEC_ERROR_BASE + 13;
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -637,93 +637,87 @@ nsNSSCertificate::GetOrganizationalUnit(
     UniquePORTString orgunit(CERT_GetOrgUnitName(&mCert->subject));
     if (orgunit) {
       aOrganizationalUnit = NS_ConvertUTF8toUTF16(orgunit.get());
     }
   }
   return NS_OK;
 }
 
+static nsresult
+UniqueCERTCertListToMutableArray(/*in*/ UniqueCERTCertList& nssChain,
+                                /*out*/ nsIArray** x509CertArray)
+{
+  if (!x509CertArray) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
+  if (!array) {
+    return NS_ERROR_FAILURE;
+  }
+
+  CERTCertListNode* node;
+  for (node = CERT_LIST_HEAD(nssChain.get());
+       !CERT_LIST_END(node, nssChain.get());
+       node = CERT_LIST_NEXT(node)) {
+    nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
+    nsresult rv = array->AppendElement(cert, false);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  array.forget(x509CertArray);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsNSSCertificate::GetChain(nsIArray** _rvChain)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
   NS_ENSURE_ARG(_rvChain);
 
   mozilla::pkix::Time now(mozilla::pkix::Now());
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   UniqueCERTCertList nssChain;
-  // We want to test all usages, but we start with server because most of the
-  // time Firefox users care about server certs.
-  if (certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, now,
-                               nullptr, /*XXX fixme*/
-                               nullptr, /* hostname */
-                               nssChain,
-                               CertVerifier::FLAG_LOCAL_ONLY)
-        != mozilla::pkix::Success) {
-    nssChain = nullptr;
-    // keep going
-  }
-
-  // This is the whitelist of all non-SSLServer usages that are supported by
-  // verifycert.
-  const int otherUsagesToTest = certificateUsageSSLClient |
-                                certificateUsageSSLCA |
-                                certificateUsageEmailSigner |
-                                certificateUsageEmailRecipient |
-                                certificateUsageObjectSigner;
-  for (int usage = certificateUsageSSLClient;
-       usage < certificateUsageAnyCA && !nssChain;
-       usage = usage << 1) {
-    if ((usage & otherUsagesToTest) == 0) {
-      continue;
-    }
+  // We want to test all usages supported by the certificate verifier, but we
+  // start with TLS server because most of the time Firefox users care about
+  // server certs.
+  const int usagesToTest[] = { certificateUsageSSLServer,
+                               certificateUsageSSLClient,
+                               certificateUsageSSLCA,
+                               certificateUsageEmailSigner,
+                               certificateUsageEmailRecipient };
+  for (auto usage : usagesToTest) {
     if (certVerifier->VerifyCert(mCert.get(), usage, now,
                                  nullptr, /*XXX fixme*/
                                  nullptr, /*hostname*/
                                  nssChain,
                                  CertVerifier::FLAG_LOCAL_ONLY)
-          != mozilla::pkix::Success) {
-      nssChain = nullptr;
-      // keep going
+          == mozilla::pkix::Success) {
+      return UniqueCERTCertListToMutableArray(nssChain, _rvChain);
     }
   }
 
-  if (!nssChain) {
-    // There is not verified path for the chain, however we still want to
-    // present to the user as much of a possible chain as possible, in the case
-    // where there was a problem with the cert or the issuers.
-    nssChain = UniqueCERTCertList(
-      CERT_GetCertChainFromCert(mCert.get(), PR_Now(), certUsageSSLClient));
-  }
+  // There is no verified path for the chain, however we still want to
+  // present to the user as much of a possible chain as possible, in the case
+  // where there was a problem with the cert or the issuers.
+  nssChain = UniqueCERTCertList(
+    CERT_GetCertChainFromCert(mCert.get(), PR_Now(), certUsageSSLClient));
   if (!nssChain) {
     return NS_ERROR_FAILURE;
   }
-
-  // enumerate the chain for scripting purposes
-  nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
-  if (!array) {
-    return NS_ERROR_FAILURE;
-  }
-  CERTCertListNode* node;
-  for (node = CERT_LIST_HEAD(nssChain.get());
-       !CERT_LIST_END(node, nssChain.get());
-       node = CERT_LIST_NEXT(node)) {
-    nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
-    array->AppendElement(cert, false);
-  }
-  *_rvChain = array;
-  NS_IF_ADDREF(*_rvChain);
-  return NS_OK;
+  return UniqueCERTCertListToMutableArray(nssChain, _rvChain);
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetSubjectName(nsAString& _subjectName)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
--- a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
+++ b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js
@@ -34,34 +34,34 @@ add_task(async function testEmailEndEnti
   let win = await displayCertificate(cert);
   checkUsages(win, ["Email Recipient Certificate", "Email Signer Certificate"]);
   await BrowserTestUtils.closeWindow(win);
 });
 
 add_task(async function testCodeSignEndEntity() {
   let cert = await readCertificate("code-ee.pem", ",,");
   let win = await displayCertificate(cert);
-  checkUsages(win, ["Object Signer"]);
+  checkError(win, "Could not verify this certificate for unknown reasons.");
   await BrowserTestUtils.closeWindow(win);
 });
 
 add_task(async function testExpired() {
   let cert = await readCertificate("expired-ca.pem", ",,");
   let win = await displayCertificate(cert);
   checkError(win, "Could not verify this certificate because it has expired.");
   await BrowserTestUtils.closeWindow(win);
-});
 
-add_task(async function testIssuerExpired() {
-  let cert = await readCertificate("ee-from-expired-ca.pem", ",,");
-  let win = await displayCertificate(cert);
-  checkError(win,
+  // These tasks may run in any order, so we run this additional testcase in the
+  // same task.
+  let eeCert = await readCertificate("ee-from-expired-ca.pem", ",,");
+  let eeWin = await displayCertificate(eeCert);
+  checkError(eeWin,
              "Could not verify this certificate because the CA certificate " +
              "is invalid.");
-  await BrowserTestUtils.closeWindow(win);
+  await BrowserTestUtils.closeWindow(eeWin);
 });
 
 add_task(async function testUnknownIssuer() {
   let cert = await readCertificate("unknown-issuer.pem", ",,");
   let win = await displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because the issuer is " +
              "unknown.");
@@ -79,25 +79,25 @@ add_task(async function testInsecureAlgo
 });
 
 add_task(async function testUntrusted() {
   let cert = await readCertificate("untrusted-ca.pem", "p,p,p");
   let win = await displayCertificate(cert);
   checkError(win,
              "Could not verify this certificate because it is not trusted.");
   await BrowserTestUtils.closeWindow(win);
-});
 
-add_task(async function testUntrustedIssuer() {
-  let cert = await readCertificate("ee-from-untrusted-ca.pem", ",,");
-  let win = await displayCertificate(cert);
-  checkError(win,
+  // These tasks may run in any order, so we run this additional testcase in the
+  // same task.
+  let eeCert = await readCertificate("ee-from-untrusted-ca.pem", ",,");
+  let eeWin = await displayCertificate(eeCert);
+  checkError(eeWin,
              "Could not verify this certificate because the issuer is not " +
              "trusted.");
-  await BrowserTestUtils.closeWindow(win);
+  await BrowserTestUtils.closeWindow(eeWin);
 });
 
 add_task(async function testRevoked() {
   // Note that there's currently no way to un-do this. This should only be a
   // problem if another test re-uses a certificate with this same key (perhaps
   // likely) and subject (less likely).
   let certBlocklist = Cc["@mozilla.org/security/certblocklist;1"]
                         .getService(Ci.nsICertBlocklist);
@@ -105,17 +105,17 @@ add_task(async function testRevoked() {
     "MBIxEDAOBgNVBAMMB3Jldm9rZWQ=", // CN=revoked
     "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8="); // hash of the shared key
   let cert = await readCertificate("revoked.pem", ",,");
   let win = await displayCertificate(cert);
   // As of bug 1312827, OneCRL only applies to TLS web server certificates, so
   // this certificate will actually verify successfully for every end-entity
   // usage except TLS web server.
   checkUsages(win, ["Email Recipient Certificate", "Email Signer Certificate",
-                    "Object Signer", "SSL Client Certificate"]);
+                    "SSL Client Certificate"]);
   await BrowserTestUtils.closeWindow(win);
 });
 
 add_task(async function testInvalid() {
   // This certificate has a keyUsage extension asserting cRLSign and
   // keyCertSign, but it doesn't have a basicConstraints extension. This
   // shouldn't be valid for any usage. Sadly, we give a pretty lame error
   // message in this case.
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -85,28 +85,26 @@ const MOZILLA_PKIX_ERROR_REQUIRED_TLS_FE
 const MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME              = MOZILLA_PKIX_ERROR_BASE + 12;
 
 // Supported Certificate Usages
 const certificateUsageSSLClient              = 0x0001;
 const certificateUsageSSLServer              = 0x0002;
 const certificateUsageSSLCA                  = 0x0008;
 const certificateUsageEmailSigner            = 0x0010;
 const certificateUsageEmailRecipient         = 0x0020;
-const certificateUsageObjectSigner           = 0x0040;
 
 // A map from the name of a certificate usage to the value of the usage.
 // Useful for printing debugging information and for enumerating all supported
 // usages.
 const allCertificateUsages = {
   certificateUsageSSLClient,
   certificateUsageSSLServer,
   certificateUsageSSLCA,
   certificateUsageEmailSigner,
   certificateUsageEmailRecipient,
-  certificateUsageObjectSigner,
 };
 
 const NO_FLAGS = 0;
 
 // Commonly certificates are represented as PEM. The format is roughly as
 // follows:
 //
 // -----BEGIN CERTIFICATE-----
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -29,11 +29,12 @@ TEST_DIRS += [
     'test_keysize',
     'test_keysize_ev',
     'test_missing_intermediate',
     'test_name_constraints',
     'test_ocsp_fetch_method',
     'test_ocsp_url',
     'test_onecrl',
     'test_pinning_dynamic',
+    'test_signed_apps',
     'test_startcom_wosign',
     'test_validity',
 ]
new file mode 100755
--- /dev/null
+++ b/security/manager/ssl/tests/unit/pycms.py
@@ -0,0 +1,184 @@
+#!/usr/bin/env 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/.
+
+"""
+Reads a specification from stdin and outputs a PKCS7 (CMS) message with
+the desired properties.
+
+The specification format is as follows:
+
+hash:<hex string>
+signer:
+<pycert specification>
+
+hash is the value that will be put in the messageDigest attribute in
+each SignerInfo of the signerInfos field of the SignedData.
+The certificate specification must come last.
+Currently only SHA-1 is supported.
+"""
+
+from pyasn1.codec.der import decoder
+from pyasn1.codec.der import encoder
+from pyasn1.type import tag, univ
+from pyasn1_modules import rfc2315, rfc2459
+import StringIO
+import base64
+import pycert
+import pykey
+import sys
+
+class Error(Exception):
+    """Base class for exceptions in this module."""
+    pass
+
+
+class UnknownDirectiveError(Error):
+    """Helper exception type to handle unknown specification
+    directives."""
+
+    def __init__(self, directive):
+        super(UnknownDirectiveError, self).__init__()
+        self.directive = directive
+
+    def __str__(self):
+        return 'Unknown directive %s' % repr(self.directive)
+
+
+class CMS(object):
+    """Utility class for reading a CMS specification and
+    generating a CMS message"""
+
+    def __init__(self, paramStream):
+        self.hash = ''
+        signerSpecification = StringIO.StringIO()
+        readingSignerSpecification = False
+        for line in paramStream.readlines():
+            if readingSignerSpecification:
+                print >>signerSpecification, line.strip()
+            elif line.strip() == 'signer:':
+                readingSignerSpecification = True
+            elif line.startswith('hash:'):
+                self.hash = line.strip()[len('hash:'):]
+            else:
+                raise UnknownDirectiveError(line.strip())
+        signerSpecification.seek(0)
+        self.signer = pycert.Certificate(signerSpecification)
+        self.signingKey = pykey.keyFromSpecification('default')
+
+    def buildAuthenticatedAttributes(self, value, implicitTag=None):
+        """Utility function to build a pyasn1 AuthenticatedAttributes
+        object. Useful because when building a SignerInfo, the
+        authenticatedAttributes needs to be tagged implicitly, but when
+        signing an AuthenticatedAttributes, it needs the explicit SET
+        tag."""
+        if implicitTag:
+            authenticatedAttributes = rfc2315.Attributes().subtype(implicitTag=implicitTag)
+        else:
+            authenticatedAttributes = rfc2315.Attributes()
+        contentTypeAttribute = rfc2315.Attribute()
+        # PKCS#9 contentType
+        contentTypeAttribute['type'] = univ.ObjectIdentifier('1.2.840.113549.1.9.3')
+        contentTypeAttribute['values'] = univ.SetOf(rfc2459.AttributeValue())
+        # PKCS#7 data
+        contentTypeAttribute['values'][0] = univ.ObjectIdentifier('1.2.840.113549.1.7.1')
+        authenticatedAttributes[0] = contentTypeAttribute
+        hashAttribute = rfc2315.Attribute()
+        # PKCS#9 messageDigest
+        hashAttribute['type'] = univ.ObjectIdentifier('1.2.840.113549.1.9.4')
+        hashAttribute['values'] = univ.SetOf(rfc2459.AttributeValue())
+        hashAttribute['values'][0] = univ.OctetString(hexValue=value)
+        authenticatedAttributes[1] = hashAttribute
+        return authenticatedAttributes
+
+    def pykeyHashToDigestAlgorithm(self, pykeyHash):
+        """Given a pykey hash algorithm identifier, builds an
+        AlgorithmIdentifier for use with pyasn1."""
+        if pykeyHash == pykey.HASH_SHA1:
+            algorithmIdentifier = rfc2459.AlgorithmIdentifier()
+            algorithmIdentifier['algorithm'] = univ.ObjectIdentifier('1.3.14.3.2.26')
+            algorithmIdentifier['parameters'] = univ.Null()
+            return algorithmIdentifier
+        raise pykey.UnknownHashAlgorithmError(pykeyHash)
+
+    def buildSignerInfo(self, certificate, pykeyHash, digestValue):
+        """Given a pyasn1 certificate, a pykey hash identifier
+        and a hash value, creates a SignerInfo with the
+        appropriate values."""
+        signerInfo = rfc2315.SignerInfo()
+        signerInfo['version'] = 1
+        issuerAndSerialNumber = rfc2315.IssuerAndSerialNumber()
+        issuerAndSerialNumber['issuer'] = self.signer.getIssuer()
+        issuerAndSerialNumber['serialNumber'] = certificate['tbsCertificate']['serialNumber']
+        signerInfo['issuerAndSerialNumber'] = issuerAndSerialNumber
+        signerInfo['digestAlgorithm'] = self.pykeyHashToDigestAlgorithm(pykeyHash)
+        rsa = rfc2459.AlgorithmIdentifier()
+        rsa['algorithm'] = rfc2459.rsaEncryption
+        rsa['parameters'] = univ.Null()
+        authenticatedAttributes = self.buildAuthenticatedAttributes(digestValue,
+          implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
+        authenticatedAttributesTBS = self.buildAuthenticatedAttributes(digestValue)
+        signerInfo['authenticatedAttributes'] = authenticatedAttributes
+        signerInfo['digestEncryptionAlgorithm'] = rsa
+        authenticatedAttributesEncoded = encoder.encode(authenticatedAttributesTBS)
+        signature = self.signingKey.sign(authenticatedAttributesEncoded, pykeyHash)
+        # signature will be a hexified bit string of the form
+        # "'<hex bytes>'H". For some reason that's what BitString wants,
+        # but since this is an OCTET STRING, we have to strip off the
+        # quotation marks and trailing "H".
+        signerInfo['encryptedDigest'] = univ.OctetString(hexValue=signature[1:-2])
+        return signerInfo
+
+    def toDER(self):
+        contentInfo = rfc2315.ContentInfo()
+        contentInfo['contentType'] = rfc2315.signedData
+
+        signedData = rfc2315.SignedData()
+        signedData['version'] = rfc2315.Version(1)
+
+        digestAlgorithms = rfc2315.DigestAlgorithmIdentifiers()
+        digestAlgorithms[0] = self.pykeyHashToDigestAlgorithm(pykey.HASH_SHA1)
+        signedData['digestAlgorithms'] = digestAlgorithms
+
+        dataContentInfo = rfc2315.ContentInfo()
+        dataContentInfo['contentType'] = rfc2315.data
+        signedData['contentInfo'] = dataContentInfo
+
+        certificates = rfc2315.ExtendedCertificatesAndCertificates().subtype(
+            implicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
+        extendedCertificateOrCertificate = rfc2315.ExtendedCertificateOrCertificate()
+        certificate = decoder.decode(self.signer.toDER(),
+            asn1Spec=rfc2459.Certificate())[0]
+        extendedCertificateOrCertificate['certificate'] = certificate
+        certificates[0] = extendedCertificateOrCertificate
+        signedData['certificates'] = certificates
+
+        signerInfos = rfc2315.SignerInfos()
+
+        signerInfos[0] = self.buildSignerInfo(certificate, pykey.HASH_SHA1, self.hash)
+        signedData['signerInfos'] = signerInfos
+
+        encoded = encoder.encode(signedData)
+        anyTag = univ.Any(encoded).subtype(
+            explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 0))
+
+        contentInfo['content'] = anyTag
+        return encoder.encode(contentInfo)
+
+    def toPEM(self):
+        output = '-----BEGIN PKCS7-----'
+        der = self.toDER()
+        b64 = base64.b64encode(der)
+        while b64:
+            output += '\n' + b64[:64]
+            b64 = b64[64:]
+        output += '\n-----END PKCS7-----'
+        return output
+
+
+# When run as a standalone program, this will read a specification from
+# stdin and output the certificate as PEM to stdout.
+if __name__ == '__main__':
+    print CMS(sys.stdin).toPEM()
old mode 100644
new mode 100755
rename from security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py
rename to security/manager/ssl/tests/unit/sign_app.py
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/sign_b2g_app.py
+++ b/security/manager/ssl/tests/unit/sign_app.py
@@ -1,176 +1,85 @@
-# flake8: noqa
+#!/usr/bin/env 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/.
 
-import argparse
+"""
+Given a directory of files, packages them up and signs the
+resulting zip file. Mainly for creating test inputs to the
+nsIX509CertDB.openSignedAppFileAsync API.
+"""
 from base64 import b64encode
 from hashlib import sha1
-import sys
+import StringIO
+import argparse
+import os
+import pycms
+import re
 import zipfile
-import ctypes
-
-import nss_ctypes
-
-# Change the limits in JarSignatureVerification.cpp when you change the limits
-# here.
-max_entry_uncompressed_len = 100 * 1024 * 1024
-max_total_uncompressed_len = 500 * 1024 * 1024
-max_entry_count = 100 * 1000
-max_entry_filename_len = 1024
-max_mf_len = max_entry_count * 50
-max_sf_len = 1024
-
-
-
-def nss_load_cert(nss_db_dir, nss_password, cert_nickname):
-  nss_ctypes.NSS_Init(nss_db_dir)
-  try:
-    wincx = nss_ctypes.SetPasswordContext(nss_password)
-    cert = nss_ctypes.PK11_FindCertFromNickname(cert_nickname, wincx)
-    return (wincx, cert)
-  except:
-    nss_ctypes.NSS_Shutdown()
-    raise
 
-def nss_create_detached_signature(cert, dataToSign, wincx):
-  certdb = nss_ctypes.CERT_GetDefaultCertDB()
-  p7 = nss_ctypes.SEC_PKCS7CreateSignedData(cert,
-                                            nss_ctypes.certUsageObjectSigner,
-                                            certdb,
-                                            nss_ctypes.SEC_OID_SHA1,
-                                            sha1(dataToSign).digest(),
-                                            wincx       )
-  try:
-    nss_ctypes.SEC_PKCS7AddSigningTime(p7)
-    nss_ctypes.SEC_PKCS7IncludeCertChain(p7, wincx)
-    return nss_ctypes.SEC_PKCS7Encode(p7, None, wincx)
-  finally:
-    nss_ctypes.SEC_PKCS7DestroyContentInfo(p7)
-
-# We receive a ids_json string for the toBeSigned app
-def sign_zip(in_zipfile_name, out_zipfile_name, cert, wincx, ids_json):
-  mf_entries = []
-  seen_entries = set()
+def walkDirectory(directory):
+    """Given a relative path to a directory, enumerates the
+    files in the tree rooted at that location. Returns a list
+    of pairs of paths to those files. The first in each pair
+    is the full path to the file. The second in each pair is
+    the path to the file relative to the directory itself."""
+    paths = []
+    for path, dirs, files in os.walk(directory):
+        for f in files:
+            fullPath = os.path.join(path, f)
+            internalPath = re.sub(r'^/', '', fullPath.replace(directory, ''))
+            paths.append((fullPath, internalPath))
+    return paths
 
-  total_uncompressed_len = 0
-  entry_count = 0
-  with zipfile.ZipFile(out_zipfile_name, 'w') as out_zip:
-    with zipfile.ZipFile(in_zipfile_name, 'r') as in_zip:
-      for entry_info in in_zip.infolist():
-        name = entry_info.filename
+def signZip(appDirectory, outputFile, issuerName, doSign):
+    """Given a directory containing the files to package up,
+    an output filename to write to, the name of the issuer of
+    the signing certificate, and whether or not to actually
+    sign the resulting package, packages up the files in the
+    directory and creates the output as appropriate."""
+    mfEntries = []
 
-        # Check for reserved and/or insane (potentially malicious) names
-        if name.endswith("/"):
-          pass
-          # Do nothing; we don't copy directory entries since they are just a
-          # waste of space.
-        elif name.lower().startswith("meta-inf/"):
-          # META-INF/* is reserved for our use
-          raise ValueError("META-INF entries are not allowed: %s" % (name))
-        elif len(name) > max_entry_filename_len:
-          raise ValueError("Entry's filename is too long: %s" % (name))
-        # TODO: elif name has invalid characters...
-        elif name in seen_entries:
-          # It is possible for a zipfile to have duplicate entries (with the exact
-          # same filenames). Python's zipfile module accepts them, but our zip
-          # reader in Gecko cannot do anything useful with them, and there's no
-          # sane reason for duplicate entries to exist, so reject them.
-          raise ValueError("Duplicate entry in input file: %s" % (name))
-        else:
-          entry_count += 1
-          if entry_count > max_entry_count:
-            raise ValueError("Too many entries in input archive")
-
-          seen_entries.add(name)
+    with zipfile.ZipFile(outputFile, 'w') as outZip:
+        for (fullPath, internalPath) in walkDirectory(appDirectory):
+            with open(fullPath) as inputFile:
+                contents = inputFile.read()
+            outZip.writestr(internalPath, contents)
 
-          # Read in the input entry, but be careful to avoid going over the
-          # various limits we have, to minimize the likelihood that we'll run
-          # out of memory. Note that we can't use the length from entry_info
-          # because that might not be accurate if the input zip file is
-          # maliciously crafted to contain misleading metadata.
-          with in_zip.open(name, 'r') as entry_file:
-            contents = entry_file.read(max_entry_uncompressed_len + 1)
-          if len(contents) > max_entry_uncompressed_len:
-            raise ValueError("Entry is too large: %s" % (name))
-          total_uncompressed_len += len(contents)
-          if total_uncompressed_len > max_total_uncompressed_len:
-            raise ValueError("Input archive is too large")
-
-          # Copy the entry, using the same compression as used in the input file
-          out_zip.writestr(entry_info, contents)
+            # Add the entry to the manifest we're building
+            base64hash = b64encode(sha1(contents).digest())
+            mfEntries.append(
+                'Name: %s\nSHA1-Digest: %s\n' % (internalPath, base64hash))
 
-          # Add the entry to the manifest we're building
-          mf_entries.append('Name: %s\nSHA1-Digest: %s\n'
-                                % (name, b64encode(sha1(contents).digest())))
-    if (ids_json):
-      mf_entries.append('Name: %s\nSHA1-Digest: %s\n'
-                        % ("META-INF/ids.json", b64encode(sha1(ids_json).digest())))
-
-    mf_contents = 'Manifest-Version: 1.0\n\n' + '\n'.join(mf_entries)
-    if len(mf_contents) > max_mf_len:
-      raise ValueError("Generated MANIFEST.MF is too large: %d" % (len(mf_contents)))
-
-    sf_contents = ('Signature-Version: 1.0\nSHA1-Digest-Manifest: %s\n'
-                                % (b64encode(sha1(mf_contents).digest())))
-    if len(sf_contents) > max_sf_len:
-      raise ValueError("Generated SIGNATURE.SF is too large: %d"
-                          % (len(mf_contents)))
-
-    p7 = nss_create_detached_signature(cert, sf_contents, wincx)
-
-    # write the signature, SF, and MF
-    out_zip.writestr("META-INF/A.RSA", p7, zipfile.ZIP_DEFLATED)
-    out_zip.writestr("META-INF/A.SF",  sf_contents, zipfile.ZIP_DEFLATED)
-    out_zip.writestr("META-INF/MANIFEST.MF", mf_contents, zipfile.ZIP_DEFLATED)
-    if (ids_json):
-        out_zip.writestr("META-INF/ids.json", ids_json, zipfile.ZIP_DEFLATED)
+        # Just exit early if we're not actually signing.
+        if not doSign:
+            return
 
-def main():
-  parser = argparse.ArgumentParser(description='Sign a B2G app.')
-  parser.add_argument('-d', action='store',
-                            required=True, help='NSS database directory')
-  parser.add_argument('-f', action='store',
-                            type=argparse.FileType('rb'),
-                            required=True, help='password file')
-  parser.add_argument('-k', action='store',
-                            required=True, help="nickname of signing cert.")
-  parser.add_argument('-i', action='store', type=argparse.FileType('rb'),
-                            required=True, help="input JAR file (unsigned)")
-  parser.add_argument('-o', action='store', type=argparse.FileType('wb'),
-                            required=True, help="output JAR file (signed)")
-  parser.add_argument('-I', '--ids-file', action='store', type=argparse.FileType('rb'),
-                     help="Path to the ids.json file", dest='I')
-  parser.add_argument('-S', '--storeId', action='store',
-                      help="Store Id for the package", dest='S')
-  parser.add_argument('-V', '--storeVersion', action='store', type=int,
-                      help="Package Version", dest='V')
-  args = parser.parse_args()
-
-  # Sadly nested groups and neccesarily inclusive groups (http://bugs.python.org/issue11588)
-  # are not implemented. Note that this means the automatic help is slighty incorrect
-  if not((not args.I and args.V and args.S) or (args.I and not args.V and not args.S)):
-      raise ValueError("Either -I or -S and -V must be specified")
+        mfContents = 'Manifest-Version: 1.0\n\n' + '\n'.join(mfEntries)
+        base64hash = b64encode(sha1(mfContents).digest())
+        sfContents = 'Signature-Version: 1.0\nSHA1-Digest-Manifest: %s\n' % base64hash
+        cmsSpecification = 'hash:%s\nsigner:\n' % sha1(sfContents).hexdigest() + \
+            'issuer:%s\n' % issuerName + \
+            'subject:xpcshell signed app test signer\n' + \
+            'extension:keyUsage:digitalSignature'
+        cmsSpecificationStream = StringIO.StringIO()
+        print >>cmsSpecificationStream, cmsSpecification
+        cmsSpecificationStream.seek(0)
+        cms = pycms.CMS(cmsSpecificationStream)
+        p7 = cms.toDER()
+        outZip.writestr('META-INF/A.RSA', p7)
+        outZip.writestr('META-INF/A.SF', sfContents)
+        outZip.writestr('META-INF/MANIFEST.MF', mfContents)
 
-  if (args.I):
-    ids_contents = args.I.read(max_entry_uncompressed_len+1)
-  else:
-    ids_contents = '''{
-  "id": "%(id)s",
-  "version": %(version)d
-}
-''' % {"id": args.S, "version": args.V}
-  if len(ids_contents) > max_entry_uncompressed_len:
-    raise ValueError("Entry is too large: %s" % (name))
-
-  db_dir = args.d
-  password = args.f.readline().strip()
-  cert_nickname = args.k
-
-  (wincx, cert) = nss_load_cert(db_dir, password, cert_nickname)
-  try:
-    sign_zip(args.i, args.o, cert, wincx, ids_contents)
-    return 0
-  finally:
-    nss_ctypes.CERT_DestroyCertificate(cert)
-    nss_ctypes.NSS_Shutdown()
-
-if __name__ == "__main__":
-    sys.exit(main())
+def main(outputFile, appPath, *args):
+    """Main entrypoint. Given an already-opened file-like
+    object, a path to the app directory to sign, and some
+    optional arguments, signs the contents of the directory and
+    writes the resulting package to the 'file'."""
+    parser = argparse.ArgumentParser(description='Sign an app.')
+    parser.add_argument('-n', '--no-sign', action='store_true',
+                        help='Don\'t actually sign - only create zip')
+    parser.add_argument('-i', '--issuer', action='store', help='Issuer name',
+                        default='xpcshell signed apps test root')
+    parsed = parser.parse_args(args)
+    signZip(appPath, outputFile, parsed.issuer, not parsed.no_sign)
--- a/security/manager/ssl/tests/unit/test_cert_keyUsage.js
+++ b/security/manager/ssl/tests/unit/test_cert_keyUsage.js
@@ -11,18 +11,18 @@ var certdb = Cc["@mozilla.org/security/x
 
 const caList = [ "ca-no-keyUsage-extension", "ca-missing-keyCertSign",
                  "ca-all-usages" ];
 const eeList = [ "ee-no-keyUsage-extension", "ee-keyCertSign-only",
                  "ee-keyEncipherment-only", "ee-keyCertSign-and-keyEncipherment" ];
 
 const caUsage = [ certificateUsageSSLCA ];
 const allEEUsages = [ certificateUsageSSLClient, certificateUsageSSLServer,
-                      certificateUsageEmailSigner, certificateUsageEmailRecipient,
-                      certificateUsageObjectSigner ];
+                      certificateUsageEmailSigner,
+                      certificateUsageEmailRecipient ];
 const serverEEUsages = [ certificateUsageSSLServer,
                          certificateUsageEmailRecipient ];
 
 const expectedUsagesMap = {
   "ca-no-keyUsage-extension": caUsage,
   "ca-missing-keyCertSign": [],
   "ca-all-usages": caUsage,
 
--- a/security/manager/ssl/tests/unit/test_cert_trust.js
+++ b/security/manager/ssl/tests/unit/test_cert_trust.js
@@ -31,34 +31,30 @@ function test_ca_distrust(ee_cert, cert_
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
-                        certificateUsageObjectSigner);
 
 
   // Test of active distrust. No usage should pass.
   setCertTrust(cert_to_modify_trust, "p,p,p");
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
-                        certificateUsageObjectSigner);
 
   // Trust set to T  -  trusted CA to issue client certs, where client cert is
   // usageSSLClient.
   setCertTrust(cert_to_modify_trust, "T,T,T");
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageSSLServer);
 
@@ -71,69 +67,60 @@ function test_ca_distrust(ee_cert, cert_
                         certificateUsageSSLCA);
 
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
-                                                  : PRErrorCodeSuccess,
-                        certificateUsageObjectSigner);
 
 
   // Now tests on the SSL trust bit
   setCertTrust(cert_to_modify_trust, "p,C,C");
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLServer);
 
   // XXX(Bug 982340)
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
-                        certificateUsageObjectSigner);
 
   // Inherited trust SSL
   setCertTrust(cert_to_modify_trust, ",C,C");
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   // XXX(Bug 982340)
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
-                        certificateUsageObjectSigner);
 
   // Now tests on the EMAIL trust bit
   setCertTrust(cert_to_modify_trust, "C,p,C");
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNTRUSTED_ISSUER,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
-                        certificateUsageObjectSigner);
 
 
   // inherited EMAIL Trust
   setCertTrust(cert_to_modify_trust, "C,,C");
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
@@ -141,18 +128,16 @@ function test_ca_distrust(ee_cert, cert_
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_CA_CERT_INVALID,
                         certificateUsageSSLCA);
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, isRootCA ? SEC_ERROR_UNKNOWN_ISSUER
                                                   : PRErrorCodeSuccess,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
-                        certificateUsageObjectSigner);
 }
 
 
 function run_test() {
   let certList = [
     "ca",
     "int",
     "ee",
@@ -186,24 +171,20 @@ function run_test() {
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, SEC_ERROR_UNKNOWN_ISSUER,
-                        certificateUsageObjectSigner);
 
   // Now make a CA trust anchor available.
   setCertTrust(ca_cert, "CTu,CTu,CTu");
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLServer);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageSSLClient);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailSigner);
   checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
                         certificateUsageEmailRecipient);
-  checkCertErrorGeneric(certdb, ee_cert, PRErrorCodeSuccess,
-                        certificateUsageObjectSigner);
 }
--- a/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
+++ b/security/manager/ssl/tests/unit/test_intermediate_basic_usage_constraints.js
@@ -20,18 +20,18 @@ function test_cert_for_usages(certChainN
   }
 
   let cert = certs[0];
   return asyncTestCertificateUsages(certdb, cert, expected_usages);
 }
 
 add_task(async function() {
   let ee_usages = [ certificateUsageSSLClient, certificateUsageSSLServer,
-                    certificateUsageEmailSigner, certificateUsageEmailRecipient,
-                    certificateUsageObjectSigner ];
+                    certificateUsageEmailSigner,
+                    certificateUsageEmailRecipient ];
   let ca_usages = [ certificateUsageSSLCA ];
   let eku_usages = [ certificateUsageSSLClient, certificateUsageSSLServer ];
 
   // Load the ca into mem
   let ca_name = "ca";
   load_cert(ca_name, "CTu,CTu,CTu");
   await test_cert_for_usages([ca_name], ca_usages);
 
--- a/security/manager/ssl/tests/unit/test_nss_shutdown.js
+++ b/security/manager/ssl/tests/unit/test_nss_shutdown.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // This test attempts to ensure that PSM doesn't deadlock or crash when shutting
 // down NSS while a background thread is attempting to use NSS.
-// Uses test_signed_apps/valid_app_1.zip from test_signed_apps.js.
+// Uses test_signed_apps/signed_app.zip from test_signed_apps.js.
 
 function startAsyncNSSOperation(certdb, appFile) {
   return new Promise((resolve, reject) => {
     certdb.openSignedAppFileAsync(Ci.nsIX509CertDB.AppXPCShellRoot, appFile,
       function(rv, aZipReader, aSignerCert) {
         // rv will either indicate success (if NSS hasn't been shut down yet) or
         // it will be some error code that varies depending on when NSS got shut
         // down. As such, there's nothing really to check here. Just resolve the
@@ -23,17 +23,17 @@ function startAsyncNSSOperation(certdb, 
 
 add_task(async function() {
   do_get_profile();
   let psm = Cc["@mozilla.org/psm;1"]
               .getService(Ci.nsISupports)
               .QueryInterface(Ci.nsIObserver);
   let certdb = Cc["@mozilla.org/security/x509certdb;1"]
                  .getService(Ci.nsIX509CertDB);
-  let appFile = do_get_file("test_signed_apps/valid_app_1.zip");
+  let appFile = do_get_file("test_signed_apps/signed_app.zip");
 
   let promises = [];
   for (let i = 0; i < 25; i++) {
     promises.push(startAsyncNSSOperation(certdb, appFile));
   }
   // Trick PSM into thinking it should shut down NSS. If this test doesn't
   // hang or crash, we're good.
   psm.observe(null, "profile-before-change", null);
--- a/security/manager/ssl/tests/unit/test_signed_apps.js
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -131,95 +131,95 @@ function original_app_path(test_name) {
 }
 
 function tampered_app_path(test_name) {
   return FileUtils.getFile("TmpD", ["test_signed_app-" + test_name + ".zip"]);
 }
 
 add_test(function () {
   certdb.openSignedAppFileAsync(
-    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
+    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("signed_app"),
     check_open_result("valid", Cr.NS_OK));
 });
 
 add_test(function () {
   certdb.openSignedAppFileAsync(
-    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned_app_1"),
+    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unsigned_app"),
     check_open_result("unsigned", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
 });
 
 add_test(function () {
   certdb.openSignedAppFileAsync(
-    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer_app_1"),
+    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("unknown_issuer_app"),
     check_open_result("unknown_issuer",
                       getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER)));
 });
 
 // Sanity check to ensure a no-op tampering gives a valid result
 add_test(function () {
   let tampered = tampered_app_path("identity_tampering");
-  tamper(original_app_path("valid_app_1"), tampered, { }, []);
+  tamper(original_app_path("signed_app"), tampered, { }, []);
   certdb.openSignedAppFileAsync(
-    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("valid_app_1"),
+    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("signed_app"),
     check_open_result("identity_tampering", Cr.NS_OK));
 });
 
 add_test(function () {
   let tampered = tampered_app_path("missing_rsa");
-  tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.RSA": removeEntry }, []);
+  tamper(original_app_path("signed_app"), tampered, { "META-INF/A.RSA": removeEntry }, []);
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
     check_open_result("missing_rsa", Cr.NS_ERROR_SIGNED_JAR_NOT_SIGNED));
 });
 
 add_test(function () {
   let tampered = tampered_app_path("missing_sf");
-  tamper(original_app_path("valid_app_1"), tampered, { "META-INF/A.SF": removeEntry }, []);
+  tamper(original_app_path("signed_app"), tampered, { "META-INF/A.SF": removeEntry }, []);
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
     check_open_result("missing_sf", Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
 });
 
 add_test(function () {
   let tampered = tampered_app_path("missing_manifest_mf");
-  tamper(original_app_path("valid_app_1"), tampered, { "META-INF/MANIFEST.MF": removeEntry }, []);
+  tamper(original_app_path("signed_app"), tampered, { "META-INF/MANIFEST.MF": removeEntry }, []);
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
     check_open_result("missing_manifest_mf",
                       Cr.NS_ERROR_SIGNED_JAR_MANIFEST_INVALID));
 });
 
 add_test(function () {
   let tampered = tampered_app_path("missing_entry");
-  tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp": removeEntry }, []);
+  tamper(original_app_path("signed_app"), tampered, { "manifest.json": removeEntry }, []);
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
     check_open_result("missing_entry", Cr.NS_ERROR_SIGNED_JAR_ENTRY_MISSING));
 });
 
 add_test(function () {
   let tampered = tampered_app_path("truncated_entry");
-  tamper(original_app_path("valid_app_1"), tampered, { "manifest.webapp": truncateEntry }, []);
+  tamper(original_app_path("signed_app"), tampered, { "manifest.json": truncateEntry }, []);
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
     check_open_result("truncated_entry", Cr.NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY));
 });
 
 add_test(function () {
   let tampered = tampered_app_path("unsigned_entry");
-  tamper(original_app_path("valid_app_1"), tampered, {},
+  tamper(original_app_path("signed_app"), tampered, {},
     [ { "name": "unsigned.txt", "content": "unsigned content!" } ]);
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
     check_open_result("unsigned_entry", Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
 });
 
 add_test(function () {
   let tampered = tampered_app_path("unsigned_metainf_entry");
-  tamper(original_app_path("valid_app_1"), tampered, {},
+  tamper(original_app_path("signed_app"), tampered, {},
     [ { name: "META-INF/unsigned.txt", content: "unsigned content!" } ]);
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot, tampered,
     check_open_result("unsigned_metainf_entry",
                       Cr.NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY));
 });
 
 // TODO: tampered MF, tampered SF
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app/README
@@ -0,0 +1,1 @@
+This is the readme for the test extension.
copy from dom/manifest/test/blue-150.png
copy to security/manager/ssl/tests/unit/test_signed_apps/app/data/image.png
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/app/manifest.json
@@ -0,0 +1,5 @@
+{
+  "manifest_version": 2,
+  "name": "Test Extension",
+  "version": "0.0.1"
+}
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/README.md
+++ /dev/null
@@ -1,18 +0,0 @@
-This file contains the scripts and binary files needed to regenerate the signed
-files used on the signed apps test.
-
-Prerequisites:
-
-* NSS 3.4 or higher.
-* Python 2.7 (should work with 2.6 also)
-* Bash
-
-Usage:
-
-Run
-
-./create_test_files.sh
-
-The new test files will be created at the ./testApps directory. Just copy the
-contents of this directory to the test directory (dom/apps/tests/signed and
-security/manager/ssl/tests/unit) to use the new files.
deleted file mode 100755
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/create_test_files.sh
+++ /dev/null
@@ -1,213 +0,0 @@
-#!/bin/bash
-
-export NSS_DEFAULT_DB_TYPE=sql
-
-export BASE_PATH=`dirname $0`
-export SIGN_SCR_LOC=.
-export APPS_TEST_LOC=../../../../../../../dom/apps/tests/signed
-export TOOLKIT_WEBAPPS_TEST_LOC=../../../../../../../toolkit/webapps/tests/data/
-
-# Creates the entry zip files (unsigned apps) from the source directories
-packageApps() {
-APPS="unsigned_app_1 unsigned_app_origin unsigned_app_origin_toolkit_webapps"
-OLD_PWD=`pwd`
-cd ${BASE_PATH}
-for i in $APPS
-do
-  echo "Creating $i.zip"
-  cd $i && zip -r ../$i.zip . && cd ..
-done
-cd ${OLD_PWD}
-}
-
-
-# Function to create a signing database
-# Parameters:
-# $1: Output directory (where the DB will be created)
-createDb() {
-
-  db=$1
-
-  mkdir -p $db
-
-  # Insecure by design, so... please don't use this for anything serious
-  passwordfile=$db/passwordfile
-
-  echo insecurepassword > $passwordfile
-  certutil -d $db -N -f $passwordfile 2>&1 >/dev/null
-
-}
-
-# Add a CA cert and a signing cert to the database
-# Arguments:
-#   $1: DB directory
-#   $2: CA CN (don't include the CN=, just the value)
-#   $3: Signing Cert CN (don't include the CN=, just the value)
-#   $4: CA short name (don't use spaces!)
-#   $5: Signing Cert short name (don't use spaces!)
-addCerts() {
-  org="O=Examplla Corporation,L=Mountain View,ST=CA,C=US"
-  ca_subj="CN=${2},${org}"
-  ee_subj="CN=${3},${org}"
-
-  noisefile=/tmp/noise.$$
-  head -c 32 /dev/urandom > $noisefile
-
-  ca_responses=/tmp/caresponses.$$
-  ee_responses=/tmp/earesponses
-
-  echo y >  $ca_responses # Is this a CA?
-  echo >>   $ca_responses # Accept default path length constraint (no constraint)
-  echo y >> $ca_responses # Is this a critical constraint?
-  echo n >  $ee_responses # Is this a CA?
-  echo >>   $ee_responses # Accept default path length constraint (no constraint)
-  echo y >> $ee_responses # Is this a critical constraint?
-
-  make_cert="certutil -d $db -f $passwordfile -S -g 2048 -Z SHA256 \
-                    -z $noisefile -y 3 -2 --extKeyUsage critical,codeSigning"
-  $make_cert -v 480 -n ${4}        -m 1 -s "$ca_subj" \
-      --keyUsage critical,certSigning      -t ",,CTu" -x < $ca_responses 2>&1 >/dev/null
-  $make_cert -v 240 -n ${5} -c ${4} -m 2 -s "$ee_subj" \
-      --keyUsage critical,digitalSignature -t ",,,"      < $ee_responses 2>&1 >/dev/null
-
-  # In case we want to inspect the generated certs
-
-  # Also, we'll need this one later on
-  certutil -d $db -L -n ${4} -r -o $db/${4}.der
-  certutil -d $db -L -n ${5} -r -o $db/${5}.der
-
-  rm -f $noisefile $ee_responses $ca_responses
-}
-
-
-# Signs an app
-# Parameters:
-# $1: Database directory
-# $2: Unsigned ZIP file path
-# $3: Signed ZIP file path
-# $4: Store ID for the signed App
-# $5: Version of the signed App
-# $6: Nickname of the signing certificate
-signApp() {
-
-  db=$1
-
-  # Once again, this is INSECURE. It doesn't matter here but
-  # DON'T use this for anything production related
-  passwordfile=$db/passwordfile
-
-  python ${BASE_PATH}/${SIGN_SCR_LOC}/sign_b2g_app.py -d $db -f $passwordfile \
-         -k ${6} -i ${2} -o ${3} -S ${4} -V ${5}
-}
-
-DB_PATH=${BASE_PATH}/signingDB
-TEST_APP_PATH=${BASE_PATH}/testApps
-
-echo "Warning! The directories ${DB_PATH} and ${TEST_APP_PATH} will be erased!"
-echo "Do you want to proceed anyway?"
-select answer in "Yes" "No"
-do
-  case $answer in
-    Yes) break;;
-    No) exit 1;;
-  esac
-done
-
-rm -rf ${DB_PATH} ${TEST_APP_PATH}
-
-TRUSTED_EE=trusted_ee1
-UNTRUSTED_EE=untrusted_ee1
-TRUSTED_CA=trusted_ca1
-UNTRUSTED_CA=untrusted_ca1
-
-# First, we'll create a new couple of signing DBs
-createDb $DB_PATH
-addCerts $DB_PATH "Valid CA" "Store Cert" trusted_ca1 ${TRUSTED_EE}
-addCerts $DB_PATH "Invalid CA" "Invalid Cert" ${UNTRUSTED_CA} ${UNTRUSTED_EE}
-
-# Then we'll create the unsigned apps
-echo "Creating unsigned apps"
-packageApps
-
-# And then we'll create all the test apps...
-mkdir -p ${TEST_APP_PATH}
-
-# We need:
-# A valid signed file, with two different versions:
-#    valid_app_1.zip
-#    valid_app_2.zip
-VALID_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
-        $TEST_APP_PATH/valid_app_1.zip \
-        $VALID_UID 1 ${TRUSTED_EE}
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
-        $TEST_APP_PATH/valid_app_2.zip \
-        $VALID_UID 2 ${TRUSTED_EE}
-
-
-# A corrupt_package:
-#    corrupt_app_1.zip
-# A corrupt package is a package with a entry modified, for example...
-CURDIR=`pwd`
-export TEMP_DIR=$TEST_APP_PATH/aux_unzip_$$
-mkdir -p $TEMP_DIR
-cd  $TEMP_DIR
-unzip ../valid_app_1.zip 2>&1 >/dev/null
-echo " - " >> index.html
-zip -r ../corrupt_app_1.zip * 2>&1 >/dev/null
-cd $CURDIR
-rm -rf $TEMP_DIR
-
-# A file signed by a unknown issuer
-#    unknown_issuer_app_1.zip
-INVALID_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_1.zip \
-        $TEST_APP_PATH/unknown_issuer_app_1.zip \
-        $INVALID_UID 1 ${UNTRUSTED_EE}
-
-# And finally a priviledged signed file that includes the origin on the manifest
-# to avoid that reverting again
-PRIV_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin.zip \
-        $TEST_APP_PATH/origin_app_1.zip \
-        $PRIV_UID 1 ${TRUSTED_EE}
-
-# A privileged signed app needed for a toolkit/webapps test
-PRIV_TOOLKIT_UID=`uuidgen`
-signApp $DB_PATH ${BASE_PATH}/unsigned_app_origin_toolkit_webapps.zip \
-        $TEST_APP_PATH/custom_origin.zip \
-        $PRIV_TOOLKIT_UID 1 ${TRUSTED_EE}
-
-# Now let's copy the trusted cert to the app directory so we have everything
-# on the same place...
-cp ${DB_PATH}/${TRUSTED_CA}.der ${TEST_APP_PATH}
-
-cat <<EOF
-
-All done. The new test files are in ${TEST_APP_PATH}. You should copy the
-contents of that directory to the dom/apps/tests/signed directory and to
-the security/manager/ssl/tests/unit/test_signed_apps (which should be the
-parent of this directory) to install them.
-
-EOF
-
-echo "Do you wish me to do that for you now?"
-select answer in "Yes" "No"
-do
-  case $answer in
-    Yes) break;;
-    No) echo "Ok, not installing the new files"
-        echo "You should run: "
-        echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
-        echo cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
-        echo cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
-        echo "to install them"
-        exit 0;;
-  esac
-done
-
-cp ${TEST_APP_PATH}/* ${BASE_PATH}/${APPS_TEST_LOC}
-cp ${TEST_APP_PATH}/* ${TEST_APP_PATH}/../unsigned_app_1.zip ${BASE_PATH}/..
-cp ${TEST_APP_PATH}/* ${BASE_PATH}/${TOOLKIT_WEBAPPS_TEST_LOC}
-
-echo "Done!"
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/nss_ctypes.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# flake8: noqa
-
-from ctypes import *
-import os
-import sys
-
-if sys.platform == 'darwin':
-  libprefix = "lib"
-  libsuffix = ".dylib"
-elif os.name == 'posix':
-  libprefix = "lib"
-  libsuffix = ".so"
-else: # assume windows
-  libprefix = ""
-  libsuffix = ".dll"
-
-plc   = cdll.LoadLibrary(libprefix + "plc4"   + libsuffix)
-nspr  = cdll.LoadLibrary(libprefix + "nspr4"  + libsuffix)
-nss   = cdll.LoadLibrary(libprefix + "nss3"   + libsuffix)
-smime = cdll.LoadLibrary(libprefix + "smime3" + libsuffix)
-
-nspr.PR_GetError.argtypes = []
-nspr.PR_GetError.restype = c_int32
-nspr.PR_ErrorToName.argtypes = [c_int32]
-nspr.PR_ErrorToName.restype = c_char_p
-
-def raise_if_not_SECSuccess(rv):
-  SECSuccess = 0
-  if (rv != SECSuccess):
-    raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
-
-def raise_if_NULL(p):
-  if not p:
-    raise ValueError(nspr.PR_ErrorToName(nspr.PR_GetError()))
-  return p
-
-PRBool = c_int
-SECStatus = c_int
-
-# from secoidt.h
-SEC_OID_SHA1 = 4
-
-# from certt.h
-certUsageObjectSigner = 6
-
-class SECItem(Structure):
-  _fields_ = [("type", c_int),
-              ("data", c_char_p),
-              ("len", c_uint)]
-
-nss.NSS_Init.argtypes = [c_char_p]
-nss.NSS_Init.restype = SECStatus
-def NSS_Init(db_dir):
-  nss.NSS_Init.argtypes = [c_char_p]
-  nss.NSS_Init.restype = SECStatus
-  raise_if_not_SECSuccess(nss.NSS_Init(db_dir))
-
-nss.NSS_Shutdown.argtypes = []
-nss.NSS_Shutdown.restype = SECStatus
-def NSS_Shutdown():
-  raise_if_not_SECSuccess(nss.NSS_Shutdown())
-
-PK11PasswordFunc = CFUNCTYPE(c_char_p, c_void_p, PRBool, c_char_p)
-
-# pass the result of this as the wincx parameter when a wincx is required
-nss.PK11_SetPasswordFunc.argtypes = [PK11PasswordFunc]
-nss.PK11_SetPasswordFunc.restype = None
-
-# Set the return type as *void so Python doesn't touch it
-plc.PL_strdup.argtypes = [c_char_p]
-plc.PL_strdup.restype = c_void_p
-def SetPasswordContext(password):
-  def callback(slot, retry, arg):
-    return plc.PL_strdup(password)
-  wincx = PK11PasswordFunc(callback)
-  nss.PK11_SetPasswordFunc(wincx)
-  return wincx
-
-nss.CERT_GetDefaultCertDB.argtypes = []
-nss.CERT_GetDefaultCertDB.restype = c_void_p
-def CERT_GetDefaultCertDB():
-  return raise_if_NULL(nss.CERT_GetDefaultCertDB())
-
-nss.PK11_FindCertFromNickname.argtypes = [c_char_p, c_void_p]
-nss.PK11_FindCertFromNickname.restype = c_void_p
-def PK11_FindCertFromNickname(nickname, wincx):
-  return raise_if_NULL(nss.PK11_FindCertFromNickname(nickname, wincx))
-
-nss.CERT_DestroyCertificate.argtypes = [c_void_p]
-nss.CERT_DestroyCertificate.restype = None
-def CERT_DestroyCertificate(cert):
-  nss.CERT_DestroyCertificate(cert)
-
-smime.SEC_PKCS7CreateSignedData.argtypes = [c_void_p, c_int, c_void_p,
-                                            c_int, c_void_p,
-                                            c_void_p, c_void_p]
-smime.SEC_PKCS7CreateSignedData.restype = c_void_p
-def SEC_PKCS7CreateSignedData(cert, certusage, certdb, digestalg, digest, wincx):
-  item = SECItem(0,  c_char_p(digest), len(digest))
-  return raise_if_NULL(smime.SEC_PKCS7CreateSignedData(cert, certusage, certdb,
-                                                       digestalg,
-                                                       pointer(item),
-                                                       None, wincx))
-
-smime.SEC_PKCS7AddSigningTime.argtypes = [c_void_p]
-smime.SEC_PKCS7AddSigningTime.restype = SECStatus
-def SEC_PKCS7AddSigningTime(p7):
-  raise_if_not_SECSuccess(smime.SEC_PKCS7AddSigningTime(p7))
-
-smime.SEC_PKCS7IncludeCertChain.argtypes = [c_void_p, c_void_p]
-smime.SEC_PKCS7IncludeCertChain.restype = SECStatus
-def SEC_PKCS7IncludeCertChain(p7, wincx):
-  raise_if_not_SECSuccess(smime.SEC_PKCS7IncludeCertChain(p7, wincx))
-
-SEC_PKCS7EncoderOutputCallback = CFUNCTYPE(None, c_void_p, c_void_p, c_long)
-smime.SEC_PKCS7Encode.argtypes = [c_void_p, SEC_PKCS7EncoderOutputCallback,
-                                  c_void_p, c_void_p, c_void_p, c_void_p]
-smime.SEC_PKCS7Encode.restype = SECStatus
-def SEC_PKCS7Encode(p7, bulkkey, wincx):
-  outputChunks = []
-  def callback(chunks, data, len):
-    outputChunks.append(string_at(data, len))
-  callbackWrapper = SEC_PKCS7EncoderOutputCallback(callback)
-  raise_if_not_SECSuccess(smime.SEC_PKCS7Encode(p7, callbackWrapper,
-                                                None, None, None, wincx))
-  return "".join(outputChunks)
-
-smime.SEC_PKCS7DestroyContentInfo.argtypes = [c_void_p]
-smime.SEC_PKCS7DestroyContentInfo.restype = None
-def SEC_PKCS7DestroyContentInfo(p7):
-  smime.SEC_PKCS7DestroyContentInfo(p7)
deleted file mode 100644
index d6fd07a4167d2f05a9a13a2eaca6a9981287dfb6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!doctype html>
-<html lang=en>
-<head><meta charset=utf-8><title>Simple App</title></head>
-<body><p>This is a Simple App.</body>
-</html>
-
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_1/manifest.webapp
+++ /dev/null
@@ -1,8 +0,0 @@
-{ "name": "Simple App"
-, "description": "A Simple Open Web App"
-, "launch_path": "tests/dom/apps/tests/signed_app.sjs"
-, "icons": { "128" : "icon-128.png" }
-, "installs_allowed_from": [ "https://marketplace.mozilla.com", "http://mochi.test:8888" ]
-, "version": 1
-, "default_locale": "en-US"
-}
deleted file mode 100644
index d6fd07a4167d2f05a9a13a2eaca6a9981287dfb6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/index.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!doctype html>
-<html lang=en>
-<head><meta charset=utf-8><title>Simple App</title></head>
-<body><p>This is a Simple App.</body>
-</html>
-
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin/manifest.webapp
+++ /dev/null
@@ -1,10 +0,0 @@
-{ "name": "Simple App",
-  "description": "A Simple Open Web App",
-  "type": "privileged",
-  "origin": "app://test.origin.privileged.app",
-  "launch_path": "tests/dom/apps/tests/signed_app.sjs",
-  "icons": { "128" : "icon-128.png" },
-  "installs_allowed_from": [ "https://marketplace.mozilla.com", "http://mochi.test:8888" ],
-  "version": 1,
-  "default_locale": "en-US"
-}
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/index.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
-<title>Test app</title>
-</head>
-<body>
-Test app:
-<iframe src="http://127.0.0.1:8888/chrome/toolkit/webapps/tests/app.sjs?appreq"></iframe>
-</body>
-</html>
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_signed_apps/gentestfiles/unsigned_app_origin_toolkit_webapps/manifest.webapp
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "name": "Custom Origin Test",
-  "version": 1,
-  "size": 777,
-  "package_path": "custom_origin.zip",
-  "launch_path": "/index.html",
-  "origin": "app://test.origin.privileged.app",
-  "type": "privileged"
-}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/moz.build
@@ -0,0 +1,34 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# 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/.
+
+@template
+def SignedAppFile(name, flags):
+    if not CONFIG['COMPILE_ENVIRONMENT']:
+        return
+
+    GENERATED_FILES += [name]
+    props = GENERATED_FILES[name]
+    props.script = '/security/manager/ssl/tests/unit/sign_app.py'
+    props.inputs = ['app/']
+    if flags:
+        props.flags = flags
+    # Turn RELATIVEDIR into list entry: like
+    # 'security/manager/ssl/tests/unit/test_signed_apps' ->
+    # TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_signed_apps.
+    files = TEST_HARNESS_FILES.xpcshell
+    for part in RELATIVEDIR.split('/'):
+        files = files[part]
+    files += ['!%s' % name]
+
+# Temporarily disabled. See bug 1256495.
+#signed_app_files = (
+#    ['signed_app.zip'],
+#    ['unknown_issuer_app.zip', '-i', 'unknown issuer'],
+#    ['unsigned_app.zip', '-n'],
+#)
+#
+#for signed_app_file_params in signed_app_files:
+#    SignedAppFile(signed_app_file_params[0], signed_app_file_params[1:])
deleted file mode 100644
index 3a12106c8fc0ad3f4bccc517eba2a3012ec57103..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
rename from security/manager/ssl/tests/unit/test_signed_apps/valid_app_1.zip
rename to security/manager/ssl/tests/unit/test_signed_apps/signed_app.zip
index 74a35a4208dcee164af51abcdd5715f3d90a67ea..022181dc73b95307ea3d5597b1933fdd52606f34
GIT binary patch
literal 2813
zc$^FHW@Zs#00D#OAaAJ{jZJ<)HVE?qac*K>W?E`-iC$K5eqJ?~f`SrEB)%-Qs5mn}
zPsvKbNCza5mzbLh<S2yzB^6vNN>cN{(qK7+S|tNL13g0}u3D}D6pO!k80u&P*&xgY
z#6hl(F21fI8JWcjKva^Es!)`gn39{Skd|Kr=9Yl0NrhRXhi2UDCQEiPCI$u&<^zhQ
zB$g!VXXYlRr|K2trFRDSx$|;Kae+MP>ERLtq^E%}2gqg{#==Jo42*X@T^vIy=Da<(
zk(0qd;K+vC^)Js0F*X-2jJy4A(*F+*b4JmSMLlu}1xi)>WQ6yC(u=37pUXO@geKG!
zlp(xg&P^5u1`r0NAYfv2)b;dp(|6Ph3U)MTV!6u3snzDu_MMlJooPW6%Q2wPAtpvf
zLv903HdbvuW+o|C1_N${BE|(xOkWI|m_8UZG2U9h%*4pVBqBPea@l9@ptbJ~KVbVf
z!zZ{<_xo2QJ&fE2nuh8Is%*@mEX+J|6$Qz~8L2rr3dNb}d8sK1i3J74kbF>-pI>62
zB*AZFU}$P+Xk=jk22o%E3j-qqQ>cJ}mZ64$8pJqxEXF}h1M`c3&QvnsMYx5Lm4Ug5
zk)Hu5&c)Qk$jGp(BXEUFpV&3Wzj@m_*vtfMZG>;WdSdFe<;~ZO@En!ugU3}@Jh&mh
zcjFwk*?CfJpRymVk1Ea1N-5;1S{k+7wf(!yRh2nRb!VOb$YoxzP|%#bTH&zgliNFe
z4rS-?y%lZZ|5&#+Q)=U7CaZ2O$9KyM9`N(_{!Qc7-k>sRN$t*##L5X9!zb8YvOfA?
zhll(M^UEF1@72X_gl<oZT2;q1LEoxcpn*;0j7-k-wbK%#w?CRDbfDC5G0()iTTXrd
zvsPC1GUxl9;(W_99&WDISIW<PyrpYuqU<}hYpQcsPvr13J1wKaqO)JDTmR|D{+Z&2
zJ2wCGRQ^>Ic*O83^O{={T&8b(nV1<F7#H&!@B(9uIaHRPg@u`ky#Xcqm_gxWW^Lsz
zy;zH3CU?abzEk%z^A~pH*t_-_w8x6(t8`mDEq^jePG#j|cRAhDk^u^LS_+nkUYz&!
z|IKP~u6Coz#~#__-T(AaGU>$gEm;TC6zYQKF<z}&djIs-#Xc^3XHNTbWLBc~k6WMq
zye&VH;ks?NyxB#|k4G=2J@cGwa(v>&t!LsB?KDLa_j7S<QuVqu?O+4%<L2W3d-A>4
zxEpzD36`0?5;)9vNy6!^+OO?74%eTB@J_B!TP{)=HjgWDdieX<ucR$cpZ>OF=gau-
zKLqA?l}Rqycx%B!`RtvOmq;~Tn7Z=S%<Esz*Ir)vHTcqwlIj^JgI(K$4?53G-y*<v
zj>j&-%Yf0aiE$+`(JTdK4g*IL5;TtNfyiA8><uK~fz8Qmh@9CBlo2v4hGHxtKaY1*
zhh^6;h<(0TiK)!Y^IEG>En4zsX<}puSaroW>}iy!ot2UnciP^YUr#;=;Wb^5zg?~R
zS6{GS;J5jV#r!unNcWtYRXJ^aMuhu|uJA7DbN{0jF!mmf*5Pc}t+Q%&TC}g%P0#Yk
zpVHY!W%ph7T3o<%h^b8Yv8m02{S9w-Y}=O~`&#qg^Rv5sv}$=5gc@Abp78ye-Twlk
zoF&s9hA&CIULx7!{^!%G#d9_kYo<0DM;p{_syO!TfVJkWrCBD|UX))<nP6D*zOCli
z`@`K~cf?P9`L#hiZo%PuS7p{5QSlA(3|PBy<&P^%)~EXjh`(RQapPV4Mdm#(7Fl;m
zsp!sDl6<pDwQ>Dw@88?my<NE9Nfj|oYt-MuCY~uh$sM&2OkbD##0$vwf;I<`3c+Bv
zU|^&rmXsEy>V`q8cPj-$Jp-;_4@W~?m&|lvH0t_78w*wnUan@I!7fgw$yrHGhEXPw
zg~{oWX@OBjE}<s2XmvMR^0tF#fou>KLAKP_(a+P(H8@1i*9~Scs?}V6z&41L0!pI=
z**#VYQHdc=X$4UM?m0fjC7GF?r9oK+25#9&=K1BeFzt}02dX}|l28xFEQ^Tz!VFXA
zsFLJx3k#335bgZJOkcP@)HVjHe&bB1JhM`_O!HKCZ~dSgFTV<d(16O2>|pbJkoOsx
zM3`|m!eF3*kpaC)hOQO88HLbV32#)P>p^cTA@rQUXgQ(lMXx0hdbya<>r8aL=!pxV
km!AVIsiA8{Pa+7dEgaYrOn^5lI6T;ZFa;Qn&72?}03}TjumAu6
deleted file mode 100644
index ec137ea14ad68f022490465e2630b52af2b3d3c2..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
rename from security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app_1.zip
rename to security/manager/ssl/tests/unit/test_signed_apps/unknown_issuer_app.zip
index 374e45f6f83d20a0df2b0b64efa906b7f70a84b3..177f545d478d99507224c8e2e31690b68a9d652c
GIT binary patch
literal 2781
zc$^FHW@Zs#0D;djLEcg^8k_upY!K!J;@rf%%(T?v61}YA{Jd%|1qCITNPJmpQE_H|
zo|2V<kq$^AFEKY2$WaObN-DTkl%(c?rNMFtwMqth26~1{T(w*QC>DS7Fx1fovO$;)
zh=W`mU3^_bGBS%5fT$!RRiP*~F(o%uAuYcM%q;;~lM1s&56!sQO_uCpObiSl%m)-p
zNi0d!&&*9sPt_~POYaQubLZuf;sSZn)59eQNKXS{4v@_@jD?RF7#Qz*x;TbZ%z1lm
zBPWA_z>y8N>tCK1Vr(v47<c>Kr2iir=8U2ti+bb|3Y4n$$q4TOr58_EKbLh*2~DUe
zXwRMP20K|87(f`5f`Ez9QP<PYP2W*3DA>`UiDfGrr&gOs+jm|@cBTbQEX#mGi<lT0
z4Y>_C*;uvtn3<$l84S1)iWnC(F+DM8VtQcE#CU1}GZP~dlSu5hJGvi(!f#nQtetW=
zbXJT8^K(BWJ&fE2l7`|2qHN5eEX+K7rFq$T`Q>@Q3{hN~T4bOk!Ea<>XliI^WMKdX
zQD6ZJ10w@dsDOc%p@xAPM7w-NL2_|MYEF(qab|j6YKlT)K>;Kef%!#1XDJ!*BJ5^l
zWngY%<Yxeib1^kBGBWJy2wdUPCw9&8Z{D^JHZuWR8{wO;o|t-VdGj?RJV&Ma;BnOz
z4{pfs-8hGBcAiw*r|d`Tqe`>0QVKb$mPRdiZT~KFRb>uS-C5^9a+wz_6f`HVRygeW
z<n~UVL)kfeZ$+E<Kh~|yl-hWi$*Nn+@!j%*2mHLff77_NH>gZnQoFMwv2w!3@Cmk;
ztdBm};UT}m{Bno$dv&oJq1)4<R@E_0(6_1<Xkb%0Ba<_I?X<+`?T@Aj9Vj(i%ro)s
zmQ&yVtd&*0%=vz&IN$P&hnuVQmGUzmZ|RzvDEm(Bn(Exu6FL0MPRppU=<FBk)_?l3
zf2O$Mj?Mo(m46il9x=Siyylh!m+9MHCT2zk#>M;wyucV@4wdC+VPR%sZ$ODIW>7e3
z-rn=8dcM%@i##qxb=FrBD)_!1Y~1kdC*#qzK1wOfm%hJl=leNRpCwtl+Vr5)<QFG7
zW?E<c%I{-5^McJY{LAur&+XR;adh5ZUtQzepuhbjzt@@L9>?a@zLxOh<#N0ky<#ix
ztnF`C9J_E;aq_|DOB2uUU2*Ef<o%PUK0c^^AxiiAgOaS=YUNGR-f^*W`ORjYJ#;Q?
zK1;b*#_paiijr@#7S7zybnxRI<};VP{U<~#JzwGH?a=mo^Lc?7hWe$uoBGeyh^^k_
zYkl5rLD#|AlOB78zd7+@r$nd6_J14fE%$tO63@<=(Y)sM^5g&YU!`?&FJZRYxrw_y
zMD+s0PP^8{^N&q3Y+{@VOf*x0nZdviPXfk~{1LflfxUqQJa9Rg4UsdGfigmd#ZZhz
z<md5@>agtE1+mW;D>0Rsd0uNZszpoAEKQ6I0jsX~hCPiEwX;&v;!fLp^Xth6A-tvw
z^0%v1|LP0&3;Z^pv6%nn2I-ztvnr>p&xmk;(G}h$eeQqM0><9M(K?(ByLDF0PK);S
zy6IUS`BOUksO-MWUW*Hu4l$JpKQ^^_u)pE$j&1w$V_$3jdwzDek5(=3f>48t+7rHC
zv-@9Ql(S^o!|)}k*GnXO-2Z%9wRp~kV$IY><7k7rO%=z!9kABCwKU7*+KcjwDH9A!
z-nZ2pdw;k)?2h=UFTXZu$1ON~@2bq2BPzZ@o&jq&uKaOj$@+930rB_iIBvXazsS7j
z#Ukr2DHYw>N|J9@sWz@(?frW@ySEGXJE<b3X^r|@*u*oXC%K~*a_Q@GpLhY;UeM|u
zsgMhH3kDVoi6x~)sk&j1>f1`eP|tuX*u&9K*CjI@SaRt4LK_2C3SO>ep203orpZ}J
zPKHq?k%h_Wk!gWZMlPWywrF)TTk^JpXMt=G7D2Yu*U`_@%{4eg&({rRFsjvDe!w<{
zl>$nm1lc`S3Q>t6PH6>E0q!|I#wD4Vo~1!q1_o}~N#^<GwlM9GrUj}#w~|l~$1ID8
z{K5=V=ctn8a0?5MvJmb3!c1SdKGZe@s(#~4r#!P#w@mX?cW?cm9525LgV2D=knCXd
ze317UnM9azH?m-$fsp~d$%U>Jy_tm2S_yA7q3c0!3nBEJz-Sqv>qV~x5qi0p(d$HX
pz37Pxq4zyIT2e#Tik?IeT2nZ%Czt?lR&aQ*0bvR-9FsXgJOIG84%Ywx
rename from security/manager/ssl/tests/unit/test_signed_apps/unsigned_app_1.zip
rename to security/manager/ssl/tests/unit/test_signed_apps/unsigned_app.zip
index 731e050aba5e5b175cea6f4393e4d6541bcb382b..748386dc560aac505a5f7d490d556f5a5c295ebf
GIT binary patch
literal 971
zc$^FHW@Zs#0D;djLEcg^8k_upY!K!J;@rf%%(T?v61}YA{Jd%|1qCITNPJmpQE_H|
zo|2V<kq$^AFEKY2$WaObN-DTkl%(c?rNMFtwMqth26~1{T(w*QC>DS7Fx1fovO$;)
zh=W`mU3^_bGBS%5fT$!RRiP*~F(o%uAuYcM%q;;~lM1s&56!sQO_uCpObiSl%m)-p
zNi0d!&&*9sPt_~POYaQubLZuf;sSZn)59eQNKXS{4v@_@jD?RF7#Qz*x;TbZ%z1lm
zBPWA_z>y8N>tCK1Vr(v47<c>Kr2iir=8U2ti+bb|3Y4n$$q4TOr58_EKbLh*2~7ds
zj7%cTxbp%GG%zxt=L~eM=&2c@wGy7T(e<DwS%jVw7zsGQn-y#<GZ3x@(p=0S9suza
Bgw+55
rename from security/manager/ssl/tests/unit/test_signed_apps/trusted_ca1.der
rename to security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.der
index 0200a7f35d1c8344a816e4109bd9cce07327f21f..f1f5d25f63e6b0f09810ac40cf6b7bbed7c7b8bc
GIT binary patch
literal 790
zc$_n6Viq!JV*Im!nTe5!NhCLr@lmY$+&j~@K3+R#N|92fW_q~+Hyfu`n@8JsUPeZ4
z15HD9164NWP!?t$xr&11;*8Xs9EIY{^t{v*g~Wn_Vug~_;u3|T{QMFFB?*2b14B~-
zLm&i$D3E|LvH&T@H8Cm~@FJYT$jZRn#K_M86z5`UVq|34)e*SDrBCdd<KMh(9c*R-
zwl=~yUp+DP+VbXWMtF`&^}*w+D<0gC-@9=R+w454woloQ)<>0QXQdQ!R4t8K?%Muc
z=BmmZrn<Avf8;VRSSV;tUafH0^U3X<K8Lb%_}+>(@qesan<=&NGLu!emgBqS1rPXn
zd;g|!Yj04Qw4`=tM`GoKjo}k)FIgXbu){-sh56+U=lAMjH$u0kMXjo1nxJo0EzrQG
zaz-X+`r2uU(c2$Q6FN|8xR__+-7TlS|5+=mdYSY6PI12F84ovC>nr7FKHkzbHBt7R
z+BMa=t0!{!nVptVVbR$y)~)~aWB*KX!yTLdc`E-Z3Or(Xm3hrA2`<yOy-duE42+A7
zfWgWG3{Y8N7FGjhM#lfZ=mYZjSy-5vm{bgeK^#>U9s@2m4sA9@R#tXqW|RnK1_fnQ
z==NRL%vQho(AfFL^!jV<FMr=2TCzf53fCdgi{BT@POg-&>R;6^t0}1VX7BY1<xQP;
zEVw$nZoa=d!MS3i^{4p{<E>J)bu>R&F12~{@Qg)LIfqri&Agkh16qaCGJd-l1YY4d
z*gb)L@^RtBV%b%EF0<0bGdI+jaUEm1S`wA@p<uO1XZIaJ^Gl~yKE6tx75+%m<ArU^
z)EhE(ZqBJy3#=AA`!kVqL-Mgj-|YXsRp{Cq{BMR^Org-P(AO#o8OP&~zEOI$?DCJq
zoADd32B?%?5ZRpnoAr^<be}^Gow_NKPw!-X=bclKYgjh@Z?lu6`!AVL**2e6-JL%c
NJeGd_pO?8e695E(I`#kn
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_signed_apps/xpcshellTestRoot.pem.certspec
@@ -0,0 +1,6 @@
+issuer:xpcshell signed apps test root
+subject:xpcshell signed apps test root
+validity:20150101-20350101
+extension:basicConstraints:cA,
+extension:keyUsage:keyEncipherment,keyCertSign
+extension:extKeyUsage:codeSigning
--- a/security/sandbox/linux/SandboxFilter.cpp
+++ b/security/sandbox/linux/SandboxFilter.cpp
@@ -58,16 +58,20 @@ using namespace sandbox::bpf_dsl;
 #ifndef MADV_FREE
 #define MADV_FREE 8
 #endif
 
 #ifndef PR_SET_PTRACER
 #define PR_SET_PTRACER 0x59616d61
 #endif
 
+// The headers define O_LARGEFILE as 0 on x86_64, but we need the
+// actual value because it shows up in file flags.
+#define O_LARGEFILE_REAL 00100000
+
 // To avoid visual confusion between "ifdef ANDROID" and "ifndef ANDROID":
 #ifndef ANDROID
 #define DESKTOP
 #endif
 
 // This file defines the seccomp-bpf system call filter policies.
 // See also SandboxFilterUtil.h, for the CASES_FOR_* macros and
 // SandboxFilterBase::Evaluate{Socket,Ipc}Call.
@@ -740,21 +744,45 @@ public:
         .ElseIf(request == FIONREAD, Allow())
         // Allow anything that isn't a tty ioctl, for now; bug 1302711
         // will cover changing this to a default-deny policy.
         .ElseIf(shifted_type != kTtyIoctls, Allow())
         .Else(SandboxPolicyCommon::EvaluateSyscall(sysno));
     }
 #endif // !MOZ_ALSA
 
-    CASES_FOR_fcntl:
-      // Some fcntls have significant side effects like sending
-      // arbitrary signals, and there's probably nontrivial kernel
-      // attack surface; this should be locked down more if possible.
-      return Allow();
+    CASES_FOR_fcntl: {
+      Arg<int> cmd(1);
+      Arg<int> flags(2);
+      // Typical use of F_SETFL is to modify the flags returned by
+      // F_GETFL and write them back, including some flags that
+      // F_SETFL ignores.  This is a default-deny policy in case any
+      // new SETFL-able flags are added.  (In particular we want to
+      // forbid O_ASYNC; see bug 1328896, but also see bug 1408438.)
+      static const int ignored_flags = O_ACCMODE | O_LARGEFILE_REAL | O_CLOEXEC;
+      static const int allowed_flags = ignored_flags | O_APPEND | O_NONBLOCK;
+      return Switch(cmd)
+        // Close-on-exec is meaningless when execve isn't allowed, but
+        // NSPR reads the bit and asserts that it has the expected value.
+        .Case(F_GETFD, Allow())
+        .Case(F_SETFD,
+              If((flags & ~FD_CLOEXEC) == 0, Allow())
+              .Else(InvalidSyscall()))
+        .Case(F_GETFL, Allow())
+        .Case(F_SETFL,
+              If((flags & ~allowed_flags) == 0, Allow())
+              .Else(InvalidSyscall()))
+        .Case(F_DUPFD_CLOEXEC, Allow())
+        // Pulseaudio uses F_SETLKW.
+        .Case(F_SETLKW, Allow())
+#ifdef F_SETLKW64
+        .Case(F_SETLKW64, Allow())
+#endif
+        .Default(SandboxPolicyCommon::EvaluateSyscall(sysno));
+    }
 
     case __NR_mprotect:
     case __NR_brk:
     case __NR_madvise:
       // libc's realloc uses mremap (Bug 1286119); wasm does too (bug 1342385).
     case __NR_mremap:
       return Allow();
 
--- a/security/sandbox/linux/reporter/SandboxReporter.cpp
+++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
@@ -195,16 +195,17 @@ SubmitToTelemetry(const SandboxReport& a
       }                                                     \
       break
 
     // The syscalls handled specially:
 
     ARG_HEX(clone, 0); // flags
     ARG_DECIMAL(prctl, 0); // option
     ARG_HEX(ioctl, 1); // request
+    ARG_DECIMAL(fcntl, 1); // cmd
     ARG_DECIMAL(madvise, 2); // advice
     ARG_CLOCKID(clock_gettime, 0); // clk_id
 
 #ifdef __NR_socketcall
     ARG_DECIMAL(socketcall, 0); // call
 #endif
 #ifdef __NR_ipc
     ARG_DECIMAL(ipc, 0); // call
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -343,16 +343,21 @@ name = "caseless"
 version = "0.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "cc"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "cexpr"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "nom 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -1092,29 +1097,30 @@ dependencies = [
  "core-graphics 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 7.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "fontsan 0.3.2 (git+https://github.com/servo/fontsan)",
  "freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx_traits 0.0.1",
- "harfbuzz-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "harfbuzz-sys 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "malloc_size_of 0.0.1",
  "malloc_size_of_derive 0.0.1",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "range 0.0.1",
  "serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-fontconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_allocator 0.0.1",
  "servo_arc 0.0.1",
  "servo_atoms 0.0.1",
  "servo_geometry 0.0.1",
  "servo_url 0.0.1",
  "simd 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
  "style_traits 0.0.1",
@@ -1188,17 +1194,17 @@ dependencies = [
  "compositing 0.0.1",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "libservo 0.0.1",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net_traits 0.0.1",
- "osmesa-src 17.3.0-devel (git+https://github.com/servo/osmesa-src)",
+ "osmesa-src 17.3.1-devel (git+https://github.com/servo/osmesa-src)",
  "osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "script_traits 0.0.1",
  "servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-glutin 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_config 0.0.1",
  "servo_geometry 0.0.1",
  "servo_url 0.0.1",
  "style_traits 0.0.1",
@@ -1225,17 +1231,17 @@ source = "registry+https://github.com/ru
 
 [[package]]
 name = "half"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "harfbuzz-sys"
-version = "0.1.14"
+version = "0.1.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "freetype 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1435,16 +1441,34 @@ dependencies = [
 ]
 
 [[package]]
 name = "itoa"
 version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "jemalloc-sys"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "jemallocator"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "jemalloc-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "jpeg-decoder"
 version = "0.1.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1566,16 +1590,17 @@ dependencies = [
  "profile_traits 0.0.1",
  "range 0.0.1",
  "rayon 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "script 0.0.1",
  "script_layout_interface 0.0.1",
  "script_traits 0.0.1",
  "selectors 0.19.0",
  "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_allocator 0.0.1",
  "servo_arc 0.0.1",
  "servo_atoms 0.0.1",
  "servo_config 0.0.1",
  "servo_geometry 0.0.1",
  "servo_url 0.0.1",
  "style 0.0.1",
  "style_traits 0.0.1",
  "webrender_api 0.52.1 (git+https://github.com/servo/webrender)",
@@ -1680,20 +1705,20 @@ dependencies = [
  "webrender 0.52.1 (git+https://github.com/servo/webrender)",
  "webrender_api 0.52.1 (git+https://github.com/servo/webrender)",
  "webvr 0.0.1",
  "webvr_traits 0.0.1",
 ]
 
 [[package]]
 name = "libz-sys"
-version = "1.0.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "gcc 0.3.47 (registry+https://github.com/rust-lang/crates.io-index)",
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "log"
 version = "0.3.8"
@@ -1728,17 +1753,16 @@ dependencies = [
 name = "malloc_size_of"
 version = "0.0.1"
 dependencies = [
  "app_units 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "hashglobe 0.1.0",
  "js 0.1.6 (git+https://github.com/servo/rust-mozjs)",
- "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_arc 0.0.1",
  "smallbitvec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_api 0.52.1 (git+https://github.com/servo/webrender)",
  "xml5ever 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -1876,20 +1900,20 @@ dependencies = [
 [[package]]
 name = "mitochondria"
 version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "mozjs_sys"
 version = "0.0.0"
-source = "git+https://github.com/servo/mozjs#834ce35c3f008010213351107b68f397989d2ffd"
+source = "git+https://github.com/servo/mozjs#424067c8d176bf1ee4c7f90c56916d35542de672"
 dependencies = [
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "mp3-metadata"
 version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -2206,18 +2230,18 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "osmesa-src"
-version = "17.3.0-devel"
-source = "git+https://github.com/servo/osmesa-src#100789c549a5716773a74a5db0a2e929470e2c9f"
+version = "17.3.1-devel"
+source = "git+https://github.com/servo/osmesa-src#6d23daede16f7edf7c66bdcce73c7583d0853733"
 
 [[package]]
 name = "osmesa-sys"
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -2358,16 +2382,17 @@ source = "registry+https://github.com/ru
 
 [[package]]
 name = "profile"
 version = "0.0.1"
 dependencies = [
  "heartbeats-simple 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "influent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "jemalloc-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "profile_traits 0.0.1",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_config 0.0.1",
  "task_info 0.0.1",
@@ -2376,16 +2401,17 @@ dependencies = [
 
 [[package]]
 name = "profile_tests"
 version = "0.0.1"
 dependencies = [
  "ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "profile 0.0.1",
  "profile_traits 0.0.1",
+ "servo_allocator 0.0.1",
 ]
 
 [[package]]
 name = "profile_traits"
 version = "0.0.1"
 dependencies = [
  "energy-monitor 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "energymon 0.3.0 (git+https://github.com/energymon/energymon-rust.git)",
@@ -2625,16 +2651,17 @@ dependencies = [
  "ref_slice 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "script_layout_interface 0.0.1",
  "script_plugins 0.0.1",
  "script_traits 0.0.1",
  "selectors 0.19.0",
  "serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo_allocator 0.0.1",
  "servo_arc 0.0.1",
  "servo_atoms 0.0.1",
  "servo_config 0.0.1",
  "servo_geometry 0.0.1",
  "servo_rand 0.0.1",
  "servo_url 0.0.1",
  "smallvec 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "style 0.0.1",
@@ -2832,22 +2859,22 @@ dependencies = [
 ]
 
 [[package]]
 name = "servo-fontconfig"
 version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo-fontconfig-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "servo-fontconfig-sys"
-version = "4.0.3"
+version = "4.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -2894,17 +2921,17 @@ dependencies = [
  "cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "glx 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "io-surface 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "servo-fontconfig-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo-glutin 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "x11 2.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "servo-websocket"
 version = "0.19.1"
@@ -2917,16 +2944,24 @@ dependencies = [
  "openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
  "unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "servo_allocator"
+version = "0.0.1"
+dependencies = [
+ "jemallocator 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "servo_arc"
 version = "0.0.1"
 dependencies = [
  "nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -3438,17 +3473,17 @@ name = "unicode-normalization"
 version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "unicode-script"
 version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "harfbuzz-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "harfbuzz-sys 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "unicode-segmentation"
 version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -3777,16 +3812,17 @@ dependencies = [
 "checksum blurz 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e73bda0f4c71c63a047351070097f3f507e6718e86b9ee525173371ef7b94b73"
 "checksum bow 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c0ad826831ef9226adeaa7fa94a866b2dd316258219ec9cefb58595818dc5c52"
 "checksum brotli 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "951f20a2cc403194b2746d9ac6f796292e3c0344e983c72c7d6bd6cff6c3d102"
 "checksum brotli-decompressor 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e80402aa0457c3c03d3996a36af7c1e7efa2ad97ee8ec7c2761b07666bab5566"
 "checksum browserhtml 0.1.17 (git+https://github.com/browserhtml/browserhtml?branch=crate)" = "<none>"
 "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
 "checksum bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c129aff112dcc562970abb69e2508b40850dd24c274761bb50fb8a0067ba6c27"
 "checksum caseless 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8950b075cff75cdabadee97148a8b5816c7cf62e5948a6005b5255d564b42fe7"
+"checksum cc 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c674f0870e3dbd4105184ea035acb1c32c8ae69939c9e228d2b11bbfe29efad"
 "checksum cexpr 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393a5f0088efbe41f9d1fcd062f24e83c278608420e62109feb2c8abee07de7d"
 "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de"
 "checksum cgl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "86765cb42c2a2c497e142af72517c1b4d7ae5bb2f25dfa77a5c69642f2342d89"
 "checksum clang-sys 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5955eab05fa8e6ff2b353753dc73a0608daa36e472a21c69f2eb51f43f593544"
 "checksum clap 2.20.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7db281b0520e97fbd15cd615dcd8f8bcad0c26f5f7d5effe705f090f39e9a758"
 "checksum clipboard 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dd3a9a938558f33ec1baaa6ca631a69c104aafaacbc66868d9ad28cf5f30564f"
 "checksum clipboard-win 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "693b1280c514045382dfdbb78d1594b1b03cdb66320aeb7ebd2bd38d49bae959"
 "checksum cmake 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)" = "d18d68987ed4c516dcc3e7913659bfa4076f5182eea4a7e0038bb060953e76ac"
@@ -3848,17 +3884,17 @@ dependencies = [
 "checksum getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9047cfbd08a437050b363d35ef160452c5fe8ea5187ae0a624708c91581d685"
 "checksum gif 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a80d6fe9e52f637df9afd4779449a7be17c39cc9c35b01589bb833f956ba596"
 "checksum gl_generator 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0940975a4ca12b088d32b5d5134826c47d2e73de4b0b459b05244c01503eccbb"
 "checksum gleam 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bf887141f0c2a83eae026cbf3fba74f0a5cb0f01d20e5cdfcd8c4ad39295be1e"
 "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
 "checksum glx 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b280007fa9c7442cfd1e0b1addb8d1a59240267110e8705f8f7e2c7bfb7e2f72"
 "checksum gvr-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e84ba5e13cd925de87b669475525f956f8e936e67ddb24fbb1a077d96bbe174c"
 "checksum half 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63d68db75012a85555434ee079e7e6337931f87a087ab2988becbadf64673a7f"
-"checksum harfbuzz-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "a2caaa66078fdfacea32db1351223697a1167ad2d4bbee6b8d4ca220ce5b10b3"
+"checksum harfbuzz-sys 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "52aa65c5649a0a2f1b27ab30093b3cc84681e17ddb552267e21948c5a6fa6b05"
 "checksum heapsize 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4c7593b1522161003928c959c20a2ca421c68e940d63d75573316a009e48a6d4"
 "checksum heartbeats-simple 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ad003ce233955e9d95f2c69cde84e68302ba9ba4a673d351c9bff93c738aadc"
 "checksum heartbeats-simple-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e1a408c0011427cc0e0049f7861c70377819aedfc006e8c901b1c70fd98fb1a4"
 "checksum html5ever 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5bfb46978eb757a603b7dfe2dafb1c62cb4dee3428d8ac1de734d83d6b022d06"
 "checksum httparse 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a6e7a63e511f9edffbab707141fbb8707d1a3098615fb2adbd5769cdfcc9b17d"
 "checksum hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)" = "368cb56b2740ebf4230520e2b90ebb0461e69034d85d1945febd9b3971426db2"
 "checksum hyper-openssl 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "85a372eb692590b3fe014c196c30f9f52d4c42f58cd49dd94caeee1593c9cc37"
 "checksum hyper_serde 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cbe43f514f80494e9329c9fc47d61b85b167d245685424637a0f4a409177e444"
@@ -3868,29 +3904,31 @@ dependencies = [
 "checksum immeta 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0b9260463a221bfe3f02100c56e2d14c050d5ffe7e44a43d0a1b2b1f2b523502"
 "checksum inflate 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "10ec05638adf7c5c788bc0cfa608cd479a13572beda20feb4898fe1d85d2c64b"
 "checksum influent 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a22b311b83431be3ab9af96ca9ea41554bb4a8551ea871ae44c3ce0c57e55f2c"
 "checksum io-surface 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad4578cee6ed49a17766fa608a4425008313c55ebb7a267622cbddd6b01751e2"
 "checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
 "checksum ipc-channel 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c10ed089b1921b01ef342c736a37ee0788eeb9a5f373bb2df1ba88d01125064f"
 "checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
 "checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
+"checksum jemalloc-sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94fb624d7e8345e5c42caab8d1db6ec925fdadff3fd0cb7dd781b41be8442828"
+"checksum jemallocator 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "1850725977c344d63af66e8fd00857646e3ec936c490cd63667860b7b03ab5c1"
 "checksum jpeg-decoder 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2805ccb10ffe4d10e06ef68a158ff94c255211ecbae848fbde2146b098f93ce7"
 "checksum js 0.1.6 (git+https://github.com/servo/rust-mozjs)" = "<none>"
 "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
 "checksum khronos_api 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d5a08e2a31d665af8f1ca437eab6d00a93c9d62a549f73f9ed8fc2e55b5a91a7"
 "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
 "checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf"
 "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b"
 "checksum leak 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73"
 "checksum leaky-cow 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40a8225d44241fd324a8af2806ba635fc7c8a7e9a7de4d5cf3ef54e71f5926fc"
 "checksum len-trait 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "723558ab8acaa07cb831b424cd164b587ddc1648b34748a30953c404e9a4a65b"
 "checksum libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e7eb6b826bfc1fdea7935d46556250d1799b7fe2d9f7951071f4291710665e3e"
 "checksum libloading 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "be99f814beb3e9503a786a592c909692bb6d4fc5a695f6ed7987223acfbd5194"
-"checksum libz-sys 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "3fdd64ef8ee652185674455c1d450b83cbc8ad895625d543b5324d923f82e4d8"
+"checksum libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "87f737ad6cc6fd6eefe3d9dc5412f1573865bded441300904d2f42269e140f16"
 "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b"
 "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
 "checksum mac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
 "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
 "checksum markup5ever 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "047150a0e03b57e638fc45af33a0b63a0362305d5b9f92ecef81df472a4cceb0"
 "checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1"
 "checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
 "checksum metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829fffe7ea1d747e23f64be972991bc516b2f1ac2ae4a3b33d8bea150c410151"
@@ -3918,17 +3956,17 @@ dependencies = [
 "checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
 "checksum offscreen_gl_context 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ee0b17158af8e1fb6c8f8df048bd3f1c4b78bebbf4883f189e2a83ce853ce9bd"
 "checksum ogg 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7137bf02687385302f4c0aecd77cfce052b69f5b4ee937be778e125c62f67e30"
 "checksum ogg_metadata 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc665717454399cba557c55ad226148996e9266ee291f8a37a98bb2cded0a490"
 "checksum open 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3478ed1686bd1300c8a981a940abc92b06fac9cbef747f4c668d4e032ff7b842"
 "checksum openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bb5d1663b73d10c6a3eda53e2e9d0346f822394e7b858d7257718f65f61dfbe2"
 "checksum openssl-sys 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "3a5886d87d3e2a0d890bf62dc8944f5e3769a405f7e1e9ef6e517e47fd7a0897"
 "checksum ordered-float 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "da12c96037889ae0be29dd2bdd260e5a62a7df24e6466d5a15bb8131c1c200a8"
-"checksum osmesa-src 17.3.0-devel (git+https://github.com/servo/osmesa-src)" = "<none>"
+"checksum osmesa-src 17.3.1-devel (git+https://github.com/servo/osmesa-src)" = "<none>"
 "checksum osmesa-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b"
 "checksum ovr-mobile-sys 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7b5f9389b2015f8340f0566c488f3e96735e2e8fd7b85d571832cd274ac2998"
 "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
 "checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e"
 "checksum parking_lot_core 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4f610cb9664da38e417ea3225f23051f589851999535290e077939838ab7a595"
 "checksum parse-hosts 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3842db828281691db6e6f1709ed6a73025747c0cd15cc10346a5cb021e2bc28"
 "checksum peeking_take_while 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
 "checksum percent-encoding 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de154f638187706bde41d9b4738748933d64e6b37bdbffc0b47a97d16a6ae356"
@@ -3965,17 +4003,17 @@ dependencies = [
 "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac"
 "checksum serde 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "bcb6a7637a47663ee073391a139ed07851f27ed2532c2abc88c6bf27a16cdf34"
 "checksum serde_bytes 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a73f5ad9bb83e1e407254c7a355f4efdaffe3c1442fc0657ddb8b9b6b225655"
 "checksum serde_derive 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "812ff66056fd9a9a5b7c119714243b0862cf98340e7d4b5ee05a932c40d5ea6c"
 "checksum serde_derive_internals 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bd381f6d01a6616cdba8530492d453b7761b456ba974e98768a18cad2cd76f58"
 "checksum serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "48b04779552e92037212c3615370f6bd57a40ebba7f20e554ff9f55e41a69a7b"
 "checksum servo-egl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "21069a884c33fe6ee596975e1f3849ed88c4ec857fbaf11d33672d8ebe051217"
 "checksum servo-fontconfig 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "93f799b649b4a2bf362398910eca35240704c7e765e780349b2bb1070d892262"
-"checksum servo-fontconfig-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6be80777ee6edecbbbf8774c76e19dddfe336256c57a4ded06d6ad3df7be358e"
+"checksum servo-fontconfig-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "38b494f03009ee81914b0e7d387ad7c145cafcd69747c2ec89b0e17bb94f303a"
 "checksum servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9232032c2e85118c0282c6562c84cab12316e655491ba0a5d1905b2320060d1b"
 "checksum servo-glutin 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05bfa660ed7bceb5df3d79ca5a9c99a59fa7019c4477cbe52b70f80034bce568"
 "checksum servo-skia 0.30000006.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c104b3a0b3938f760772c09584fd6b2bb7ca205e2553a767b357f8ddbfbccbc"
 "checksum servo-websocket 0.19.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a1ff13c5d852c2793805226e688044309f2c1d8f063784805a13e99cb75b611"
 "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
 "checksum shared_library 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04126b6fcfd2710fb5b6d18f4207b6c535f2850a7e1a43bcd526d44f30a79a"
 "checksum shell32-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "72f20b8f3c060374edb8046591ba28f62448c369ccbdc7b02075103fb3a9e38d"
 "checksum sig 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c6649e43c1a1e68d29ed56d0dc3b5b6cf3b901da77cf107c4066b9e3da036df5"
new file mode 100644
--- /dev/null
+++ b/servo/components/allocator/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "servo_allocator"
+version = "0.0.1"
+authors = ["The Servo Project Developers"]
+license = "MPL-2.0"
+publish = false
+
+[lib]
+path = "lib.rs"
+
+[features]
+unstable = ["kernel32-sys", "jemallocator"]
+
+[target.'cfg(not(windows))'.dependencies]
+jemallocator = { version = "0.1.3", optional = true }
+
+[target.'cfg(windows)'.dependencies]
+kernel32-sys = { version = "0.2.1", optional = true }
new file mode 100644
--- /dev/null
+++ b/servo/components/allocator/lib.rs
@@ -0,0 +1,62 @@
+/* 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/. */
+
+//! Selecting the default global allocator for Servo
+
+#![cfg_attr(all(feature = "unstable", windows), feature(alloc_system, allocator_api))]
+#![cfg_attr(feature = "unstable", feature(global_allocator))]
+
+#[cfg(feature = "unstable")]
+#[global_allocator]
+static ALLOC: platform::Allocator = platform::Allocator;
+
+pub use platform::usable_size;
+
+
+#[cfg(all(feature = "unstable", not(windows)))]
+mod platform {
+    extern crate jemallocator;
+
+    pub use self::jemallocator::Jemalloc as Allocator;
+    use std::os::raw::c_void;
+
+    /// Get the size of a heap block.
+    pub unsafe extern "C" fn usable_size(ptr: *const c_void) -> usize {
+        jemallocator::usable_size(ptr)
+    }
+}
+
+#[cfg(all(feature = "unstable", windows))]
+mod platform {
+    extern crate alloc_system;
+    extern crate kernel32;
+
+    pub use self::alloc_system::System as Allocator;
+    use self::kernel32::{GetProcessHeap, HeapSize, HeapValidate};
+    use std::os::raw::c_void;
+
+    /// Get the size of a heap block.
+    pub unsafe extern "C" fn usable_size(mut ptr: *const c_void) -> usize {
+        let heap = GetProcessHeap();
+
+        if HeapValidate(heap, 0, ptr) == 0 {
+            ptr = *(ptr as *const *const c_void).offset(-1);
+        }
+
+        HeapSize(heap, 0, ptr) as usize
+    }
+}
+
+#[cfg(not(feature = "unstable"))]
+mod platform {
+    use std::os::raw::c_void;
+
+    /// Without `#[global_allocator]` we cannot be certain of what allocator is used
+    /// or how it is linked. We therefore disable memory reporting. (Return zero.)
+    pub unsafe extern "C" fn usable_size(_ptr: *const c_void) -> usize {
+        0
+    }
+}
+
+
--- a/servo/components/gfx/Cargo.toml
+++ b/servo/components/gfx/Cargo.toml
@@ -48,16 +48,17 @@ xi-unicode = "0.1.0"
 [target.'cfg(target_os = "macos")'.dependencies]
 byteorder = "1.0"
 core-foundation = "0.4"
 core-graphics = "0.9"
 core-text = "7.0"
 
 [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
 freetype = "0.3"
+servo_allocator = {path = "../allocator"}
 
 [target.'cfg(target_os = "linux")'.dependencies]
 servo-fontconfig = "0.2.1"
 
 [target.'cfg(target_os = "android")'.dependencies]
 xml5ever = {version = "0.10"}
 
 [target.'cfg(any(target_feature = "sse2", target_feature = "neon"))'.dependencies]
--- a/servo/components/gfx/lib.rs
+++ b/servo/components/gfx/lib.rs
@@ -22,18 +22,18 @@ extern crate bitflags;
 #[cfg(target_os = "windows")] extern crate truetype;
 
 extern crate euclid;
 extern crate fnv;
 
 #[cfg(target_os = "linux")]
 extern crate fontconfig;
 extern crate fontsan;
-#[cfg(any(target_os = "linux", target_os = "android"))]
-extern crate freetype;
+#[cfg(any(target_os = "linux", target_os = "android"))] extern crate freetype;
+#[cfg(any(target_os = "linux", target_os = "android"))] extern crate servo_allocator;
 extern crate gfx_traits;
 
 // Eventually we would like the shaper to be pluggable, as many operating systems have their own
 // shapers. For now, however, this is a hard dependency.
 extern crate harfbuzz_sys as harfbuzz;
 
 extern crate ipc_channel;
 #[macro_use]
--- a/servo/components/gfx/platform/freetype/font_context.rs
+++ b/servo/components/gfx/platform/freetype/font_context.rs
@@ -3,17 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use freetype::freetype::FT_Add_Default_Modules;
 use freetype::freetype::FT_Done_Library;
 use freetype::freetype::FT_Library;
 use freetype::freetype::FT_Memory;
 use freetype::freetype::FT_MemoryRec_;
 use freetype::freetype::FT_New_Library;
-use malloc_size_of::{malloc_size_of, MallocSizeOf, MallocSizeOfOps};
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use servo_allocator::usable_size;
 use std::mem;
 use std::os::raw::{c_long, c_void};
 use std::ptr;
 use std::rc::Rc;
 
 // We pass a |User| struct -- via an opaque |void*| -- to FreeType each time a new instance is
 // created. FreeType passes it back to the ft_alloc/ft_realloc/ft_free callbacks. We use it to
 // record the memory usage of each FreeType instance.
@@ -26,57 +27,58 @@ const FT_ALIGNMENT: usize = 1;
 
 extern fn ft_alloc(mem: FT_Memory, req_size: c_long) -> *mut c_void {
     assert!(FT_ALIGNMENT == 1);
     let mut vec = Vec::<u8>::with_capacity(req_size as usize);
     let ptr = vec.as_mut_ptr() as *mut c_void;
     mem::forget(vec);
 
     unsafe {
-        let actual_size = malloc_size_of(ptr as *const _);
+        let actual_size = usable_size(ptr as *const _);
         let user = (*mem).user as *mut User;
         (*user).size += actual_size;
     }
 
     ptr
 }
 
 extern fn ft_free(mem: FT_Memory, ptr: *mut c_void) {
     unsafe {
-        let actual_size = malloc_size_of(ptr as *const _);
+        let actual_size = usable_size(ptr as *const _);
         let user = (*mem).user as *mut User;
         (*user).size -= actual_size;
 
         assert!(FT_ALIGNMENT == 1);
         mem::drop(Vec::<u8>::from_raw_parts(ptr as *mut u8, actual_size, 0))
     }
 }
 
-extern fn ft_realloc(mem: FT_Memory, _cur_size: c_long, new_req_size: c_long,
+extern fn ft_realloc(mem: FT_Memory, old_size: c_long, new_req_size: c_long,
                      old_ptr: *mut c_void) -> *mut c_void {
     let old_actual_size;
     let mut vec;
     unsafe {
-        old_actual_size = malloc_size_of(old_ptr as *const _);
-        vec = Vec::<u8>::from_raw_parts(old_ptr as *mut u8, old_actual_size, old_actual_size);
+        old_actual_size = usable_size(old_ptr as *const _);
+        let old_size = old_size as usize;
+        vec = Vec::<u8>::from_raw_parts(old_ptr as *mut u8, old_size, old_size);
     };
 
     let new_req_size = new_req_size as usize;
     if new_req_size > old_actual_size {
         vec.reserve_exact(new_req_size - old_actual_size)
     } else if new_req_size < old_actual_size {
         vec.truncate(new_req_size);
         vec.shrink_to_fit()
     }
 
     let new_ptr = vec.as_mut_ptr() as *mut c_void;
     mem::forget(vec);
 
     unsafe {
-        let new_actual_size = malloc_size_of(new_ptr as *const _);
+        let new_actual_size = usable_size(new_ptr as *const _);
         let user = (*mem).user as *mut User;
         (*user).size += new_actual_size;
         (*user).size -= old_actual_size;
     }
 
     new_ptr
 }
 
--- a/servo/components/layout_thread/Cargo.toml
+++ b/servo/components/layout_thread/Cargo.toml
@@ -35,16 +35,17 @@ parking_lot = "0.4"
 profile_traits = {path = "../profile_traits"}
 range = {path = "../range"}
 rayon = "0.8"
 script = {path = "../script"}
 script_layout_interface = {path = "../script_layout_interface"}
 script_traits = {path = "../script_traits"}
 selectors = { path = "../selectors" }
 serde_json = "1.0"
+servo_allocator = {path = "../allocator"}
 servo_arc = {path = "../servo_arc"}
 servo_atoms = {path = "../atoms"}
 servo_config = {path = "../config"}
 servo_geometry = {path = "../geometry"}
 servo_url = {path = "../url"}
 style = {path = "../style"}
 style_traits = {path = "../style_traits"}
 webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]}
--- a/servo/components/layout_thread/dom_wrapper.rs
+++ b/servo/components/layout_thread/dom_wrapper.rs
@@ -681,31 +681,33 @@ impl<'le> ::selectors::Element for Servo
         self.element.local_name()
     }
 
     #[inline]
     fn get_namespace(&self) -> &Namespace {
         self.element.namespace()
     }
 
-    fn match_pseudo_element(&self,
-                            _pseudo: &PseudoElement,
-                            _context: &mut MatchingContext)
-                            -> bool
-    {
+    fn match_pseudo_element(
+        &self,
+        _pseudo: &PseudoElement,
+        _context: &mut MatchingContext<Self::Impl>,
+    ) -> bool {
         false
     }
 
-    fn match_non_ts_pseudo_class<F>(&self,
-                                    pseudo_class: &NonTSPseudoClass,
-                                    _: &mut MatchingContext,
-                                    _: &RelevantLinkStatus,
-                                    _: &mut F)
-                                    -> bool
-        where F: FnMut(&Self, ElementSelectorFlags),
+    fn match_non_ts_pseudo_class<F>(
+        &self,
+        pseudo_class: &NonTSPseudoClass,
+        _: &mut MatchingContext<Self::Impl>,
+        _: &RelevantLinkStatus,
+        _: &mut F,
+    ) -> bool
+    where
+        F: FnMut(&Self, ElementSelectorFlags),
     {
         match *pseudo_class {
             // https://github.com/servo/servo/issues/8718
             NonTSPseudoClass::Link |
             NonTSPseudoClass::AnyLink => self.is_link(),
             NonTSPseudoClass::Visited => false,
 
             NonTSPseudoClass::Lang(ref lang) => self.match_element_lang(None, &*lang),
@@ -1171,21 +1173,21 @@ impl<'le> ::selectors::Element for Servo
         self.element.get_local_name()
     }
 
     #[inline]
     fn get_namespace(&self) -> &Namespace {
         self.element.get_namespace()
     }
 
-    fn match_pseudo_element(&self,
-                            _pseudo: &PseudoElement,
-                            _context: &mut MatchingContext)
-                            -> bool
-    {
+    fn match_pseudo_element(
+        &self,
+        _pseudo: &PseudoElement,
+        _context: &mut MatchingContext<Self::Impl>
+    ) -> bool {
         false
     }
 
     fn attr_matches(&self,
                     ns: &NamespaceConstraint<&Namespace>,
                     local_name: &LocalName,
                     operation: &AttrSelectorOperation<&String>)
                     -> bool {
@@ -1198,23 +1200,25 @@ impl<'le> ::selectors::Element for Servo
                 let values = unsafe {
                     (*self.element.element.unsafe_get()).get_attr_vals_for_layout(local_name)
                 };
                 values.iter().any(|v| v.eval_selector(operation))
             }
         }
     }
 
-    fn match_non_ts_pseudo_class<F>(&self,
-                                    _: &NonTSPseudoClass,
-                                    _: &mut MatchingContext,
-                                    _: &RelevantLinkStatus,
-                                    _: &mut F)
-                                    -> bool
-        where F: FnMut(&Self, ElementSelectorFlags),
+    fn match_non_ts_pseudo_class<F>(
+        &self,
+        _: &NonTSPseudoClass,
+        _: &mut MatchingContext<Self::Impl>,
+        _: &RelevantLinkStatus,
+        _: &mut F,
+    ) -> bool
+    where
+        F: FnMut(&Self, ElementSelectorFlags),
     {
         // NB: This could maybe be implemented
         warn!("ServoThreadSafeLayoutElement::match_non_ts_pseudo_class called");
         false
     }
 
     fn is_link(&self) -> bool {
         warn!("ServoThreadSafeLayoutElement::is_link called");
--- a/servo/components/layout_thread/lib.rs
+++ b/servo/components/layout_thread/lib.rs
@@ -34,16 +34,17 @@ extern crate parking_lot;
 extern crate profile_traits;
 extern crate range;
 extern crate rayon;
 extern crate script;
 extern crate script_layout_interface;
 extern crate script_traits;
 extern crate selectors;
 extern crate serde_json;
+extern crate servo_allocator;
 extern crate servo_arc;
 extern crate servo_atoms;
 extern crate servo_config;
 extern crate servo_geometry;
 extern crate servo_url;
 extern crate style;
 extern crate style_traits;
 extern crate webrender_api;
@@ -79,17 +80,17 @@ use layout::query::{process_margin_style
 use layout::query::{process_node_geometry_request, process_node_scroll_area_request};
 use layout::query::{process_node_scroll_root_id_request, process_offset_parent_query};
 use layout::sequential;
 use layout::traversal::{ComputeStackingRelativePositions, PreorderFlowTraversal, RecalcStyleAndConstructFlows};
 use layout::webrender_helpers::WebRenderDisplayListConverter;
 use layout::wrapper::LayoutNodeLayoutData;
 use layout_traits::LayoutThreadFactory;
 use libc::c_void;
-use malloc_size_of::{malloc_size_of, MallocSizeOf, MallocSizeOfOps};
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
 use msg::constellation_msg::PipelineId;
 use msg::constellation_msg::TopLevelBrowsingContextId;
 use net_traits::image_cache::{ImageCache, UsePlaceholder};
 use parking_lot::RwLock;
 use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
 use profile_traits::time::{self, TimerMetadata, profile};
 use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
@@ -770,17 +771,17 @@ impl LayoutThread {
     }
 
     fn collect_reports<'a, 'b>(&self,
                                reports_chan: ReportsChan,
                                possibly_locked_rw_data: &mut RwData<'a, 'b>) {
         let mut reports = vec![];
         // Servo uses vanilla jemalloc, which doesn't have a
         // malloc_enclosing_size_of function.
-        let mut ops = MallocSizeOfOps::new(malloc_size_of, None, None);
+        let mut ops = MallocSizeOfOps::new(::servo_allocator::usable_size, None, None);
 
         // FIXME(njn): Just measuring the display tree for now.
         let rw_data = possibly_locked_rw_data.lock();
         let display_list = rw_data.display_list.as_ref();
         let formatted_url = &format!("url({})", self.url);
         reports.push(Report {
             path: path![formatted_url, "layout-thread", "display-list"],
             kind: ReportKind::ExplicitJemallocHeapSize,
--- a/servo/components/malloc_size_of/Cargo.toml
+++ b/servo/components/malloc_size_of/Cargo.toml
@@ -3,19 +3,16 @@ name = "malloc_size_of"
 version = "0.0.1"
 authors = ["The Servo Project Developers"]
 license = "MIT/Apache-2.0"
 publish = false
 
 [lib]
 path = "lib.rs"
 
-[target.'cfg(windows)'.dependencies]
-kernel32-sys = "0.2.1"
-
 [features]
 servo = ["js", "string_cache", "url", "webrender_api", "xml5ever"]
 
 [dependencies]
 app_units = "0.5.5"
 cssparser = "0.22.0"
 euclid = "0.15"
 hashglobe = { path = "../hashglobe" }
--- a/servo/components/malloc_size_of/lib.rs
+++ b/servo/components/malloc_size_of/lib.rs
@@ -44,32 +44,28 @@
 //!   `<Box<_> as MallocSizeOf>::size_of(field, ops)`.
 
 extern crate app_units;
 extern crate cssparser;
 extern crate euclid;
 extern crate hashglobe;
 #[cfg(feature = "servo")]
 extern crate js;
-#[cfg(target_os = "windows")]
-extern crate kernel32;
 extern crate servo_arc;
 extern crate smallbitvec;
 extern crate smallvec;
 #[cfg(feature = "servo")]
 extern crate string_cache;
 #[cfg(feature = "servo")]
 extern crate url;
 #[cfg(feature = "servo")]
 extern crate webrender_api;
 #[cfg(feature = "servo")]
 extern crate xml5ever;
 
-#[cfg(target_os = "windows")]
-use kernel32::{GetProcessHeap, HeapSize, HeapValidate};
 use std::hash::{BuildHasher, Hash};
 use std::mem::size_of;
 use std::ops::Range;
 use std::os::raw::c_void;
 
 /// A C function that takes a pointer to a heap allocation and returns its size.
 type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
 
@@ -141,43 +137,16 @@ impl MallocSizeOfOps {
 
     /// Call `have_seen_ptr_op` on `ptr`.
     pub fn have_seen_ptr<T>(&mut self, ptr: *const T) -> bool {
         let have_seen_ptr_op = self.have_seen_ptr_op.as_mut().expect("missing have_seen_ptr_op");
         have_seen_ptr_op(ptr as *const c_void)
     }
 }
 
-/// Get the size of a heap block.
-#[cfg(not(target_os = "windows"))]
-pub unsafe extern "C" fn malloc_size_of(ptr: *const c_void) -> usize {
-    // The C prototype is `je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr)`. On some
-    // platforms `JEMALLOC_USABLE_SIZE_CONST` is `const` and on some it is empty. But in practice
-    // this function doesn't modify the contents of the block that `ptr` points to, so we use
-    // `*const c_void` here.
-    extern "C" {
-        #[cfg_attr(any(prefixed_jemalloc, target_os = "macos", target_os = "ios", target_os = "android"),
-                   link_name = "je_malloc_usable_size")]
-        fn malloc_usable_size(ptr: *const c_void) -> usize;
-    }
-    malloc_usable_size(ptr)
-}
-
-/// Get the size of a heap block.
-#[cfg(target_os = "windows")]
-pub unsafe extern "C" fn malloc_size_of(mut ptr: *const c_void) -> usize {
-    let heap = GetProcessHeap();
-
-    if HeapValidate(heap, 0, ptr) == 0 {
-        ptr = *(ptr as *const *const c_void).offset(-1);
-    }
-
-    HeapSize(heap, 0, ptr) as usize
-}
-
 /// Trait for measuring the "deep" heap usage of a data structure. This is the
 /// most commonly-used of the traits.
 pub trait MallocSizeOf {
     /// Measure the heap usage of all descendant heap-allocated structures, but
     /// not the space taken up by the value itself.
     fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize;
 }
 
--- a/servo/components/profile/Cargo.toml
+++ b/servo/components/profile/Cargo.toml
@@ -5,17 +5,17 @@ authors = ["The Servo Project Developers
 license = "MPL-2.0"
 publish = false
 
 [lib]
 name = "profile"
 path = "lib.rs"
 
 [features]
-unstable = []
+unstable = ["jemalloc-sys"]
 
 [dependencies]
 profile_traits = {path = "../profile_traits"}
 influent = "0.4"
 ipc-channel = "0.9"
 heartbeats-simple = "0.4"
 log = "0.3.5"
 serde = "1.0"
@@ -26,8 +26,9 @@ time = "0.1.12"
 [target.'cfg(target_os = "macos")'.dependencies]
 task_info = {path = "../../support/rust-task_info"}
 
 [target.'cfg(target_os = "linux")'.dependencies]
 regex = "0.2"
 
 [target.'cfg(not(target_os = "windows"))'.dependencies]
 libc = "0.2"
+jemalloc-sys = {version = "0.1.3", optional = true}
--- a/servo/components/profile/lib.rs
+++ b/servo/components/profile/lib.rs
@@ -1,23 +1,20 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#![cfg_attr(all(feature = "unstable", not(target_os = "windows")), feature(alloc_jemalloc))]
-
 #![deny(unsafe_code)]
 
 #[allow(unused_extern_crates)]
-#[cfg(all(feature = "unstable", not(target_os = "windows")))]
-extern crate alloc_jemalloc;
 extern crate heartbeats_simple;
 extern crate influent;
 extern crate ipc_channel;
-#[allow(unused_extern_crates)]
+#[cfg(all(feature = "unstable", not(target_os = "windows")))]
+extern crate jemalloc_sys;
 #[cfg(not(target_os = "windows"))]
 extern crate libc;
 #[macro_use]
 extern crate log;
 #[macro_use]
 extern crate profile_traits;
 #[cfg(target_os = "linux")]
 extern crate regex;
--- a/servo/components/profile/mem.rs
+++ b/servo/components/profile/mem.rs
@@ -349,17 +349,17 @@ impl ReportsForest {
         }
     }
 }
 
 //---------------------------------------------------------------------------
 
 mod system_reporter {
     #[cfg(all(feature = "unstable", not(target_os = "windows")))]
-    use libc::{c_char, c_void, size_t};
+    use libc::{c_void, size_t};
     #[cfg(target_os = "linux")]
     use libc::c_int;
     use profile_traits::mem::{Report, ReportKind, ReporterRequest};
     #[cfg(all(feature = "unstable", not(target_os = "windows")))]
     use std::ffi::CString;
     #[cfg(all(feature = "unstable", not(target_os = "windows")))]
     use std::mem::size_of;
     #[cfg(all(feature = "unstable", not(target_os = "windows")))]
@@ -455,21 +455,17 @@ mod system_reporter {
     }
 
     #[cfg(not(target_os = "linux"))]
     fn system_heap_allocated() -> Option<usize> {
         None
     }
 
     #[cfg(all(feature = "unstable", not(target_os = "windows")))]
-    extern {
-        #[cfg_attr(any(target_os = "macos", target_os = "android"), link_name = "je_mallctl")]
-        fn mallctl(name: *const c_char, oldp: *mut c_void, oldlenp: *mut size_t,
-                   newp: *mut c_void, newlen: size_t) -> ::libc::c_int;
-    }
+    use jemalloc_sys::mallctl;
 
     #[cfg(all(feature = "unstable", not(target_os = "windows")))]
     fn jemalloc_stat(value_name: &str) -> Option<usize> {
         // Before we request the measurement of interest, we first send an "epoch"
         // request. Without that jemalloc gives cached statistics(!) which can be
         // highly inaccurate.
         let epoch_name = "epoch";
         let epoch_c_name = CString::new(epoch_name).unwrap();
--- a/servo/components/script/Cargo.toml
+++ b/servo/components/script/Cargo.toml
@@ -8,17 +8,17 @@ publish = false
 build = "build.rs"
 
 [lib]
 name = "script"
 path = "lib.rs"
 
 [features]
 debugmozjs = ['js/debugmozjs']
-unstable = []
+unstable = ["servo_allocator/unstable"]
 
 [build-dependencies]
 cmake = "0.1"
 phf_codegen = "0.7.18"
 phf_shared = "0.7.18"
 serde_json = "1.0"
 
 [target.'cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))'.dependencies]
@@ -72,16 +72,17 @@ profile_traits = {path = "../profile_tra
 ref_filter_map = "1.0.1"
 ref_slice = "1.0"
 regex = "0.2"
 script_layout_interface = {path = "../script_layout_interface"}
 script_plugins = {path = "../script_plugins"}
 script_traits = {path = "../script_traits"}
 selectors = { path = "../selectors" }
 serde = "1.0"
+servo_allocator = {path = "../allocator"}
 servo_arc = {path = "../servo_arc"}
 servo_atoms = {path = "../atoms"}
 servo_config = {path = "../config"}
 servo_geometry = {path = "../geometry" }
 servo_rand = {path = "../rand"}
 servo_url = {path = "../url"}
 smallvec = "0.4"
 style = {path = "../style"}
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -2523,21 +2523,21 @@ impl<'a> SelectorsElement for DomRoot<El
     fn opaque(&self) -> ::selectors::OpaqueElement {
         ::selectors::OpaqueElement::new(self.reflector().get_jsobject().get())
     }
 
     fn parent_element(&self) -> Option<DomRoot<Element>> {
         self.upcast::<Node>().GetParentElement()
     }
 
-    fn match_pseudo_element(&self,
-                            _pseudo: &PseudoElement,
-                            _context: &mut MatchingContext)
-                            -> bool
-    {
+    fn match_pseudo_element(
+        &self,
+        _pseudo: &PseudoElement,
+        _context: &mut MatchingContext<Self::Impl>,
+    ) -> bool {
         false
     }
 
 
     fn first_child_element(&self) -> Option<DomRoot<Element>> {
         self.node.child_elements().next()
     }
 
@@ -2589,23 +2589,25 @@ impl<'a> SelectorsElement for DomRoot<El
     fn get_local_name(&self) -> &LocalName {
         self.local_name()
     }
 
     fn get_namespace(&self) -> &Namespace {
         self.namespace()
     }
 
-    fn match_non_ts_pseudo_class<F>(&self,
-                                    pseudo_class: &NonTSPseudoClass,
-                                    _: &mut MatchingContext,
-                                    _: &RelevantLinkStatus,
-                                    _: &mut F)
-                                    -> bool
-        where F: FnMut(&Self, ElementSelectorFlags),
+    fn match_non_ts_pseudo_class<F>(
+        &self,
+        pseudo_class: &NonTSPseudoClass,
+        _: &mut MatchingContext<Self::Impl>,
+        _: &RelevantLinkStatus,
+        _: &mut F,
+    ) -> bool
+    where
+        F: FnMut(&Self, ElementSelectorFlags),
     {
         match *pseudo_class {
             // https://github.com/servo/servo/issues/8718
             NonTSPseudoClass::Link |
             NonTSPseudoClass::AnyLink => self.is_link(),
             NonTSPseudoClass::Visited => false,
 
             NonTSPseudoClass::ServoNonZeroBorder => {
--- a/servo/components/script/lib.rs
+++ b/servo/components/script/lib.rs
@@ -73,16 +73,17 @@ extern crate phf;
 extern crate profile_traits;
 extern crate ref_filter_map;
 extern crate ref_slice;
 extern crate regex;
 extern crate script_layout_interface;
 extern crate script_traits;
 extern crate selectors;
 extern crate serde;
+extern crate servo_allocator;
 extern crate servo_arc;
 #[macro_use] extern crate servo_atoms;
 extern crate servo_config;
 extern crate servo_geometry;
 extern crate servo_rand;
 extern crate servo_url;
 extern crate smallvec;
 #[macro_use]
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -68,17 +68,17 @@ use hyper::mime::{Mime, SubLevel, TopLev
 use hyper_serde::Serde;
 use ipc_channel::ipc::{self, IpcSender};
 use ipc_channel::router::ROUTER;
 use js::glue::GetWindowProxyClass;
 use js::jsapi::{JSAutoCompartment, JSContext, JS_SetWrapObjectCallbacks};
 use js::jsapi::{JSTracer, SetWindowProxyClass};
 use js::jsval::UndefinedValue;
 use js::rust::Runtime;
-use malloc_size_of::{malloc_size_of, MallocSizeOfOps};
+use malloc_size_of::MallocSizeOfOps;
 use mem::malloc_size_of_including_self;
 use metrics::PaintTimeMetrics;
 use microtask::{MicrotaskQueue, Microtask};
 use msg::constellation_msg::{BrowsingContextId, FrameType, PipelineId, PipelineNamespace, TopLevelBrowsingContextId};
 use net_traits::{FetchMetadata, FetchResponseListener, FetchResponseMsg};
 use net_traits::{Metadata, NetworkError, ReferrerPolicy, ResourceThreads};
 use net_traits::image_cache::{ImageCache, PendingImageResponse};
 use net_traits::request::{CredentialsMode, Destination, RedirectMode, RequestInit};
@@ -1501,17 +1501,17 @@ impl ScriptThread {
     }
 
     fn collect_reports(&self, reports_chan: ReportsChan) {
         let mut path_seg = String::from("url(");
         let mut dom_tree_size = 0;
         let mut reports = vec![];
         // Servo uses vanilla jemalloc, which doesn't have a
         // malloc_enclosing_size_of function.
-        let mut ops = MallocSizeOfOps::new(malloc_size_of, None, None);
+        let mut ops = MallocSizeOfOps::new(::servo_allocator::usable_size, None, None);
 
         for (_, document) in self.documents.borrow().iter() {
             let current_url = document.url();
 
             for child in document.upcast::<Node>().traverse_preorder() {
                 dom_tree_size += malloc_size_of_including_self(&mut ops, &*child);
             }
             dom_tree_size += malloc_size_of_including_self(&mut ops, document.window());
--- a/servo/components/selectors/context.rs
+++ b/servo/components/selectors/context.rs
@@ -1,15 +1,16 @@
 /* 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 attr::CaseSensitivity;
 use bloom::BloomFilter;
 use nth_index_cache::NthIndexCache;
+use parser::SelectorImpl;
 use tree::OpaqueElement;
 
 /// What kind of selector matching mode we should use.
 ///
 /// There are two modes of selector matching. The difference is only noticeable
 /// in presence of pseudo-elements.
 #[derive(Clone, Copy, Debug, PartialEq)]
 pub enum MatchingMode {
@@ -69,17 +70,20 @@ impl QuirksMode {
             QuirksMode::Quirks => CaseSensitivity::AsciiCaseInsensitive,
         }
     }
 }
 
 /// Data associated with the matching process for a element.  This context is
 /// used across many selectors for an element, so it's not appropriate for
 /// transient data that applies to only a single selector.
-pub struct MatchingContext<'a> {
+pub struct MatchingContext<'a, Impl>
+where
+    Impl: SelectorImpl,
+{
     /// Input with the matching mode we should use when matching selectors.
     pub matching_mode: MatchingMode,
     /// Input with the bloom filter used to fast-reject selectors.
     pub bloom_filter: Option<&'a BloomFilter>,
     /// An optional cache to speed up nth-index-like selectors.
     pub nth_index_cache: Option<&'a mut NthIndexCache>,
     /// Input that controls how matching for links is handled.
     pub visited_handling: VisitedHandlingMode,
@@ -102,19 +106,23 @@ pub struct MatchingContext<'a> {
     /// See https://drafts.csswg.org/selectors-4/#scope-pseudo
     pub scope_element: Option<OpaqueElement>,
 
     /// The current nesting level of selectors that we're matching.
     pub nesting_level: usize,
 
     quirks_mode: QuirksMode,
     classes_and_ids_case_sensitivity: CaseSensitivity,
+    _impl: ::std::marker::PhantomData<Impl>,
 }
 
-impl<'a> MatchingContext<'a> {
+impl<'a, Impl> MatchingContext<'a, Impl>
+where
+    Impl: SelectorImpl,
+{
     /// Constructs a new `MatchingContext`.
     pub fn new(
         matching_mode: MatchingMode,
         bloom_filter: Option<&'a BloomFilter>,
         nth_index_cache: Option<&'a mut NthIndexCache>,
         quirks_mode: QuirksMode,
     ) -> Self {
         Self::new_for_visited(
@@ -139,16 +147,17 @@ impl<'a> MatchingContext<'a> {
             bloom_filter,
             visited_handling,
             nth_index_cache,
             quirks_mode,
             relevant_link_found: false,
             classes_and_ids_case_sensitivity: quirks_mode.classes_and_ids_case_sensitivity(),
             scope_element: None,
             nesting_level: 0,
+            _impl: ::std::marker::PhantomData,
         }
     }
 
     /// The quirks mode of the document.
     #[inline]
     pub fn quirks_mode(&self) -> QuirksMode {
         self.quirks_mode
     }
--- a/servo/components/selectors/matching.rs
+++ b/servo/components/selectors/matching.rs
@@ -50,25 +50,25 @@ impl ElementSelectorFlags {
 
     /// Returns the subset of flags that apply to the parent.
     pub fn for_parent(self) -> ElementSelectorFlags {
         self & (HAS_SLOW_SELECTOR | HAS_SLOW_SELECTOR_LATER_SIBLINGS | HAS_EDGE_CHILD_SELECTOR)
     }
 }
 
 /// Holds per-compound-selector data.
-struct LocalMatchingContext<'a, 'b: 'a> {
-    shared: &'a mut MatchingContext<'b>,
+struct LocalMatchingContext<'a, 'b: 'a, Impl: SelectorImpl> {
+    shared: &'a mut MatchingContext<'b, Impl>,
     matches_hover_and_active_quirk: bool,
 }
 
 pub fn matches_selector_list<E>(
     selector_list: &SelectorList<E::Impl>,
     element: &E,
-    context: &mut MatchingContext,
+    context: &mut MatchingContext<E::Impl>,
 ) -> bool
 where
     E: Element
 {
     selector_list.0.iter().any(|selector| {
         matches_selector(selector,
                          0,
                          None,
@@ -136,19 +136,23 @@ impl Default for RelevantLinkStatus {
         RelevantLinkStatus::NotLooking
     }
 }
 
 impl RelevantLinkStatus {
     /// If we found the relevant link for this element, record that in the
     /// overall matching context for the element as a whole and stop looking for
     /// addtional links.
-    fn examine_potential_link<E>(&self, element: &E, context: &mut MatchingContext)
-                                 -> RelevantLinkStatus
-        where E: Element,
+    fn examine_potential_link<E>(
+        &self,
+        element: &E,
+        context: &mut MatchingContext<E::Impl>,
+    ) -> RelevantLinkStatus
+    where
+        E: Element,
     {
         // If a relevant link was previously found, we no longer want to look
         // for links.  Only the nearest ancestor link is considered relevant.
         if *self != RelevantLinkStatus::Looking {
             return RelevantLinkStatus::NotLooking
         }
 
         if !element.is_link() {
@@ -163,18 +167,23 @@ impl RelevantLinkStatus {
         // specific selector's matching process.
         RelevantLinkStatus::Found
     }
 
     /// Returns whether an element is considered visited for the purposes of
     /// matching.  This is true only if the element is a link, an relevant link
     /// exists for the element, and the visited handling mode is set to accept
     /// relevant links as visited.
-    pub fn is_visited<E>(&self, element: &E, context: &MatchingContext) -> bool
-        where E: Element,
+    pub fn is_visited<E>(
+        &self,
+        element: &E,
+        context: &MatchingContext<E::Impl>,
+    ) -> bool
+    where
+        E: Element,
     {
         if !element.is_link() {
             return false
         }
 
         if context.visited_handling == VisitedHandlingMode::AllLinksVisitedAndUnvisited {
             return true;
         }
@@ -188,18 +197,23 @@ impl RelevantLinkStatus {
     }
 
     /// Returns whether an element is considered unvisited for the purposes of
     /// matching.  Assuming the element is a link, this is always true for
     /// non-relevant links, since only relevant links can potentially be treated
     /// as visited.  If this is a relevant link, then is it unvisited if the
     /// visited handling mode is set to treat all links as unvisted (including
     /// relevant links).
-    pub fn is_unvisited<E>(&self, element: &E, context: &MatchingContext) -> bool
-        where E: Element,
+    pub fn is_unvisited<E>(
+        &self,
+        element: &E,
+        context: &MatchingContext<E::Impl>
+    ) -> bool
+    where
+        E: Element,
     {
         if !element.is_link() {
             return false
         }
 
         if context.visited_handling == VisitedHandlingMode::AllLinksVisitedAndUnvisited {
             return true;
         }
@@ -272,17 +286,17 @@ enum SelectorMatchingResult {
 /// unncessary cache miss for cases when we can fast-reject with AncestorHashes
 /// (which the caller can store inline with the selector pointer).
 #[inline(always)]
 pub fn matches_selector<E, F>(
     selector: &Selector<E::Impl>,
     offset: usize,
     hashes: Option<&AncestorHashes>,
     element: &E,
-    context: &mut MatchingContext,
+    context: &mut MatchingContext<E::Impl>,
     flags_setter: &mut F,
 ) -> bool
 where
     E: Element,
     F: FnMut(&E, ElementSelectorFlags),
 {
     // Use the bloom filter to fast-reject.
     if let Some(hashes) = hashes {
@@ -313,17 +327,17 @@ pub enum CompoundSelectorMatchingResult 
 ///
 /// Requires that `from_offset` points to a `Combinator`.
 ///
 /// NOTE(emilio): This doesn't allow to match in the leftmost sequence of the
 /// complex selector, but it happens to be the case we don't need it.
 pub fn matches_compound_selector<E>(
     selector: &Selector<E::Impl>,
     mut from_offset: usize,
-    context: &mut MatchingContext,
+    context: &mut MatchingContext<E::Impl>,
     element: &E,
 ) -> CompoundSelectorMatchingResult
 where
     E: Element
 {
     debug_assert_ne!(from_offset, 0);
     if cfg!(debug_assertions) {
         selector.combinator_at_parse_order(from_offset - 1); // This asserts.
@@ -356,17 +370,17 @@ where
 
     CompoundSelectorMatchingResult::FullyMatched
 }
 
 /// Matches a complex selector.
 pub fn matches_complex_selector<E, F>(
     mut iter: SelectorIter<E::Impl>,
     element: &E,
-    context: &mut MatchingContext,
+    context: &mut MatchingContext<E::Impl>,
     flags_setter: &mut F,
 ) -> bool
 where
     E: Element,
     F: FnMut(&E, ElementSelectorFlags),
 {
     // If this is the special pseudo-element mode, consume the ::pseudo-element
     // before proceeding, since the caller has already handled that part.
@@ -405,17 +419,17 @@ where
         SelectorMatchingResult::Matched => true,
         _ => false
     }
 }
 
 #[inline]
 fn matches_hover_and_active_quirk<Impl: SelectorImpl>(
     selector_iter: &SelectorIter<Impl>,
-    context: &MatchingContext,
+    context: &MatchingContext<Impl>,
     rightmost: Rightmost,
 ) -> bool {
     if context.quirks_mode() != QuirksMode::Quirks {
         return false;
     }
 
     if context.nesting_level != 0 {
         return false;
@@ -460,17 +474,17 @@ fn matches_hover_and_active_quirk<Impl: 
 enum Rightmost {
     Yes,
     No,
 }
 
 fn matches_complex_selector_internal<E, F>(
     mut selector_iter: SelectorIter<E::Impl>,
     element: &E,
-    context: &mut MatchingContext,
+    context: &mut MatchingContext<E::Impl>,
     relevant_link: &mut RelevantLinkStatus,
     flags_setter: &mut F,
     rightmost: Rightmost,
 ) -> SelectorMatchingResult
 where
     E: Element,
     F: FnMut(&E, ElementSelectorFlags),
 {
@@ -584,17 +598,17 @@ where
     }
 }
 
 /// Determines whether the given element matches the given single selector.
 #[inline]
 fn matches_simple_selector<E, F>(
     selector: &Component<E::Impl>,
     element: &E,
-    context: &mut LocalMatchingContext,
+    context: &mut LocalMatchingContext<E::Impl>,
     relevant_link: &RelevantLinkStatus,
     flags_setter: &mut F,
 ) -> bool
 where
     E: Element,
     F: FnMut(&E, ElementSelectorFlags),
 {
     match *selector {
@@ -756,17 +770,17 @@ fn select_name<'a, T>(is_html: bool, loc
     } else {
         local_name
     }
 }
 
 #[inline]
 fn matches_generic_nth_child<E, F>(
     element: &E,
-    context: &mut LocalMatchingContext,
+    context: &mut LocalMatchingContext<E::Impl>,
     a: i32,
     b: i32,
     is_of_type: bool,
     is_from_end: bool,
     flags_setter: &mut F,
 ) -> bool
 where
     E: Element,
--- a/servo/components/selectors/tree.rs
+++ b/servo/components/selectors/tree.rs
@@ -62,27 +62,28 @@ pub trait Element: Sized + Clone + Debug
                     ns: &NamespaceConstraint<&<Self::Impl as SelectorImpl>::NamespaceUrl>,
                     local_name: &<Self::Impl as SelectorImpl>::LocalName,
                     operation: &AttrSelectorOperation<&<Self::Impl as SelectorImpl>::AttrValue>)
                     -> bool;
 
     fn match_non_ts_pseudo_class<F>(
         &self,
         pc: &<Self::Impl as SelectorImpl>::NonTSPseudoClass,
-        context: &mut MatchingContext,
+        context: &mut MatchingContext<Self::Impl>,
         relevant_link: &RelevantLinkStatus,
         flags_setter: &mut F,
     ) -> bool
     where
         F: FnMut(&Self, ElementSelectorFlags);
 
-    fn match_pseudo_element(&self,
-                            pe: &<Self::Impl as SelectorImpl>::PseudoElement,
-                            context: &mut MatchingContext)
-                            -> bool;
+    fn match_pseudo_element(
+        &self,
+        pe: &<Self::Impl as SelectorImpl>::PseudoElement,
+        context: &mut MatchingContext<Self::Impl>,
+    ) -> bool;
 
     /// Whether this element is a `link`.
     fn is_link(&self) -> bool;
 
     fn has_id(&self,
               id: &<Self::Impl as SelectorImpl>::Identifier,
               case_sensitivity: CaseSensitivity)
               -> bool;
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -1829,17 +1829,17 @@ impl<'le> ::selectors::Element for Gecko
         unsafe {
             WeakNamespace::new(Gecko_Namespace(self.0))
         }
     }
 
     fn match_non_ts_pseudo_class<F>(
         &self,
         pseudo_class: &NonTSPseudoClass,
-        context: &mut MatchingContext,
+        context: &mut MatchingContext<Self::Impl>,
         relevant_link: &RelevantLinkStatus,
         flags_setter: &mut F,
     ) -> bool
     where
         F: FnMut(&Self, ElementSelectorFlags),
     {
         use selectors::matching::*;
         match *pseudo_class {
@@ -1970,17 +1970,17 @@ impl<'le> ::selectors::Element for Gecko
                 }
             }
         }
     }
 
     fn match_pseudo_element(
         &self,
         pseudo_element: &PseudoElement,
-        _context: &mut MatchingContext
+        _context: &mut MatchingContext<Self::Impl>,
     ) -> bool {
         // TODO(emilio): I believe we could assert we are a pseudo-element and
         // match the proper pseudo-element, given how we rulehash the stuff
         // based on the pseudo.
         match self.implemented_pseudo_element() {
             Some(ref pseudo) => *pseudo == pseudo_element.canonical(),
             None => false,
         }
--- a/servo/components/style/invalidation/element/element_wrapper.rs
+++ b/servo/components/style/invalidation/element/element_wrapper.rs
@@ -147,17 +147,17 @@ impl<'a, E> fmt::Debug for ElementWrappe
 impl<'a, E> Element for ElementWrapper<'a, E>
     where E: TElement,
 {
     type Impl = SelectorImpl;
 
     fn match_non_ts_pseudo_class<F>(
         &self,
         pseudo_class: &NonTSPseudoClass,
-        context: &mut MatchingContext,
+        context: &mut MatchingContext<Self::Impl>,
         relevant_link: &RelevantLinkStatus,
         _setter: &mut F,
     ) -> bool
     where
          F: FnMut(&Self, ElementSelectorFlags),
     {
         // Some pseudo-classes need special handling to evaluate them against
         // the snapshot.
@@ -253,17 +253,17 @@ impl<'a, E> Element for ElementWrapper<'
                 )
             }
         }
     }
 
     fn match_pseudo_element(
         &self,
         pseudo_element: &PseudoElement,
-        context: &mut MatchingContext,
+        context: &mut MatchingContext<Self::Impl>,
     ) -> bool {
         self.element.match_pseudo_element(pseudo_element, context)
     }
 
     fn is_link(&self) -> bool {
         self.element.is_link()
     }