Merge mozilla-central to inbound. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Sat, 08 Dec 2018 00:18:17 +0200
changeset 508906 904080f44dbd2ae3cc92c794a817b281d6cb7237
parent 508905 b2eb9000daed2a2cff623eae8ca2387bb59e4601 (current diff)
parent 508866 ca0f00593e38cdab54c3a990059bbf1150e77365 (diff)
child 508907 a72dafbb2e8089c38cf155f846e2256b90c1aa9e
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. a=merge CLOSED TREE
gfx/layers/ipc/LayersMessageUtils.h
toolkit/themes/linux/mozapps/extensions/heart.png
toolkit/themes/osx/mozapps/extensions/heart.png
toolkit/themes/windows/mozapps/extensions/heart.png
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -346,27 +346,16 @@ toolbarpaletteitem {
 }
 
 #titlebar-secondary-buttonbox:-moz-locale-dir(rtl),
 .titlebar-buttonbox-container:-moz-locale-dir(ltr) {
   -moz-box-ordinal-group: 0;
 }
 %endif
 
-%ifdef XP_WIN
-:root[sizemode="maximized"] #titlebar-buttonbox {
-  -moz-appearance: -moz-window-button-box-maximized;
-}
-
-:root[tabletmode] #titlebar-min,
-:root[tabletmode] #titlebar-max {
-  display: none !important;
-}
-%endif
-
 :root[inDOMFullscreen] #navigator-toolbox,
 :root[inDOMFullscreen] #fullscr-toggler,
 :root[inDOMFullscreen] #sidebar-box,
 :root[inDOMFullscreen] #sidebar-splitter,
 :root[inFullscreen]:not([OSXLionFullscreen]) toolbar:not([fullscreentoolbar=true]),
 :root[inFullscreen] .global-notificationbox {
   visibility: collapse;
 }
--- a/browser/components/extensions/parent/ext-menus.js
+++ b/browser/components/extensions/parent/ext-menus.js
@@ -650,21 +650,27 @@ MenuItem.prototype = {
       this[propName] = createProperties[propName];
     }
 
     if ("icons" in createProperties && createProperties.icons === null) {
       this.icons = null;
     }
 
     if (createProperties.documentUrlPatterns != null) {
-      this.documentUrlMatchPattern = new MatchPatternSet(this.documentUrlPatterns);
+      this.documentUrlMatchPattern = new MatchPatternSet(this.documentUrlPatterns, {
+        restrictSchemes: this.extension.restrictSchemes,
+      });
     }
 
     if (createProperties.targetUrlPatterns != null) {
-      this.targetUrlMatchPattern = new MatchPatternSet(this.targetUrlPatterns, {restrictSchemes: false});
+      this.targetUrlMatchPattern = new MatchPatternSet(this.targetUrlPatterns, {
+        // restrictSchemes default to false when matching links instead of pages
+        // (see Bug 1280370 for a rationale).
+        restrictSchemes: false,
+      });
     }
 
     // If a child MenuItem does not specify any contexts, then it should
     // inherit the contexts specified from its parent.
     if (createProperties.parentId && !createProperties.contexts) {
       this.contexts = this.parent.contexts;
     }
   },
--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js
+++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_targetUrlPatterns.js
@@ -184,8 +184,84 @@ add_task(async function unsupportedSchem
   await extension.startup();
   await extension.awaitMessage("ready");
   await openExtensionContextMenu("#test_link_element");
   await extension.awaitMessage("done");
   await closeContextMenu();
 
   await extension.unload();
 });
+
+add_task(async function privileged_are_allowed_to_use_restrictedSchemes() {
+  let privilegedExtension = ExtensionTestUtils.loadExtension({
+    isPrivileged: true,
+    manifest: {
+      permissions: ["tabs", "contextMenus", "mozillaAddons"],
+    },
+    async background() {
+      browser.contextMenus.create({
+        id: "privileged-extension",
+        title: "Privileged Extension",
+        contexts: ["page"],
+        documentUrlPatterns: ["about:reader*"],
+      });
+
+      browser.tabs.onUpdated.addListener(async (tabId, changeInfo) => {
+        if (changeInfo.url && changeInfo.url.startsWith("about:reader")) {
+          browser.test.sendMessage("readerModeEntered");
+        }
+      });
+
+      browser.test.onMessage.addListener(async (msg) => {
+        if (msg !== "enterReaderMode") {
+          browser.test.fail(`Received unexpected test message: ${msg}`);
+          return;
+        }
+
+        browser.tabs.toggleReaderMode();
+      });
+    },
+  });
+
+  let nonPrivilegedExtension = ExtensionTestUtils.loadExtension({
+    isPrivileged: false,
+    manifest: {
+      permissions: ["contextMenus", "mozillaAddons"],
+    },
+    async background() {
+      browser.contextMenus.create({
+        id: "non-privileged-extension",
+        title: "Non Privileged Extension",
+        contexts: ["page"],
+        documentUrlPatterns: ["about:reader*"],
+      });
+    },
+  });
+
+  const baseUrl = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
+  const url = `${baseUrl}/readerModeArticle.html`;
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true, true);
+
+  await Promise.all([
+    privilegedExtension.startup(),
+    nonPrivilegedExtension.startup(),
+  ]);
+
+  privilegedExtension.sendMessage("enterReaderMode");
+  await privilegedExtension.awaitMessage("readerModeEntered");
+
+  const contextMenu = await openContextMenu("body > h1");
+
+  let item = contextMenu.getElementsByAttribute("label", "Privileged Extension");
+  is(item.length, 1, "Privileged extension's contextMenu item found as expected");
+
+  item = contextMenu.getElementsByAttribute("label", "Non Privileged Extension");
+  is(item.length, 0, "Non privileged extension's contextMenu not found as expected");
+
+  await closeContextMenu();
+
+  BrowserTestUtils.removeTab(tab);
+
+  await Promise.all([
+    privilegedExtension.unload(),
+    nonPrivilegedExtension.unload(),
+  ]);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/search/.eslintrc.js
@@ -0,0 +1,31 @@
+"use strict";
+
+module.exports = {
+  rules: {
+    "mozilla/var-only-at-top-level": "error",
+    "require-jsdoc": ["error", {
+        "require": {
+            "FunctionDeclaration": false,
+            "MethodDefinition": false,
+            "ClassDeclaration": true,
+            "ArrowFunctionExpression": false,
+            "FunctionExpression": false
+        }
+    }],
+    "valid-jsdoc": ["error", {
+      prefer: {
+        return: "returns",
+      },
+      preferType: {
+        Boolean: "boolean",
+        Number: "number",
+        String: "string",
+        Object: "object",
+        bool: "boolean",
+      },
+      requireParamDescription: false,
+      requireReturn: false,
+      requireReturnDescription: false,
+    }],
+  }
+};
--- a/browser/components/search/content/search-one-offs.js
+++ b/browser/components/search/content/search-one-offs.js
@@ -2,16 +2,20 @@
  * 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";
 
 /* eslint-env mozilla/browser-window */
 /* globals XULCommandEvent */
 
+/**
+ * Defines the search one-off button elements. These are displayed at the bottom
+ * of the address bar or the search bar.
+ */
 class SearchOneOffs {
   constructor(container) {
     this.container = container;
 
     this.container.appendChild(MozXULElement.parseXULToFragment(`
       <deck class="search-panel-one-offs-header search-panel-header search-panel-current-input">
         <label class="searchbar-oneoffheader-search" value="&searchWithHeader.label;"/>
         <hbox class="search-panel-searchforwith search-panel-current-input">
@@ -161,20 +165,24 @@ class SearchOneOffs {
   /**
    * Width in pixels of the one-off buttons.  49px is the min-width of
    * each search engine button, adapt this const when changing the css.
    * It's actually 48px + 1px of right border.
    */
   get buttonWidth() {
     return 49;
   }
+
   /**
    * The popup that contains the one-offs.  This is required, so it should
    * never be null or undefined, except possibly before the one-offs are
    * used.
+   *
+   * @param {DOMElement} val
+   *        The new value to set.
    */
   set popup(val) {
     let events = [
       "popupshowing",
       "popuphidden",
     ];
     if (this._popup) {
       for (let event of events) {
@@ -202,16 +210,19 @@ class SearchOneOffs {
     return this._popup;
   }
 
   /**
    * The textbox associated with the one-offs.  Set this to a textbox to
    * automatically keep the related one-offs UI up to date.  Otherwise you
    * can leave it null/undefined, and in that case you should update the
    * query property manually.
+   *
+   * @param {DOMElement} val
+   *        The new value to set.
    */
   set textbox(val) {
     if (this._textbox) {
       this._textbox.removeEventListener("input", this);
     }
     if (val) {
       val.addEventListener("input", this);
     }
@@ -225,31 +236,38 @@ class SearchOneOffs {
   get textbox() {
     return this._textbox;
   }
 
   /**
    * The query string currently shown in the one-offs.  If the textbox
    * property is non-null, then this is automatically updated on
    * input.
+   *
+   * @param {string} val
+   *        The new query string to set.
    */
   set query(val) {
     this._query = val;
     if (this.popup && this.popup.popupOpen) {
       this._updateAfterQueryChanged();
     }
     return val;
   }
 
   get query() {
     return this._query;
   }
+
   /**
    * The selected one-off, a xul:button, including the add-engine button
-   * and the search-settings button.  Null if no one-off is selected.
+   * and the search-settings button.
+   *
+   * @param {DOMElement|null} val
+   *        The selected one-off button. Null if no one-off is selected.
    */
   set selectedButton(val) {
     if (val && val.classList.contains("dummy")) {
       // Never select dummy buttons.
       val = null;
     }
     let previousButton = this._selectedButton;
     if (previousButton) {
@@ -271,19 +289,23 @@ class SearchOneOffs {
     });
     this.dispatchEvent(event);
     return val;
   }
 
   get selectedButton() {
     return this._selectedButton;
   }
+
   /**
    * The index of the selected one-off, including the add-engine button
-   * and the search-settings button.  -1 if no one-off is selected.
+   * and the search-settings button.
+   *
+   * @param {number} val
+   *        The new index to set, -1 for nothing selected.
    */
   set selectedButtonIndex(val) {
     let buttons = this.getSelectableButtons(true);
     this.selectedButton = buttons[val];
     return val;
   }
 
   get selectedButtonIndex() {
@@ -617,17 +639,17 @@ class SearchOneOffs {
     return this._popup &&
       document.getAnonymousElementByAttribute(this._popup, "id", this._buttonIDForEngine(engine));
   }
 
   /**
    * Updates the popup and textbox for the currently selected or moused-over
    * button.
    *
-   * @param mousedOverButton
+   * @param {DOMElement} mousedOverButton
    *        The currently moused-over button, or null if there isn't one.
    */
   _updateStateForButton(mousedOverButton) {
     let button = mousedOverButton;
 
     // Ignore dummy buttons.
     if (button && button.classList.contains("dummy")) {
       button = null;
@@ -691,17 +713,17 @@ class SearchOneOffs {
     if (aForceNewTab) {
       where = "tab";
       if (Services.prefs.getBoolPref("browser.tabs.loadInBackground")) {
         params = {
           inBackground: true,
         };
       }
     } else {
-      var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
+      let newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
       if ((aEvent instanceof KeyboardEvent && aEvent.altKey) ^ newTabPref &&
           !gBrowser.selectedTab.isEmpty) {
         where = "tab";
       }
       if (aEvent instanceof MouseEvent &&
           (aEvent.button == 1 || aEvent.getModifierState("Accel"))) {
         where = "tab";
         params = {
@@ -711,33 +733,31 @@ class SearchOneOffs {
     }
 
     this.popup.handleOneOffSearch(aEvent, aEngine, where, params);
   }
 
   /**
    * Increments or decrements the index of the currently selected one-off.
    *
-   * @param aForward
+   * @param {boolean} aForward
    *        If true, the index is incremented, and if false, the index is
    *        decremented.
-   * @param aIncludeNonEngineButtons
+   * @param {boolean} aIncludeNonEngineButtons
    *        If true, non-dummy buttons that do not have engines are included.
    *        These buttons include the OpenSearch and settings buttons.  For
    *        example, if the currently selected button is an engine button,
    *        the next button is the settings button, and you pass true for
    *        aForward, then passing true for this value would cause the
    *        settings to be selected.  Passing false for this value would
    *        cause the selection to clear or wrap around, depending on what
    *        value you passed for the aWrapAround parameter.
-   * @param aWrapAround
+   * @param {boolean} aWrapAround
    *        If true, the selection wraps around between the first and last
    *        buttons.
-   * @return True if the selection can continue to advance after this method
-   *         returns and false if not.
    */
   advanceSelection(aForward, aIncludeNonEngineButtons, aWrapAround) {
     let buttons = this.getSelectableButtons(aIncludeNonEngineButtons);
     let index;
     if (this.selectedButton) {
       let inc = aForward ? 1 : -1;
       let oldIndex = buttons.indexOf(this.selectedButton);
       index = (oldIndex + inc + buttons.length) % buttons.length;
@@ -759,28 +779,28 @@ class SearchOneOffs {
    * Alt+Up/Down, and Up/Down keys within the buttons.  Since one-off buttons
    * are always used in conjunction with a list of some sort (in this.popup),
    * it also handles Up/Down keys that cross the boundaries between list
    * items and the one-off buttons.
    *
    * If this method handles the key press, then event.defaultPrevented will
    * be true when it returns.
    *
-   * @param event
+   * @param {Event} event
    *        The key event.
-   * @param numListItems
+   * @param {number} numListItems
    *        The number of items in the list.  The reason that this is a
    *        parameter at all is that the list may contain items at the end
    *        that should be ignored, depending on the consumer.  That's true
    *        for the urlbar for example.
-   * @param allowEmptySelection
+   * @param {boolean} allowEmptySelection
    *        Pass true if it's OK that neither the list nor the one-off
    *        buttons contains a selection.  Pass false if either the list or
    *        the one-off buttons (or both) should always contain a selection.
-   * @param textboxUserValue
+   * @param {string} [textboxUserValue]
    *        When the last list item is selected and the user presses Down,
    *        the first one-off becomes selected and the textbox value is
    *        restored to the value that the user typed.  Pass that value here.
    *        However, if you pass true for allowEmptySelection, you don't need
    *        to pass anything for this parameter.  (Pass undefined or null.)
    */
   handleKeyPress(event, numListItems, allowEmptySelection, textboxUserValue) {
     if (!this.popup) {
@@ -960,23 +980,23 @@ class SearchOneOffs {
     return false;
   }
 
   /**
    * If the given event is related to the one-offs, this method records
    * one-off telemetry for it.  this.telemetryOrigin will be appended to the
    * computed source, so make sure you set that first.
    *
-   * @param aEvent
+   * @param {Event} aEvent
    *        An event, like a click on a one-off button.
-   * @param aOpenUILinkWhere
+   * @param {string} aOpenUILinkWhere
    *        The "where" passed to openUILink.
-   * @param aOpenUILinkParams
+   * @param {object} aOpenUILinkParams
    *        The "params" passed to openUILink.
-   * @return True if telemetry was recorded and false if not.
+   * @returns {boolean} True if telemetry was recorded and false if not.
    */
   maybeRecordTelemetry(aEvent, aOpenUILinkWhere, aOpenUILinkParams) {
     if (!aEvent) {
       return false;
     }
 
     let source = null;
     let type = "unknown";
@@ -1228,9 +1248,8 @@ class SearchOneOffs {
     Services.tm.dispatchToMainThread(() => {
       this.selectedButton = null;
       this._contextEngine = null;
     });
   }
 }
 
 window.SearchOneOffs = SearchOneOffs;
-
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -22,22 +22,22 @@
       <constructor><![CDATA[
         if (this.closest("searchbar").parentNode.parentNode.localName ==
             "toolbarpaletteitem")
           return;
 
         if (Services.prefs.getBoolPref("browser.urlbar.clickSelectsAll"))
           this.setAttribute("clickSelectsAll", true);
 
-        var textBox = document.getAnonymousElementByAttribute(this,
+        let textBox = document.getAnonymousElementByAttribute(this,
                                               "anonid", "moz-input-box");
 
         // Force the Custom Element to upgrade until Bug 1470242 handles this:
         customElements.upgrade(textBox);
-        var cxmenu = textBox.menupopup;
+        let cxmenu = textBox.menupopup;
         cxmenu.addEventListener("popupshowing",
                                 () => { this.initContextMenu(cxmenu); },
                                 {capture: true, once: true});
 
         this.setAttribute("aria-owns", this.popup.id);
         this.closest("searchbar")._textboxInitialized = true;
       ]]></constructor>
 
@@ -162,17 +162,17 @@
         <body><![CDATA[
           // Entering customization mode after the search bar had focus causes
           // the popup to appear again, due to focus returning after the
           // hamburger panel closes. Don't open in that spurious event.
           if (document.documentElement.getAttribute("customizing") == "true") {
             return;
           }
 
-          var popup = this.popup;
+          let popup = this.popup;
           if (!popup.mPopupOpen) {
             // Initially the panel used for the searchbar (PopupSearchAutoComplete
             // in browser.xul) is hidden to avoid impacting startup / new
             // window performance. The base binding's openPopup would normally
             // call the overriden openAutocompletePopup in
             // browser-search-autocomplete-result-popup binding to unhide the popup,
             // but since we're overriding openPopup we need to unhide the panel
             // ourselves.
@@ -184,27 +184,27 @@
             }
 
             popup.mInput = this;
             // clear any previous selection, see bugs 400671 and 488357
             popup.selectedIndex = -1;
 
             document.popupNode = null;
 
-            var outerRect = this.getBoundingClientRect();
-            var innerRect = this.inputField.getBoundingClientRect();
+            let outerRect = this.getBoundingClientRect();
+            let innerRect = this.inputField.getBoundingClientRect();
             let width = RTL_UI ?
                         innerRect.right - outerRect.left :
                         outerRect.right - innerRect.left;
             popup.setAttribute("width", width > 100 ? width : 100);
 
             // invalidate() depends on the width attribute
             popup._invalidate();
 
-            var yOffset = outerRect.bottom - innerRect.bottom;
+            let yOffset = outerRect.bottom - innerRect.bottom;
             popup.openPopup(this.inputField, "after_start", 0, yOffset, false, false);
           }
         ]]></body>
       </method>
 
       <method name="openSearch">
         <body>
           <![CDATA[
@@ -294,17 +294,17 @@
 
         isCommandEnabled(aCommand) {
           return true;
         },
 
         doCommand(aCommand) {
           switch (aCommand) {
             case "cmd_clearhistory":
-              var param = this._self.getAttribute("autocompletesearchparam");
+              let param = this._self.getAttribute("autocompletesearchparam");
 
               BrowserSearch.searchBar.FormHistory.update({ op: "remove", fieldname: param }, null);
               this._self.value = "";
               break;
             case "cmd_togglesuggest":
               let enabled =
                 Services.prefs.getBoolPref("browser.search.suggest.enabled");
               Services.prefs.setBoolPref("browser.search.suggest.enabled",
@@ -338,26 +338,26 @@
                action="return this.openSearch();"/>
 
       <handler event="keypress" keycode="VK_UP" modifiers="alt"
                phase="capturing"
                action="return this.openSearch();"/>
 
       <handler event="dragover">
       <![CDATA[
-        var types = event.dataTransfer.types;
+        let types = event.dataTransfer.types;
         if (types.includes("text/plain") || types.includes("text/x-moz-text-internal"))
           event.preventDefault();
       ]]>
       </handler>
 
       <handler event="drop">
       <![CDATA[
-        var dataTransfer = event.dataTransfer;
-        var data = dataTransfer.getData("text/plain");
+        let dataTransfer = event.dataTransfer;
+        let data = dataTransfer.getData("text/plain");
         if (!data)
           data = dataTransfer.getData("text/x-moz-text-internal");
         if (data) {
           event.preventDefault();
           this.value = data;
           this.closest("searchbar").openSuggestionsPanel();
         }
       ]]>
@@ -393,18 +393,18 @@
 
       <method name="onPopupClick">
         <parameter name="aEvent"/>
         <body><![CDATA[
           // Ignore all right-clicks
           if (aEvent.button == 2)
             return;
 
-          var searchBar = BrowserSearch.searchBar;
-          var popupForSearchBar = searchBar && searchBar.textbox == this.mInput;
+          let searchBar = BrowserSearch.searchBar;
+          let popupForSearchBar = searchBar && searchBar.textbox == this.mInput;
           if (popupForSearchBar) {
             searchBar.telemetrySearchDetails = {
               index: this.selectedIndex,
               kind: "mouse",
             };
           }
 
           // Check for unmodified left-click, and use default behavior
@@ -417,20 +417,20 @@
           // Check for middle-click or modified clicks on the search bar
           if (popupForSearchBar) {
             BrowserUsageTelemetry.recordSearchbarSelectedResultMethod(
               aEvent,
               this.selectedIndex
             );
 
             // Handle search bar popup clicks
-            var search = this.input.controller.getValueAt(this.selectedIndex);
+            let search = this.input.controller.getValueAt(this.selectedIndex);
 
             // open the search results according to the clicking subtlety
-            var where = whereToOpenLink(aEvent, false, true);
+            let where = whereToOpenLink(aEvent, false, true);
             let params = {};
 
             // But open ctrl/cmd clicks on autocomplete items in a new background tab.
             let modifier = AppConstants.platform == "macosx" ?
                            aEvent.metaKey :
                            aEvent.ctrlKey;
             if (where == "tab" && (aEvent instanceof MouseEvent) &&
                 (aEvent.button == 1 || modifier))
--- a/browser/components/search/content/searchbar.js
+++ b/browser/components/search/content/searchbar.js
@@ -18,21 +18,24 @@ const inheritsMap = {
 function inheritAttribute(parent, child, attr) {
   if (!parent.hasAttribute(attr)) {
     child.removeAttribute(attr);
   } else {
     child.setAttribute(attr, parent.getAttribute(attr));
   }
 }
 
+/**
+ * Defines the search bar element.
+ */
 class MozSearchbar extends MozXULElement {
 
   static get observedAttributes() {
     let unique = new Set();
-    for (var i in inheritsMap) {
+    for (let i in inheritsMap) {
       inheritsMap[i].forEach(attr => unique.add(attr));
     }
     return Array.from(unique);
   }
 
   attributeChangedCallback() {
     this.inheritAttributes();
   }
@@ -157,17 +160,17 @@ class MozSearchbar extends MozXULElement
   }
 
   set currentEngine(val) {
     Services.search.defaultEngine = val;
     return val;
   }
 
   get currentEngine() {
-    var currentEngine = Services.search.defaultEngine;
+    let currentEngine = Services.search.defaultEngine;
     // Return a dummy engine if there is no currentEngine
     return currentEngine || { name: "", uri: null };
   }
   /**
    * textbox is used by sanitize.js to clear the undo history when
    * clearing form information.
    */
   get textbox() {
@@ -208,21 +211,21 @@ class MozSearchbar extends MozXULElement
     this._textbox.select();
   }
 
   setIcon(element, uri) {
     element.setAttribute("src", uri);
   }
 
   updateDisplay() {
-    var uri = this.currentEngine.iconURI;
+    let uri = this.currentEngine.iconURI;
     this.setIcon(this, uri ? uri.spec : "");
 
-    var name = this.currentEngine.name;
-    var text = this._stringBundle.getFormattedString("searchtip", [name]);
+    let name = this.currentEngine.name;
+    let text = this._stringBundle.getFormattedString("searchtip", [name]);
     this._textbox.label = text;
     this._textbox.tooltipText = text;
   }
 
   updateGoButtonVisibility() {
     this.querySelector(".search-go-button").hidden = !this._textbox.value;
   }
 
@@ -238,44 +241,44 @@ class MozSearchbar extends MozXULElement
       this._textbox.mController.handleText();
     } else if (aShowOnlySettingsIfEmpty) {
       this.setAttribute("showonlysettings", "true");
     }
   }
 
   selectEngine(aEvent, isNextEngine) {
     // Find the new index
-    var newIndex = this.engines.indexOf(this.currentEngine);
+    let newIndex = this.engines.indexOf(this.currentEngine);
     newIndex += isNextEngine ? 1 : -1;
 
     if (newIndex >= 0 && newIndex < this.engines.length) {
       this.currentEngine = this.engines[newIndex];
     }
 
     aEvent.preventDefault();
     aEvent.stopPropagation();
 
     this.openSuggestionsPanel();
   }
 
   handleSearchCommand(aEvent, aEngine, aForceNewTab) {
-    var where = "current";
+    let where = "current";
     let params;
 
     // Open ctrl/cmd clicks on one-off buttons in a new background tab.
     if (aEvent && aEvent.originalTarget.classList.contains("search-go-button")) {
       if (aEvent.button == 2)
         return;
       where = whereToOpenLink(aEvent, false, true);
     } else if (aForceNewTab) {
       where = "tab";
       if (Services.prefs.getBoolPref("browser.tabs.loadInBackground"))
         where += "-background";
     } else {
-      var newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
+      let newTabPref = Services.prefs.getBoolPref("browser.search.openintab");
       if (((aEvent instanceof KeyboardEvent && aEvent.altKey) ^ newTabPref) &&
         !gBrowser.selectedTab.isEmpty) {
         where = "tab";
       }
       if ((aEvent instanceof MouseEvent) &&
         (aEvent.button == 1 || aEvent.getModifierState("Accel"))) {
         where = "tab";
         params = {
@@ -283,18 +286,18 @@ class MozSearchbar extends MozXULElement
         };
       }
     }
 
     this.handleSearchCommandWhere(aEvent, aEngine, where, params);
   }
 
   handleSearchCommandWhere(aEvent, aEngine, aWhere, aParams) {
-    var textBox = this._textbox;
-    var textValue = textBox.value;
+    let textBox = this._textbox;
+    let textValue = textBox.value;
 
     let selection = this.telemetrySearchDetails;
     let oneOffRecorded = false;
 
     BrowserUsageTelemetry.recordSearchbarSelectedResultMethod(
       aEvent,
       selection ? selection.index : -1
     );
@@ -330,33 +333,33 @@ class MozSearchbar extends MozXULElement
     // This is a one-off search only if oneOffRecorded is true.
     this.doSearch(textValue, aWhere, aEngine, aParams, oneOffRecorded);
 
     if (aWhere == "tab" && aParams && aParams.inBackground)
       this.focus();
   }
 
   doSearch(aData, aWhere, aEngine, aParams, aOneOff) {
-    var textBox = this._textbox;
+    let textBox = this._textbox;
 
     // Save the current value in the form history
     if (aData && !PrivateBrowsingUtils.isWindowPrivate(window) && this.FormHistory.enabled) {
       this.FormHistory.update({
         op: "bump",
         fieldname: textBox.getAttribute("autocompletesearchparam"),
         value: aData,
       }, {
         handleError(aError) {
           Cu.reportError("Saving search to form history failed: " + aError.message);
         },
       });
     }
 
     let engine = aEngine || this.currentEngine;
-    var submission = engine.getSubmission(aData, null, "searchbar");
+    let submission = engine.getSubmission(aData, null, "searchbar");
     let telemetrySearchDetails = this.telemetrySearchDetails;
     this.telemetrySearchDetails = null;
     if (telemetrySearchDetails && telemetrySearchDetails.index == -1) {
       telemetrySearchDetails = null;
     }
     // If we hit here, we come either from a one-off, a plain search or a suggestion.
     const details = {
       isOneOff: aOneOff,
@@ -385,17 +388,17 @@ class MozSearchbar extends MozXULElement
 
   _setupEventListeners() {
     this.addEventListener("command", (event) => {
       const target = event.originalTarget;
       if (target.engine) {
         this.currentEngine = target.engine;
       } else if (target.classList.contains("addengine-item")) {
         // Select the installed engine if the installation succeeds
-        var installCallback = {
+        let installCallback = {
           onSuccess: engine => this.currentEngine = engine,
         };
         Services.search.addEngine(target.getAttribute("uri"), null,
           target.getAttribute("src"), false,
           installCallback);
       } else
         return;
 
--- a/browser/components/search/test/browser/SearchTestUtils.jsm
+++ b/browser/components/search/test/browser/SearchTestUtils.jsm
@@ -15,17 +15,17 @@ var SearchTestUtils = Object.freeze({
       registerCleanupFunction,
     };
   },
 
   /**
    * Adds a search engine to the search service. It will remove the engine
    * at the end of the test.
    *
-   * @param {String}   url                     The URL of the engine to add.
+   * @param {string}   url                     The URL of the engine to add.
    * @param {Function} registerCleanupFunction Pass the registerCleanupFunction
    *                                           from the test's scope.
    * @returns {Promise} Returns a promise that is resolved with the new engine
    *                    or rejected if it fails.
    */
   promiseNewSearchEngine(url) {
     return new Promise((resolve, reject) => {
       Services.search.addEngine(url, "", false, {
--- a/browser/components/search/test/browser/browser_426329.js
+++ b/browser/components/search/test/browser/browser_426329.js
@@ -1,37 +1,37 @@
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 ChromeUtils.defineModuleGetter(this, "FormHistory",
   "resource://gre/modules/FormHistory.jsm");
 
 function expectedURL(aSearchTerms) {
   const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/browser/test.html";
-  var searchArg = Services.textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
+  let searchArg = Services.textToSubURI.ConvertAndEscape("utf-8", aSearchTerms);
   return ENGINE_HTML_BASE + "?test=" + searchArg;
 }
 
 function simulateClick(aEvent, aTarget) {
-  var event = document.createEvent("MouseEvent");
-  var ctrlKeyArg  = aEvent.ctrlKey || false;
-  var altKeyArg   = aEvent.altKey || false;
-  var shiftKeyArg = aEvent.shiftKey || false;
-  var metaKeyArg  = aEvent.metaKey || false;
-  var buttonArg   = aEvent.button || 0;
+  let event = document.createEvent("MouseEvent");
+  let ctrlKeyArg  = aEvent.ctrlKey || false;
+  let altKeyArg   = aEvent.altKey || false;
+  let shiftKeyArg = aEvent.shiftKey || false;
+  let metaKeyArg  = aEvent.metaKey || false;
+  let buttonArg   = aEvent.button || 0;
   event.initMouseEvent("click", true, true, window,
                         0, 0, 0, 0, 0,
                         ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
                         buttonArg, null);
   aTarget.dispatchEvent(event);
 }
 
 // modified from toolkit/components/satchel/test/test_form_autocomplete.html
 function checkMenuEntries(expectedValues) {
-  var actualValues = getMenuEntries();
+  let actualValues = getMenuEntries();
   is(actualValues.length, expectedValues.length, "Checking length of expected menu");
-  for (var i = 0; i < expectedValues.length; i++)
+  for (let i = 0; i < expectedValues.length; i++)
     is(actualValues[i], expectedValues[i], "Checking menu entry #" + i);
 }
 
 function getMenuEntries() {
   // Could perhaps pull values directly from the controller, but it seems
   // more reliable to test the values that are actually in the richlistbox?
   return Array.map(searchBar.textbox.popup.richlistbox.itemChildren,
                    item => item.getAttribute("ac-value"));
@@ -53,22 +53,22 @@ function countEntries(name, value) {
   });
 }
 
 var searchBar;
 var searchButton;
 var searchEntries = ["test"];
 function promiseSetEngine() {
   return new Promise(resolve => {
-    var ss = Services.search;
+    let ss = Services.search;
 
     function observer(aSub, aTopic, aData) {
       switch (aData) {
         case "engine-added":
-          var engine = ss.getEngineByName("Bug 426329");
+          let engine = ss.getEngineByName("Bug 426329");
           ok(engine, "Engine was added.");
           ss.defaultEngine = engine;
           break;
         case "engine-current":
           ok(ss.defaultEngine.name == "Bug 426329", "currentEngine set");
           searchBar = BrowserSearch.searchBar;
           searchButton = searchBar.querySelector(".search-go-button");
           ok(searchButton, "got search-go-button");
@@ -83,27 +83,27 @@ function promiseSetEngine() {
     Services.obs.addObserver(observer, "browser-search-engine-modified");
     ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/browser/426329.xml",
                  "data:image/x-icon,%00", false);
   });
 }
 
 function promiseRemoveEngine() {
   return new Promise(resolve => {
-    var ss = Services.search;
+    let ss = Services.search;
 
     function observer(aSub, aTopic, aData) {
       if (aData == "engine-removed") {
         Services.obs.removeObserver(observer, "browser-search-engine-modified");
         resolve();
       }
     }
 
     Services.obs.addObserver(observer, "browser-search-engine-modified");
-    var engine = ss.getEngineByName("Bug 426329");
+    let engine = ss.getEngineByName("Bug 426329");
     ss.removeEngine(engine);
   });
 }
 
 
 var preSelectedBrowser;
 var preTabNo;
 async function prepareTest() {
@@ -212,25 +212,25 @@ add_task(async function testRightClick()
     simulateClick({ button: 2 }, searchButton);
   });
   // The click in the searchbox focuses it, which opens the suggestion
   // panel. Clean up after ourselves.
   searchBar.textbox.popup.hidePopup();
 });
 
 add_task(async function testSearchHistory() {
-  var textbox = searchBar._textbox;
-  for (var i = 0; i < searchEntries.length; i++) {
+  let textbox = searchBar._textbox;
+  for (let i = 0; i < searchEntries.length; i++) {
     let count = await countEntries(textbox.getAttribute("autocompletesearchparam"), searchEntries[i]);
     ok(count > 0, "form history entry '" + searchEntries[i] + "' should exist");
   }
 });
 
 add_task(async function testAutocomplete() {
-  var popup = searchBar.textbox.popup;
+  let popup = searchBar.textbox.popup;
   let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
   searchBar.textbox.showHistoryPopup();
   await popupShownPromise;
   checkMenuEntries(searchEntries);
 });
 
 add_task(async function testClearHistory() {
   // Open the textbox context menu to trigger controller attachment.
--- a/browser/components/search/test/browser/browser_addEngine.js
+++ b/browser/components/search/test/browser/browser_addEngine.js
@@ -28,17 +28,17 @@ function observer(aSubject, aTopic, aDat
   }
 
   if (method)
     gCurrentTest[method](engine);
 }
 
 function checkEngine(checkObj, engineObj) {
   info("Checking engine");
-  for (var prop in checkObj)
+  for (let prop in checkObj)
     is(checkObj[prop], engineObj[prop], prop + " is correct");
 }
 
 var gTests = [
   {
     name: "opensearch install",
     engine: {
       name: "Foo",
--- a/browser/components/search/test/browser/browser_contextmenu.js
+++ b/browser/components/search/test/browser/browser_contextmenu.js
@@ -2,17 +2,17 @@
  *  * http://creativecommons.org/publicdomain/zero/1.0/ */
 /*
  * Test searching for the selected text using the context menu
  */
 
 add_task(async function() {
   const ss = Services.search;
   const ENGINE_NAME = "Foo";
-  var contextMenu;
+  let contextMenu;
 
   // We want select events to be fired.
   await SpecialPowers.pushPrefEnv({set: [["dom.select_events.enabled", true]]});
 
   let envService = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
   let originalValue = envService.get("XPCSHELL_TEST_PROFILE_DIR");
   envService.set("XPCSHELL_TEST_PROFILE_DIR", "1");
 
@@ -23,17 +23,17 @@ add_task(async function() {
   resProt.setSubstitution("search-plugins",
                           Services.io.newURI(url));
 
   let searchDonePromise;
   await new Promise(resolve => {
     function observer(aSub, aTopic, aData) {
       switch (aData) {
         case "engine-added":
-          var engine = ss.getEngineByName(ENGINE_NAME);
+          let engine = ss.getEngineByName(ENGINE_NAME);
           ok(engine, "Engine was added.");
           ss.defaultEngine = engine;
           envService.set("XPCSHELL_TEST_PROFILE_DIR", originalValue);
           resProt.setSubstitution("search-plugins", originalSubstitution);
           break;
         case "engine-current":
           is(ss.defaultEngine.name, ENGINE_NAME, "currentEngine set");
           resolve();
@@ -61,24 +61,24 @@ add_task(async function() {
     return new Promise(resolve => {
       content.document.addEventListener("selectionchange", function() {
         resolve();
       }, {once: true});
       content.document.getSelection().selectAllChildren(content.document.body);
     });
   });
 
-  var eventDetails = { type: "contextmenu", button: 2 };
+  let eventDetails = { type: "contextmenu", button: 2 };
 
   let popupPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
   BrowserTestUtils.synthesizeMouseAtCenter("body", eventDetails, gBrowser.selectedBrowser);
   await popupPromise;
 
   info("checkContextMenu");
-  var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
+  let searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
   ok(searchItem, "Got search context menu item");
   is(searchItem.label, "Search " + ENGINE_NAME + " for \u201ctest search\u201d", "Check context menu label");
   is(searchItem.disabled, false, "Check that search context menu item is enabled");
 
   await BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     searchItem.click();
   });
 
--- a/browser/components/search/test/browser/browser_oneOffContextMenu_setDefault.js
+++ b/browser/components/search/test/browser/browser_oneOffContextMenu_setDefault.js
@@ -96,17 +96,17 @@ add_task(async function test_urlBarChang
 
   await promiseClosePopup(urlbarPopup);
 });
 
 /**
  * Promises that an engine change has happened for the current engine, which
  * has resulted in the test engine now being the current engine.
  *
- * @return {Promise} Resolved once the test engine is set as the current engine.
+ * @returns {Promise} Resolved once the test engine is set as the current engine.
  */
 function promiseCurrentEngineChanged() {
   return new Promise(resolve => {
     function observer(aSub, aTopic, aData) {
       if (aData == "engine-current") {
         Assert.equal(Services.search.defaultEngine.name, TEST_ENGINE_NAME, "currentEngine set");
         Services.obs.removeObserver(observer, "browser-search-engine-modified");
         resolve();
@@ -116,23 +116,23 @@ function promiseCurrentEngineChanged() {
     Services.obs.addObserver(observer, "browser-search-engine-modified");
   });
 }
 
 /**
  * Opens the specified urlbar/search popup and gets the test engine from the
  * one-off buttons.
  *
- * @param {Boolean} isSearch true if the search popup should be opened; false
+ * @param {boolean} isSearch true if the search popup should be opened; false
  *                           for the urlbar popup.
- * @param {Object} popup The expected popup.
- * @param {Object} oneOffInstance The expected one-off instance for the popup.
- * @param {String} baseId The expected string for the id of the current
+ * @param {object} popup The expected popup.
+ * @param {object} oneOffInstance The expected one-off instance for the popup.
+ * @param {string} baseId The expected string for the id of the current
  *                        engine button, without the engine name.
- * @return {Object} Returns an object that represents the one off button for the
+ * @returns {object} Returns an object that represents the one off button for the
  *                          test engine.
  */
 async function openPopupAndGetEngineButton(isSearch, popup, oneOffInstance, baseId) {
   // Open the popup.
   let promise = promiseEvent(popup, "popupshown");
   info("Opening panel");
 
   // We have to open the popups in differnt ways.
--- a/browser/components/search/test/browser/head.js
+++ b/browser/components/search/test/browser/head.js
@@ -2,16 +2,23 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 ChromeUtils.import("resource://testing-common/CustomizableUITestUtils.jsm", this);
 let gCUITestUtils = new CustomizableUITestUtils(window);
 
 /**
  * Recursively compare two objects and check that every property of expectedObj has the same value
  * on actualObj.
+ *
+ * @param {object} expectedObj
+ *        The expected object to find.
+ * @param {object} actualObj
+ *        The object to inspect.
+ * @param {string} name
+ *        The name of the engine, used for test detail logging.
  */
 function isSubObjectOf(expectedObj, actualObj, name) {
   for (let prop in expectedObj) {
     if (typeof expectedObj[prop] == "function")
       continue;
     if (expectedObj[prop] instanceof Object) {
       is(actualObj[prop].length, expectedObj[prop].length, name + "[" + prop + "]");
       isSubObjectOf(expectedObj[prop], actualObj[prop], name + "[" + prop + "]");
@@ -35,19 +42,19 @@ function promiseEvent(aTarget, aEventNam
   }
 
   return BrowserTestUtils.waitForEvent(aTarget, aEventName, false, cancelEvent);
 }
 
 /**
  * Adds a new search engine to the search service and confirms it completes.
  *
- * @param {String} basename  The file to load that contains the search engine
+ * @param {string} basename  The file to load that contains the search engine
  *                           details.
- * @param {Object} [options] Options for the test:
+ * @param {object} [options] Options for the test:
  *   - {String} [iconURL]       The icon to use for the search engine.
  *   - {Boolean} [setAsCurrent] Whether to set the new engine to be the
  *                              current engine or not.
  *   - {String} [testPath]      Used to override the current test path if this
  *                              file is used from a different directory.
  * @returns {Promise} The promise is resolved once the engine is added, or
  *                    rejected if the addition failed.
  */
@@ -146,21 +153,21 @@ function promiseStateChangeURI() {
   });
 }
 
 
 /**
  * Waits for a load (or custom) event to finish in a given tab. If provided
  * load an uri into the tab.
  *
- * @param tab
+ * @param {object} tab
  *        The tab to load into.
- * @param [optional] url
+ * @param {string} [url]
  *        The url to load, or the current url.
- * @return {Promise} resolved when the event is handled.
+ * @returns {Promise} resolved when the event is handled.
  * @resolves to the received event
  * @rejects if a valid load event is not received within a meaningful interval
  */
 function promiseTabLoadEvent(tab, url) {
   info("Wait tab event: load");
 
   function handle(loadedUrl) {
     if (loadedUrl === "about:blank" || (url && loadedUrl !== url)) {
--- a/browser/components/search/test/browser/webapi.html
+++ b/browser/components/search/test/browser/webapi.html
@@ -1,16 +1,16 @@
 <!DOCTYPE html>
 
 <html>
 <head>
 <script>
 function installEngine() {
-  var query = window.location.search.substring(1);
-  var args = JSON.parse(decodeURIComponent(query));
+  let query = window.location.search.substring(1);
+  let args = JSON.parse(decodeURIComponent(query));
 
   window.external.AddSearchProvider(...args);
 }
 </script>
 </head>
 <body onload="installEngine()">
 </body>
 </html>
--- a/browser/extensions/formautofill/content/autofillEditForms.js
+++ b/browser/extensions/formautofill/content/autofillEditForms.js
@@ -401,17 +401,17 @@ class EditCreditCard extends EditAutofil
 
     // Empty month option
     this._elements.month.appendChild(new Option());
 
     // Populate month list. Format: "month number - month name"
     let dateFormat = new Intl.DateTimeFormat(navigator.language, {month: "long"}).format;
     for (let i = 0; i < count; i++) {
       let monthNumber = (i + 1).toString();
-      let monthName = dateFormat(new Date(Date.UTC(1970, i, 1)));
+      let monthName = dateFormat(new Date(1970, i));
       let option = new Option();
       option.value = monthNumber;
       // XXX: Bug 1446164 - Localize this string.
       option.textContent = `${monthNumber.padStart(2, "0")} - ${monthName}`;
       this._elements.month.appendChild(option);
     }
   }
 
--- a/browser/extensions/formautofill/test/browser/browser_editCreditCardDialog.js
+++ b/browser/extensions/formautofill/test/browser/browser_editCreditCardDialog.js
@@ -20,16 +20,17 @@ add_task(async function test_cancelEditC
 });
 
 add_task(async function test_saveCreditCard() {
   await testDialog(EDIT_CREDIT_CARD_DIALOG_URL, (win) => {
     EventUtils.synthesizeKey("VK_TAB", {}, win);
     EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-number"], {}, win);
     EventUtils.synthesizeKey("VK_TAB", {}, win);
     EventUtils.synthesizeKey("0" + TEST_CREDIT_CARD_1["cc-exp-month"].toString(), {}, win);
+    is(win.document.activeElement.selectedOptions[0].text, "04 - April", "Displayed month should match number and name");
     EventUtils.synthesizeKey("VK_TAB", {}, win);
     EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-exp-year"].toString(), {}, win);
     EventUtils.synthesizeKey("VK_TAB", {}, win);
     EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-name"], {}, win);
     EventUtils.synthesizeKey("VK_TAB", {}, win);
     EventUtils.synthesizeKey(TEST_CREDIT_CARD_1["cc-type"], {}, win);
     EventUtils.synthesizeKey("VK_TAB", {}, win);
     EventUtils.synthesizeKey("VK_TAB", {}, win);
--- a/browser/extensions/screenshots/background/main.js
+++ b/browser/extensions/screenshots/background/main.js
@@ -142,16 +142,20 @@ this.main = (function() {
         .then(() => sendEvent("start-shot", "context-menu", {incognito: tab.incognito}));
     }));
   });
 
   function urlEnabled(url) {
     if (shouldOpenMyShots(url)) {
       return true;
     }
+    // Allow screenshots on urls related to web pages in reader mode.
+    if (url && url.startsWith("about:reader?url=")) {
+      return true;
+    }
     if (isShotOrMyShotPage(url) || /^(?:about|data|moz-extension):/i.test(url) || isBlacklistedUrl(url)) {
       return false;
     }
     return true;
   }
 
   function isShotOrMyShotPage(url) {
     // It's okay to take a shot of any pages except shot pages and My Shots
--- a/browser/extensions/screenshots/background/startBackground.js
+++ b/browser/extensions/screenshots/background/startBackground.js
@@ -36,17 +36,17 @@ this.startBackground = (function() {
       console.error("Error loading Screenshots:", error);
     });
   });
 
   browser.contextMenus.create({
     id: "create-screenshot",
     title: browser.i18n.getMessage("contextMenuLabel"),
     contexts: ["page"],
-    documentUrlPatterns: ["<all_urls>"],
+    documentUrlPatterns: ["<all_urls>", "about:reader*"],
   });
 
   browser.contextMenus.onClicked.addListener((info, tab) => {
     loadIfNecessary().then(() => {
       main.onClickedContextMenu(info, tab);
     }).catch((error) => {
       console.error("Error loading Screenshots:", error);
     });
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -199,41 +199,41 @@
 
       @media (-moz-windows-default-theme) {
         #main-menubar > menu[_moz-menuactive="true"] {
           color: inherit;
         }
 
         #main-menubar > menu[_moz-menuactive="true"],
         .titlebar-button:hover {
-          background-color: hsla(0,0%,100%,.22);
+          background-color: hsla(0,0%,0%,.12);
         }
         .titlebar-button:hover:active {
-          background-color: hsla(0,0%,100%,.32);
-        }
-        #main-menubar > menu[_moz-menuactive="true"]:-moz-lwtheme-darktext,
-        .titlebar-button:-moz-lwtheme-darktext:hover {
-          background-color: hsla(0,0%,0%,.12);
-        }
-        .titlebar-button:-moz-lwtheme-darktext:hover:active {
           background-color: hsla(0,0%,0%,.22);
         }
 
-        .titlebar-button:not(:hover) > .toolbarbutton-icon:-moz-window-inactive {
-          opacity: 0.5;
+        #toolbar-menubar[brighttext] > #main-menubar > menu[_moz-menuactive="true"],
+        toolbar[brighttext] .titlebar-button:not(.titlebar-close):hover {
+          background-color: hsla(0,0%,100%,.22);
+        }
+        toolbar[brighttext] .titlebar-button:not(.titlebar-close):hover:active {
+          background-color: hsla(0,0%,100%,.32);
         }
 
         .titlebar-close:hover {
           stroke: white;
           background-color: hsl(355,86%,49%);
         }
-
         .titlebar-close:hover:active {
           background-color: hsl(355,82%,69%);
         }
+
+        .titlebar-button:not(:hover) > .toolbarbutton-icon:-moz-window-inactive {
+          opacity: 0.5;
+        }
       }
 
       @media (-moz-windows-default-theme: 0) {
         .titlebar-button {
           background-color: -moz-field;
           stroke: ButtonText;
         }
         .titlebar-button:hover {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -345,40 +345,45 @@
 @media (-moz-os-version: windows-win7),
        (-moz-os-version: windows-win8) {
   /* Preserve window control buttons position at the top of the button box. */
   .titlebar-buttonbox-container {
     -moz-box-align: start;
   }
 }
 
-/* titlebar command buttons */
+/* window control buttons */
 
 .titlebar-min {
   -moz-appearance: -moz-window-button-minimize;
 }
 
 .titlebar-max {
   -moz-appearance: -moz-window-button-maximize;
 }
 
-#main-window[sizemode="maximized"] .titlebar-max {
+:root[sizemode="maximized"] .titlebar-max {
   -moz-appearance: -moz-window-button-restore;
 }
 
 .titlebar-close {
   -moz-appearance: -moz-window-button-close;
 }
 
 @media (-moz-windows-classic: 0) {
   .titlebar-min {
     margin-inline-end: 2px;
   }
 }
 
+:root[tabletmode] .titlebar-min,
+:root[tabletmode] .titlebar-max {
+  display: none;
+}
+
 /* ::::: bookmark menus ::::: */
 
 menu.bookmark-item,
 menuitem.bookmark-item {
   min-width: 0;
   max-width: 32em;
 }
 
new file mode 100644
--- /dev/null
+++ b/build/debian-packages/nasm-wheezy.diff
@@ -0,0 +1,48 @@
+diff -Nru nasm-2.13.01/debian/changelog nasm-2.13.01/debian/changelog
+--- nasm-2.13.01/debian/changelog	2017-05-08 16:33:23.000000000 +0000
++++ nasm-2.13.01/debian/changelog	2018-11-30 09:54:37.000000000 +0000
+@@ -1,3 +1,12 @@
++nasm (2.13.01-1.deb7moz1) wheezy; urgency=low
++
++  * Changes for compatibility with building on Debian Wheezy:
++  * Remove fonts-liberation2 dep which is not present on Wheezy
++  * Disable docs building and installation which is not possible
++    without fonts-liberation2
++
++ -- Thomas Daede <tdaede@mozilla.com>  Fri, 30 Nov 2018 09:54:37 +0000
++
+ nasm (2.13.01-1) experimental; urgency=low
+ 
+   * New upstream version 2.13.01
+--- nasm-2.13.01/debian/control	2017-05-08 16:33:23.000000000 +0000
++++ nasm-2.13.01/debian/control	2018-11-30 09:53:57.000000000 +0000
+@@ -8,7 +8,7 @@
+ Build-Depends:
+  debhelper (>= 9),
+  dpkg-dev (>= 1.16.1~),
+- fonts-liberation2,
++ fontconfig,
+  ghostscript,
+  libfont-ttf-perl,
+  libsort-versions-perl,
+--- nasm-2.13.01/debian/rules	2017-05-08 16:33:23.000000000 +0000
++++ nasm-2.13.01/debian/rules	2018-11-30 09:53:57.000000000 +0000
+@@ -28,7 +28,7 @@
+ build-stamp:
+ 	dh_testdir
+ 	./configure --prefix=/usr --mandir=/usr/share/man
+-	$(MAKE) all doc
++	$(MAKE) all
+ 	> $@
+ 
+ binary: binary-arch binary-indep
+@@ -38,8 +38,7 @@
+ 	dh_testroot
+ 	dh_clean
+ 	dh_installdirs usr/bin usr/share/man/man1
+-	$(MAKE) INSTALLROOT=$(CURDIR)/debian/nasm install install_rdf
+-	dh_installdocs AUTHORS README TODO doc/html doc/nasmdoc.txt doc/nasmdoc.ps doc/nasmdoc.pdf
++	$(MAKE) INSTALLROOT=$(CURDIR)/debian/nasm install
+ 	dh_installexamples misc/nasm.sl test/*
+ 	dh_installman debian/*.1
+ 	dh_installchangelogs CHANGES
--- a/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
+++ b/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
@@ -61,17 +61,18 @@ function inspectDebugTarget(type, id) {
           await debugRemoteAddon(id, devtoolsClient);
         } else if (runtimeType === RUNTIMES.THIS_FIREFOX) {
           debugLocalAddon(id);
         }
         break;
       }
       case DEBUG_TARGETS.WORKER: {
         // Open worker toolbox in new window.
-        const front = runtimeDetails.client.client.getActor(id);
+        const devtoolsClient = runtimeDetails.clientWrapper.client;
+        const front = devtoolsClient.getActor(id);
         gDevToolsBrowser.openWorkerToolbox(front);
         break;
       }
 
       default: {
         console.error("Failed to inspect the debug target of " +
                       `type: ${ type } id: ${ id }`);
       }
--- a/devtools/client/aboutdebugging-new/src/components/debugtarget/InspectAction.js
+++ b/devtools/client/aboutdebugging-new/src/components/debugtarget/InspectAction.js
@@ -33,17 +33,17 @@ class InspectAction extends PureComponen
   render() {
     return Localized(
       {
         id: "about-debugging-debug-target-inspect-button",
       },
       dom.button(
         {
           onClick: e => this.inspect(),
-          className: "default-button",
+          className: "default-button  js-debug-target-inspect-button",
         },
         "Inspect"
       )
     );
   }
 }
 
 module.exports = InspectAction;
--- a/devtools/client/aboutdebugging-new/test/browser/browser.ini
+++ b/devtools/client/aboutdebugging-new/test/browser/browser.ini
@@ -40,8 +40,9 @@ skip-if = (os == 'linux' && bits == 32) 
 [browser_aboutdebugging_sidebar_usb_status.js]
 skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug 1499638
 [browser_aboutdebugging_stop_adb.js]
 skip-if = (os == 'linux' && bits == 32) # ADB start() fails on linux 32, see Bug 1499638
 [browser_aboutdebugging_system_addons.js]
 [browser_aboutdebugging_tab_favicons.js]
 [browser_aboutdebugging_thisfirefox.js]
 [browser_aboutdebugging_thisfirefox_runtime_info.js]
+[browser_aboutdebugging_thisfirefox_worker_inspection.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox_worker_inspection.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* import-globals-from mocks/head-client-wrapper-mock.js */
+Services.scriptloader.loadSubScript(
+  CHROME_URL_ROOT + "mocks/head-client-wrapper-mock.js", this);
+/* import-globals-from mocks/head-runtime-client-factory-mock.js */
+Services.scriptloader.loadSubScript(
+  CHROME_URL_ROOT + "mocks/head-runtime-client-factory-mock.js", this);
+const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
+
+add_task(async function() {
+  const thisFirefoxClient = createThisFirefoxClientMock();
+  // Prepare a worker mock.
+  const testWorkerTargetFront = {
+    actorID: "test-worker-id",
+  };
+  const testWorker = {
+    name: "Test Worker",
+    workerTargetFront: testWorkerTargetFront,
+  };
+  // Add a worker mock as other worker.
+  thisFirefoxClient.listWorkers = () => ({
+    otherWorkers: [testWorker],
+    serviceWorkers: [],
+    sharedWorkers: [],
+  });
+  // Override getActor function of client which is used in inspect action for worker.
+  thisFirefoxClient.client.getActor = id => {
+    return id === testWorkerTargetFront.actorID ? testWorkerTargetFront : null;
+  };
+
+  const runtimeClientFactoryMock = createRuntimeClientFactoryMock();
+  runtimeClientFactoryMock.createClientForRuntime = runtime => {
+    const { RUNTIMES } = require("devtools/client/aboutdebugging-new/src/constants");
+    if (runtime.id === RUNTIMES.THIS_FIREFOX) {
+      return thisFirefoxClient;
+    }
+    throw new Error("Unexpected runtime id " + runtime.id);
+  };
+
+  info("Enable mocks");
+  enableRuntimeClientFactoryMock(runtimeClientFactoryMock);
+  const originalOpenWorkerForToolbox =  gDevToolsBrowser.openWorkerToolbox;
+  registerCleanupFunction(() => {
+    disableRuntimeClientFactoryMock();
+    gDevToolsBrowser.openWorkerToolbox = originalOpenWorkerForToolbox;
+  });
+
+  const { document, tab } = await openAboutDebugging();
+
+  const workerTarget = findDebugTargetByText(testWorker.name, document);
+  ok(workerTarget, "Worker target appeared for the test worker");
+  const inspectButton = workerTarget.querySelector(".js-debug-target-inspect-button");
+  ok(inspectButton, "Inspect button for the worker appeared");
+
+  info("Check whether the correct actor front will be opened in worker toolbox");
+  const waitForWorkerInspection = new Promise(resolve => {
+    // Override openWorkerToolbox of gDevToolsBrowser to check the parameter.
+    gDevToolsBrowser.openWorkerToolbox = front => {
+      if (front === testWorkerTargetFront) {
+        resolve();
+      }
+    };
+  });
+
+  inspectButton.click();
+
+  await waitForWorkerInspection;
+  ok(true, "Correct actor front will be opened in worker toolbox");
+
+  await removeTab(tab);
+});
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Frames/Group.js
@@ -74,17 +74,18 @@ export default class Group extends Compo
     FrameMenu(
       frame,
       frameworkGroupingOn,
       { copyStackTrace, toggleFrameworkGrouping, toggleBlackBox },
       event
     );
   }
 
-  toggleFrames = () => {
+  toggleFrames = (event) => {
+    event.stopPropagation();
     this.setState(prevState => ({ expanded: !prevState.expanded }));
   };
 
   renderFrames() {
     const {
       group,
       selectFrame,
       selectedFrame,
--- a/devtools/client/webconsole/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/test/mochitest/browser.ini
@@ -288,16 +288,17 @@ subsuite = clipboard
 [browser_webconsole_context_menu_open_url.js]
 [browser_webconsole_context_menu_store_as_global.js]
 [browser_webconsole_cors_errors.js]
 [browser_webconsole_csp_ignore_reflected_xss_message.js]
 [browser_webconsole_csp_violation.js]
 [browser_webconsole_cspro.js]
 [browser_webconsole_document_focus.js]
 [browser_webconsole_duplicate_errors.js]
+[browser_webconsole_error_with_grouped_stack.js]
 [browser_webconsole_error_with_longstring_stack.js]
 [browser_webconsole_error_with_unicode.js]
 [browser_webconsole_errors_after_page_reload.js]
 [browser_webconsole_eval_in_debugger_stackframe.js]
 [browser_webconsole_eval_in_debugger_stackframe2.js]
 [browser_webconsole_execution_scope.js]
 [browser_webconsole_external_script_errors.js]
 [browser_webconsole_file_uri.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_error_with_grouped_stack.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check if an error with a stack containing grouped frames works as expected.
+
+"use strict";
+
+const MESSAGE = "React Error";
+const TEST_URI = `data:text/html;charset=utf8,<script>
+  const x = new Error("${MESSAGE}");
+  x.stack = "a@http://exampl.com:1:1\\n" +
+    "grouped@http://react.js:1:1\\n" +
+    "grouped2@http://react.js:1:1";
+  console.error(x);
+</script>`;
+
+add_task(async function() {
+  const hud = await openNewTabAndConsole(TEST_URI);
+
+  info("Wait for the error to be logged");
+  const msgNode = await waitFor(() => findMessage(hud, MESSAGE));
+  ok(!msgNode.classList.contains("open"), `Error logged not expanded`);
+
+  const groupNode = await waitFor(() => msgNode.querySelector(".group"));
+  ok(groupNode, "The error object is logged as expected");
+
+  const onGroupExpanded = waitFor(() => msgNode.querySelector(".frames-group.expanded"));
+  groupNode.click();
+  await onGroupExpanded;
+
+  ok(true, "The stacktrace group was expanded");
+  is(msgNode.querySelectorAll(".frame").length, 3, "Expected frames are displayed");
+  ok(!msgNode.classList.contains("open"), `Error message is still not expanded`);
+});
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -369,19 +369,19 @@ void KeyframeEffect::EnsureBaseStyles(
   if (aBaseStylesChanged != nullptr) {
     *aBaseStylesChanged = false;
   }
 
   if (!mTarget) {
     return;
   }
 
-  Maybe<BaseValuesHashmap> previousBaseStyles;
+  BaseValuesHashmap previousBaseStyles;
   if (aBaseStylesChanged != nullptr) {
-    previousBaseStyles.emplace(std::move(mBaseValues));
+    previousBaseStyles = std::move(mBaseValues);
   }
 
   mBaseValues.Clear();
 
   nsPresContext* presContext =
       nsContentUtils::GetContextForContent(mTarget->mElement);
   // If |aProperties| is empty we're not going to dereference |presContext| so
   // we don't care if it is nullptr.
@@ -400,17 +400,17 @@ void KeyframeEffect::EnsureBaseStyles(
   RefPtr<ComputedStyle> baseComputedStyle;
   for (const AnimationProperty& property : aProperties) {
     EnsureBaseStyle(property, presContext, aComputedValues, baseComputedStyle);
   }
 
   if (aBaseStylesChanged != nullptr) {
     for (auto iter = mBaseValues.Iter(); !iter.Done(); iter.Next()) {
       if (AnimationValue(iter.Data()) !=
-          AnimationValue(previousBaseStyles->Get(iter.Key()))) {
+          AnimationValue(previousBaseStyles.Get(iter.Key()))) {
         *aBaseStylesChanged = true;
         break;
       }
     }
   }
 }
 
 void KeyframeEffect::EnsureBaseStyle(
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1551,25 +1551,27 @@ bool nsFocusManager::Blur(nsPIDOMWindowO
   nsCOMPtr<nsPIDOMWindowOuter> window = mFocusedWindow;
   if (!window) {
     mFocusedElement = nullptr;
     return true;
   }
 
   nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
   if (!docShell) {
+    mFocusedWindow = nullptr;
     mFocusedElement = nullptr;
     return true;
   }
 
   // Keep a ref to presShell since dispatching the DOM event may cause
   // the document to be destroyed.
   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
   if (!presShell) {
     mFocusedElement = nullptr;
+    mFocusedWindow = nullptr;
     return true;
   }
 
   bool clearFirstBlurEvent = false;
   if (!mFirstBlurEvent) {
     mFirstBlurEvent = element;
     clearFirstBlurEvent = true;
   }
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -29,16 +29,17 @@
 #include "DecoderDoctorDiagnostics.h"
 #include "WebMDecoder.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/dom/MediaSource.h"
 #include "DecoderTraits.h"
 #ifdef MOZ_WIDGET_ANDROID
+#include "AndroidDecoderModule.h"
 #include "FennecJNIWrappers.h"
 #include "GeneratedJNIWrappers.h"
 #endif
 #include <functional>
 
 namespace mozilla {
 namespace dom {
 
@@ -304,19 +305,19 @@ static nsTArray<KeySystemConfig> GetSupp
       // decode AAC, and a codec wasn't specified, be conservative
       // and reject the MediaKeys request, since we assume Widevine
       // will be used with AAC.
       if (WMFDecoderModule::HasAAC()) {
         widevine.mMP4.SetCanDecrypt(EME_CODEC_AAC);
       }
 #elif !defined(MOZ_WIDGET_ANDROID)
       widevine.mMP4.SetCanDecrypt(EME_CODEC_AAC);
-#endif
       widevine.mMP4.SetCanDecrypt(EME_CODEC_FLAC);
       widevine.mMP4.SetCanDecrypt(EME_CODEC_OPUS);
+#endif
 
 #if defined(MOZ_WIDGET_ANDROID)
       using namespace mozilla::java;
       // MediaDrm.isCryptoSchemeSupported only allows passing
       // "video/mp4" or "video/webm" for mimetype string.
       // See
       // https://developer.android.com/reference/android/media/MediaDrm.html#isCryptoSchemeSupported(java.util.UUID,
       // java.lang.String) for more detail.
@@ -346,17 +347,17 @@ static nsTArray<KeySystemConfig> GetSupp
            &widevine.mWebM},
           {nsCString(AUDIO_WEBM), EME_CODEC_OPUS, MediaDrmProxy::OPUS,
            &widevine.mWebM},
       };
 
       for (const auto& data : validationList) {
         if (MediaDrmProxy::IsCryptoSchemeSupported(kEMEKeySystemWidevine,
                                                    data.mMimeType)) {
-          if (MediaDrmProxy::CanDecode(data.mCodecType)) {
+          if (AndroidDecoderModule::SupportsMimeType(data.mMimeType)) {
             data.mSupportType->SetCanDecryptAndDecode(data.mEMECodecType);
           } else {
             data.mSupportType->SetCanDecrypt(data.mEMECodecType);
           }
         }
       }
 #else
       widevine.mMP4.SetCanDecryptAndDecode(EME_CODEC_H264);
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -471,23 +471,29 @@ already_AddRefed<MediaKeySession> MediaK
 
   // Add session to the set of sessions awaiting their sessionId being ready.
   mPendingSessions.Put(session->Token(), session);
 
   return session.forget();
 }
 
 void MediaKeys::OnSessionLoaded(PromiseId aId, bool aSuccess) {
+  EME_LOG("MediaKeys[%p]::OnSessionLoaded() resolve promise id=%d", this, aId);
+
+  ResolvePromiseWithResult(aId, aSuccess);
+}
+
+template <typename T>
+void MediaKeys::ResolvePromiseWithResult(PromiseId aId, const T& aResult) {
   RefPtr<DetailedPromise> promise(RetrievePromise(aId));
   if (!promise) {
     return;
   }
-  EME_LOG("MediaKeys[%p]::OnSessionLoaded() resolve promise id=%d", this, aId);
 
-  promise->MaybeResolve(aSuccess);
+  promise->MaybeResolve(aResult);
 }
 
 void MediaKeys::OnSessionClosed(MediaKeySession* aSession) {
   nsAutoString id;
   aSession->GetSessionId(id);
   mKeySessions.Remove(id);
 }
 
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -139,16 +139,19 @@ class MediaKeys final : public nsISuppor
 
   // JavaScript: MediaKeys.GetStatusForPolicy()
   already_AddRefed<Promise> GetStatusForPolicy(const MediaKeysPolicy& aPolicy,
                                                ErrorResult& aR);
   // Called by CDMProxy when CDM successfully GetStatusForPolicy.
   void ResolvePromiseWithKeyStatus(PromiseId aId,
                                    dom::MediaKeyStatus aMediaKeyStatus);
 
+  template <typename T>
+  void ResolvePromiseWithResult(PromiseId aId, const T& aResult);
+
  private:
   // Instantiate CDMProxy instance.
   // It could be MediaDrmCDMProxy (Widevine on Fennec) or ChromiumCDMProxy (the
   // rest).
   already_AddRefed<CDMProxy> CreateCDMProxy(nsIEventTarget* aMainThread);
 
   // Removes promise from mPromises, and returns it.
   already_AddRefed<DetailedPromise> RetrievePromise(PromiseId aId);
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.cpp
@@ -96,20 +96,24 @@ void MediaDrmCDMProxy::LoadSession(Promi
   // TODO: Implement LoadSession.
   RejectPromise(
       aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
       NS_LITERAL_CSTRING("Currently Fennec does not support LoadSession"));
 }
 
 void MediaDrmCDMProxy::SetServerCertificate(PromiseId aPromiseId,
                                             nsTArray<uint8_t>& aCert) {
-  // TODO: Implement SetServerCertificate.
-  RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
-                NS_LITERAL_CSTRING(
-                    "Currently Fennec does not support SetServerCertificate"));
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mOwnerThread);
+
+  mOwnerThread->Dispatch(NewRunnableMethod<PromiseId, const nsTArray<uint8_t>>(
+                             "MediaDrmCDMProxy::md_SetServerCertificate", this,
+                             &MediaDrmCDMProxy::md_SetServerCertificate,
+                             aPromiseId, std::move(aCert)),
+                         NS_DISPATCH_NORMAL);
 }
 
 void MediaDrmCDMProxy::UpdateSession(const nsAString& aSessionId,
                                      PromiseId aPromiseId,
                                      nsTArray<uint8_t>& aResponse) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mOwnerThread);
   NS_ENSURE_TRUE_VOID(!mKeys.IsNull());
@@ -280,16 +284,35 @@ void MediaDrmCDMProxy::ResolvePromise(Pr
     nsCOMPtr<nsIRunnable> task;
     task =
         NewRunnableMethod<PromiseId>("MediaDrmCDMProxy::ResolvePromise", this,
                                      &MediaDrmCDMProxy::ResolvePromise, aId);
     mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
   }
 }
 
+template <typename T>
+void MediaDrmCDMProxy::ResolvePromiseWithResult(PromiseId aId,
+                                                const T& aResult) {
+  if (NS_IsMainThread()) {
+    if (!mKeys.IsNull()) {
+      mKeys->ResolvePromiseWithResult(aId, aResult);
+    } else {
+      NS_WARNING("MediaDrmCDMProxy unable to resolve promise!");
+    }
+    return;
+  }
+
+  nsCOMPtr<nsIRunnable> task;
+  task = NewRunnableMethod<PromiseId, T>(
+      "MediaDrmCDMProxy::ResolvePromiseWithResult", this,
+      &MediaDrmCDMProxy::ResolvePromiseWithResult<T>, aId, aResult);
+  mMainThread->Dispatch(task.forget(), NS_DISPATCH_NORMAL);
+}
+
 const nsString& MediaDrmCDMProxy::KeySystem() const { return mKeySystem; }
 
 DataMutex<CDMCaps>& MediaDrmCDMProxy::Capabilites() { return mCapabilites; }
 
 void MediaDrmCDMProxy::OnKeyStatusesChange(const nsAString& aSessionId) {
   MOZ_ASSERT(NS_IsMainThread());
   if (mKeys.IsNull()) {
     return;
@@ -356,16 +379,35 @@ void MediaDrmCDMProxy::md_CreateSession(
     return;
   }
 
   mCDM->CreateSession(aData->mCreateSessionToken, aData->mPromiseId,
                       aData->mInitDataType, aData->mInitData,
                       ToMediaDrmSessionType(aData->mSessionType));
 }
 
+void MediaDrmCDMProxy::md_SetServerCertificate(PromiseId aPromiseId,
+                                               const nsTArray<uint8_t>& aCert) {
+  MOZ_ASSERT(IsOnOwnerThread());
+
+  if (!mCDM) {
+    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING("Null CDM in md_SetServerCertificate"));
+    return;
+  }
+
+  if (mCDM->SetServerCertificate(aCert)) {
+    ResolvePromiseWithResult(aPromiseId, true);
+  } else {
+    RejectPromise(aPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
+                  NS_LITERAL_CSTRING(
+                      "MediaDrmCDMProxy unable to set server certificate"));
+  }
+}
+
 void MediaDrmCDMProxy::md_UpdateSession(UniquePtr<UpdateSessionData>&& aData) {
   MOZ_ASSERT(IsOnOwnerThread());
 
   if (!mCDM) {
     RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR,
                   NS_LITERAL_CSTRING("Null CDM in md_UpdateSession"));
     return;
   }
--- a/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
+++ b/dom/media/eme/mediadrm/MediaDrmCDMProxy.h
@@ -109,16 +109,19 @@ class MediaDrmCDMProxy : public CDMProxy
 
   const nsString& GetMediaDrmStubId() const;
 
  private:
   virtual ~MediaDrmCDMProxy();
 
   void OnCDMCreated(uint32_t aPromiseId);
 
+  template <typename T>
+  void ResolvePromiseWithResult(PromiseId aId, const T& aResult);
+
   struct CreateSessionData {
     MediaKeySessionType mSessionType;
     uint32_t mCreateSessionToken;
     PromiseId mPromiseId;
     nsCString mInitDataType;
     nsTArray<uint8_t> mInitData;
   };
 
@@ -158,16 +161,18 @@ class MediaDrmCDMProxy : public CDMProxy
   UniquePtr<MediaDrmProxySupport> mCDM;
   UniquePtr<MediaDrmCDMCallbackProxy> mCallback;
   bool mShutdownCalled;
 
   // =====================================================================
   // For MediaDrmProxySupport
   void md_Init(uint32_t aPromiseId);
   void md_CreateSession(UniquePtr<CreateSessionData>&& aData);
+  void md_SetServerCertificate(PromiseId aPromiseId,
+                               const nsTArray<uint8_t>& aCert);
   void md_UpdateSession(UniquePtr<UpdateSessionData>&& aData);
   void md_CloseSession(UniquePtr<SessionOpData>&& aData);
   void md_Shutdown();
   // =====================================================================
 };
 
 }  // namespace mozilla
 
--- a/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp
+++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.cpp
@@ -256,9 +256,16 @@ void MediaDrmProxySupport::Shutdown() {
 
   if (mDestroyed) {
     return;
   }
   mBridgeProxy->Destroy();
   mDestroyed = true;
 }
 
+bool MediaDrmProxySupport::SetServerCertificate(
+    const nsTArray<uint8_t>& aCert) {
+  jni::ByteArray::LocalRef cert = jni::ByteArray::New(
+      reinterpret_cast<const int8_t*>(aCert.Elements()), aCert.Length());
+  return mBridgeProxy->SetServerCertificate(cert);
+}
+
 }  // namespace mozilla
--- a/dom/media/eme/mediadrm/MediaDrmProxySupport.h
+++ b/dom/media/eme/mediadrm/MediaDrmProxySupport.h
@@ -46,16 +46,18 @@ class MediaDrmProxySupport final {
                      const nsTArray<uint8_t>& aResponse);
 
   void CloseSession(uint32_t aPromiseId, const nsCString& aSessionId);
 
   void Shutdown();
 
   const nsString& GetMediaDrmStubId() const { return mMediaDrmStubId; }
 
+  bool SetServerCertificate(const nsTArray<uint8_t>& aCert);
+
  private:
   const nsString mKeySystem;
   java::MediaDrmProxy::GlobalRef mBridgeProxy;
   java::MediaDrmProxy::NativeMediaDrmProxyCallbacks::GlobalRef mJavaCallbacks;
   DecryptorProxyCallback* mCallback;
   bool mDestroyed;
   nsString mMediaDrmStubId;
 };
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -35,12 +35,13 @@ UNIFIED_SOURCES += [
     'MediaKeySession.cpp',
     'MediaKeyStatusMap.cpp',
     'MediaKeySystemAccess.cpp',
     'MediaKeySystemAccessManager.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
   DIRS += ['mediadrm']
+  LOCAL_INCLUDES += ['/dom/media/platforms/android']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -16,16 +16,19 @@
 
 #include <jni.h>
 
 #undef LOG
 #define LOG(arg, ...)                                     \
   MOZ_LOG(                                                \
       sAndroidDecoderModuleLog, mozilla::LogLevel::Debug, \
       ("AndroidDecoderModule(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
+#define SLOG(arg, ...)                                        \
+  MOZ_LOG(sAndroidDecoderModuleLog, mozilla::LogLevel::Debug, \
+          ("%s: " arg, __func__, ##__VA_ARGS__))
 
 using namespace mozilla;
 using namespace mozilla::gl;
 using namespace mozilla::java::sdk;
 using media::TimeUnit;
 
 namespace mozilla {
 
@@ -52,18 +55,17 @@ static bool GetFeatureStatus(int32_t aFe
   }
   return status == nsIGfxInfo::FEATURE_STATUS_OK;
 };
 
 AndroidDecoderModule::AndroidDecoderModule(CDMProxy* aProxy) {
   mProxy = static_cast<MediaDrmCDMProxy*>(aProxy);
 }
 
-bool AndroidDecoderModule::SupportsMimeType(
-    const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
+bool AndroidDecoderModule::SupportsMimeType(const nsACString& aMimeType) {
   if (jni::GetAPIVersion() < 16) {
     return false;
   }
 
   if (aMimeType.EqualsLiteral("video/mp4") ||
       aMimeType.EqualsLiteral("video/avc")) {
     return true;
   }
@@ -87,31 +89,36 @@ bool AndroidDecoderModule::SupportsMimeT
   }
 
   // Prefer the gecko decoder for opus and vorbis; stagefright crashes
   // on content demuxed from mp4.
   // Not all android devices support FLAC even when they say they do.
   if (OpusDataDecoder::IsOpus(aMimeType) ||
       VorbisDataDecoder::IsVorbis(aMimeType) ||
       aMimeType.EqualsLiteral("audio/flac")) {
-    LOG("Rejecting audio of type %s", aMimeType.Data());
+    SLOG("Rejecting audio of type %s", aMimeType.Data());
     return false;
   }
 
   // Prefer the gecko decoder for Theora.
   // Not all android devices support Theora even when they say they do.
   if (TheoraDecoder::IsTheora(aMimeType)) {
-    LOG("Rejecting video of type %s", aMimeType.Data());
+    SLOG("Rejecting video of type %s", aMimeType.Data());
     return false;
   }
 
   return java::HardwareCodecCapabilityUtils::FindDecoderCodecInfoForMimeType(
       TranslateMimeType(aMimeType));
 }
 
+bool AndroidDecoderModule::SupportsMimeType(
+    const nsACString& aMimeType, DecoderDoctorDiagnostics* aDiagnostics) const {
+  return AndroidDecoderModule::SupportsMimeType(aMimeType);
+}
+
 already_AddRefed<MediaDataDecoder> AndroidDecoderModule::CreateVideoDecoder(
     const CreateDecoderParams& aParams) {
   // Temporary - forces use of VPXDecoder when alpha is present.
   // Bug 1263836 will handle alpha scenario once implemented. It will shift
   // the check for alpha to PDMFactory but not itself remove the need for a
   // check.
   if (aParams.VideoConfig().HasAlpha()) {
     return nullptr;
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -18,16 +18,18 @@ class AndroidDecoderModule : public Plat
   already_AddRefed<MediaDataDecoder> CreateAudioDecoder(
       const CreateDecoderParams& aParams) override;
 
   explicit AndroidDecoderModule(CDMProxy* aProxy = nullptr);
 
   bool SupportsMimeType(const nsACString& aMimeType,
                         DecoderDoctorDiagnostics* aDiagnostics) const override;
 
+  static bool SupportsMimeType(const nsACString& aMimeType);
+
  private:
   virtual ~AndroidDecoderModule() {}
   RefPtr<MediaDrmCDMProxy> mProxy;
 };
 
 extern LazyLogModule sAndroidDecoderModuleLog;
 
 const nsCString TranslateMimeType(const nsACString& aMimeType);
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-694726ea7eee67ea3d8c6586aba28e9867e7f8ea
+74b9312fc0305ecaf06322b0f2f466da96e0a64c
--- a/gfx/wr/webrender/res/cs_blur.glsl
+++ b/gfx/wr/webrender/res/cs_blur.glsl
@@ -94,19 +94,16 @@ void main(void) {
 #define SAMPLE_TYPE float
 #define SAMPLE_TEXTURE(uv)  texture(sPrevPassAlpha, uv).r
 #endif
 
 // TODO(gw): Write a fast path blur that handles smaller blur radii
 //           with a offset / weight uniform table and a constant
 //           loop iteration count!
 
-// TODO(gw): Make use of the bilinear sampling trick to reduce
-//           the number of texture fetches needed for a gaussian blur.
-
 void main(void) {
     SAMPLE_TYPE original_color = SAMPLE_TEXTURE(vUv);
 
     // TODO(gw): The gauss function gets NaNs when blur radius
     //           is zero. In the future, detect this earlier
     //           and skip the blur passes completely.
     if (vSupport == 0) {
         oFragColor = vec4(original_color);
@@ -114,29 +111,52 @@ void main(void) {
     }
 
     // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
     vec3 gauss_coefficient;
     gauss_coefficient.x = 1.0 / (sqrt(2.0 * 3.14159265) * vSigma);
     gauss_coefficient.y = exp(-0.5 / (vSigma * vSigma));
     gauss_coefficient.z = gauss_coefficient.y * gauss_coefficient.y;
 
-    float gauss_coefficient_sum = 0.0;
+    float gauss_coefficient_total = gauss_coefficient.x;
     SAMPLE_TYPE avg_color = original_color * gauss_coefficient.x;
-    gauss_coefficient_sum += gauss_coefficient.x;
     gauss_coefficient.xy *= gauss_coefficient.yz;
 
-    for (int i = 1; i <= vSupport; i++) {
-        vec2 offset = vOffsetScale * float(i);
+    // Evaluate two adjacent texels at a time. We can do this because, if c0
+    // and c1 are colors of adjacent texels and k0 and k1 are arbitrary
+    // factors, this formula:
+    //
+    //     k0 * c0 + k1 * c1          (Equation 1)
+    //
+    // is equivalent to:
+    //
+    //                                 k1
+    //     (k0 + k1) * lerp(c0, c1, -------)
+    //                              k0 + k1
+    //
+    // A texture lookup of adjacent texels evaluates this formula:
+    //
+    //     lerp(c0, c1, t)
+    //
+    // for some t. So we can let `t = k1/(k0 + k1)` and effectively evaluate
+    // Equation 1 with a single texture lookup.
+
+    for (int i = 1; i <= vSupport; i += 2) {
+        float gauss_coefficient_subtotal = gauss_coefficient.x;
+        gauss_coefficient.xy *= gauss_coefficient.yz;
+        gauss_coefficient_subtotal += gauss_coefficient.x;
+        float gauss_ratio = gauss_coefficient.x / gauss_coefficient_subtotal;
+
+        vec2 offset = vOffsetScale * (float(i) + gauss_ratio);
 
         vec2 st0 = clamp(vUv.xy - offset, vUvRect.xy, vUvRect.zw);
-        avg_color += SAMPLE_TEXTURE(vec3(st0, vUv.z)) * gauss_coefficient.x;
+        avg_color += SAMPLE_TEXTURE(vec3(st0, vUv.z)) * gauss_coefficient_subtotal;
 
         vec2 st1 = clamp(vUv.xy + offset, vUvRect.xy, vUvRect.zw);
-        avg_color += SAMPLE_TEXTURE(vec3(st1, vUv.z)) * gauss_coefficient.x;
+        avg_color += SAMPLE_TEXTURE(vec3(st1, vUv.z)) * gauss_coefficient_subtotal;
 
-        gauss_coefficient_sum += 2.0 * gauss_coefficient.x;
+        gauss_coefficient_total += 2.0 * gauss_coefficient_subtotal;
         gauss_coefficient.xy *= gauss_coefficient.yz;
     }
 
-    oFragColor = vec4(avg_color) / gauss_coefficient_sum;
+    oFragColor = vec4(avg_color) / gauss_coefficient_total;
 }
 #endif
--- a/gfx/wr/webrender/src/batch.rs
+++ b/gfx/wr/webrender/src/batch.rs
@@ -1785,23 +1785,17 @@ impl AlphaBatchBuilder {
                                 uv_rect_address,
                                 z_id,
                             );
                         }
                     }
                 }
             }
             PrimitiveInstanceKind::LinearGradient { data_handle, ref visible_tiles_range, .. } => {
-                let prim_data = &ctx.resources.prim_data_store[data_handle];
-                let (ref stops_handle, brush_segments) = match prim_data.kind {
-                    PrimitiveTemplateKind::LinearGradient { stops_handle, ref brush_segments, .. } => {
-                        (stops_handle, brush_segments)
-                    }
-                    _ => unreachable!()
-                };
+                let prim_data = &ctx.resources.linear_grad_data_store[data_handle];
                 let specified_blend_mode = BlendMode::PremultipliedAlpha;
 
                 let mut prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_instance.combined_local_clip_rect,
                     task_address,
                     specific_prim_address: GpuCacheAddress::invalid(),
                     clip_task_address,
@@ -1817,35 +1811,35 @@ impl AlphaBatchBuilder {
                     } else {
                         BlendMode::None
                     };
 
                     let batch_params = BrushBatchParameters::shared(
                         BrushBatchKind::LinearGradient,
                         BatchTextures::no_texture(),
                         [
-                            stops_handle.as_int(gpu_cache),
+                            prim_data.stops_handle.as_int(gpu_cache),
                             0,
                             0,
                         ],
                         0,
                     );
 
                     prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
 
                     let prim_header_index = prim_headers.push(
                         &prim_header,
                         z_id,
                         batch_params.prim_user_data,
                     );
 
-                    let segments = if brush_segments.is_empty() {
+                    let segments = if prim_data.brush_segments.is_empty() {
                         None
                     } else {
-                        Some(brush_segments.as_slice())
+                        Some(prim_data.brush_segments.as_slice())
                     };
 
                     self.add_segmented_prim_to_batch(
                         segments,
                         prim_data.opacity,
                         &batch_params,
                         specified_blend_mode,
                         non_segmented_blend_mode,
@@ -1858,37 +1852,31 @@ impl AlphaBatchBuilder {
                         prim_instance.clip_task_index,
                         ctx,
                     );
                 } else {
                     let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
 
                     add_gradient_tiles(
                         visible_tiles,
-                        stops_handle,
+                        &prim_data.stops_handle,
                         BrushBatchKind::LinearGradient,
                         specified_blend_mode,
                         bounding_rect,
                         clip_task_address,
                         gpu_cache,
                         &mut self.batch_list,
                         &prim_header,
                         prim_headers,
                         z_id,
                     );
                 }
             }
             PrimitiveInstanceKind::RadialGradient { data_handle, ref visible_tiles_range, .. } => {
-                let prim_data = &ctx.resources.prim_data_store[data_handle];
-                let (stops_handle, brush_segments) = match prim_data.kind {
-                    PrimitiveTemplateKind::RadialGradient { ref stops_handle, ref brush_segments, .. } => {
-                        (stops_handle, brush_segments)
-                    }
-                    _ => unreachable!()
-                };
+                let prim_data = &ctx.resources.radial_grad_data_store[data_handle];
                 let specified_blend_mode = BlendMode::PremultipliedAlpha;
 
                 let mut prim_header = PrimitiveHeader {
                     local_rect: prim_rect,
                     local_clip_rect: prim_instance.combined_local_clip_rect,
                     task_address,
                     specific_prim_address: GpuCacheAddress::invalid(),
                     clip_task_address,
@@ -1904,35 +1892,35 @@ impl AlphaBatchBuilder {
                     } else {
                         BlendMode::None
                     };
 
                     let batch_params = BrushBatchParameters::shared(
                         BrushBatchKind::RadialGradient,
                         BatchTextures::no_texture(),
                         [
-                            stops_handle.as_int(gpu_cache),
+                            prim_data.stops_handle.as_int(gpu_cache),
                             0,
                             0,
                         ],
                         0,
                     );
 
                     prim_header.specific_prim_address = gpu_cache.get_address(&prim_data.gpu_cache_handle);
 
                     let prim_header_index = prim_headers.push(
                         &prim_header,
                         z_id,
                         batch_params.prim_user_data,
                     );
 
-                    let segments = if brush_segments.is_empty() {
+                    let segments = if prim_data.brush_segments.is_empty() {
                         None
                     } else {
-                        Some(brush_segments.as_slice())
+                        Some(prim_data.brush_segments.as_slice())
                     };
 
                     self.add_segmented_prim_to_batch(
                         segments,
                         prim_data.opacity,
                         &batch_params,
                         specified_blend_mode,
                         non_segmented_blend_mode,
@@ -1945,17 +1933,17 @@ impl AlphaBatchBuilder {
                         prim_instance.clip_task_index,
                         ctx,
                     );
                 } else {
                     let visible_tiles = &ctx.scratch.gradient_tiles[*visible_tiles_range];
 
                     add_gradient_tiles(
                         visible_tiles,
-                        stops_handle,
+                        &prim_data.stops_handle,
                         BrushBatchKind::RadialGradient,
                         specified_blend_mode,
                         bounding_rect,
                         clip_task_address,
                         gpu_cache,
                         &mut self.batch_list,
                         &prim_header,
                         prim_headers,
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -17,20 +17,21 @@ use clip::{ClipChainId, ClipRegion, Clip
 use clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
 use frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
 use glyph_rasterizer::FontInstance;
 use hit_test::{HitTestingItem, HitTestingRun};
 use image::simplify_repeated_primitive;
 use intern::{Handle, Internable};
 use internal_types::{FastHashMap, FastHashSet};
 use picture::{Picture3DContext, PictureCompositeMode, PicturePrimitive, PrimitiveList};
-use prim_store::{PrimitiveInstance, PrimitiveKeyKind, RadialGradientParams};
-use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, GradientStopKey, NinePatchDescriptor};
+use prim_store::{PrimitiveInstance, PrimitiveKeyKind};
+use prim_store::{PrimitiveKey, PrimitiveSceneData, PrimitiveInstanceKind, NinePatchDescriptor};
 use prim_store::{PrimitiveDataHandle, PrimitiveStore, PrimitiveStoreStats, LineDecorationCacheKey};
 use prim_store::{ScrollNodeAndClipChain, PictureIndex, register_prim_chase_id, get_line_decoration_sizes};
+use prim_store::gradient::{GradientStopKey, LinearGradient, RadialGradient, RadialGradientParams};
 use prim_store::text_run::TextRun;
 use render_backend::{DocumentView};
 use resource_cache::{FontInstanceMap, ImageRequest};
 use scene::{Scene, ScenePipeline, StackingContextHelpers};
 use scene_builder::{DocumentResources, InternerMut};
 use spatial_node::{StickyFrameInfo, ScrollFrameKind, SpatialNodeType};
 use std::{f32, mem, usize};
 use std::collections::vec_deque::VecDeque;
@@ -776,17 +777,17 @@ impl<'a> DisplayListFlattener<'a> {
                     info.gradient.end_point,
                     item.gradient_stops(),
                     info.gradient.extend_mode,
                     info.tile_size,
                     info.tile_spacing,
                     pipeline_id,
                     None,
                 ) {
-                    self.add_primitive(
+                    self.add_nonshadowable_primitive(
                         clip_and_scroll,
                         &prim_info,
                         Vec::new(),
                         prim_key_kind,
                     );
                 }
             }
             SpecificDisplayItem::RadialGradient(ref info) => {
@@ -798,17 +799,17 @@ impl<'a> DisplayListFlattener<'a> {
                     info.gradient.radius.width / info.gradient.radius.height,
                     item.gradient_stops(),
                     info.gradient.extend_mode,
                     info.tile_size,
                     info.tile_spacing,
                     pipeline_id,
                     None,
                 );
-                self.add_primitive(
+                self.add_nonshadowable_primitive(
                     clip_and_scroll,
                     &prim_info,
                     Vec::new(),
                     prim_key_kind,
                 );
             }
             SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
                 let bounds = box_shadow_info
@@ -1108,45 +1109,60 @@ impl<'a> DisplayListFlattener<'a> {
             println!("\tadded to stacking context at {}", self.sc_stack.len());
         }
         let stacking_context = self.sc_stack.last_mut().unwrap();
         stacking_context.primitives.push(prim_instance);
     }
 
     /// Convenience interface that creates a primitive entry and adds it
     /// to the draw list.
+    fn add_nonshadowable_primitive<P>(
+        &mut self,
+        clip_and_scroll: ScrollNodeAndClipChain,
+        info: &LayoutPrimitiveInfo,
+        clip_items: Vec<(LayoutPoint, ClipItemKey)>,
+        prim: P,
+    )
+    where
+        P: Internable<InternData = PrimitiveSceneData> + IsVisible,
+        P::Source: AsInstanceKind<Handle<P::Marker>>,
+        DocumentResources: InternerMut<P>,
+    {
+        if prim.is_visible() {
+            let clip_chain_id = self.build_clip_chain(
+                clip_items,
+                clip_and_scroll.spatial_node_index,
+                clip_and_scroll.clip_chain_id,
+            );
+            self.add_prim_to_draw_list(
+                info,
+                clip_chain_id,
+                clip_and_scroll,
+                prim
+            );
+        }
+    }
+
     pub fn add_primitive<P>(
         &mut self,
         clip_and_scroll: ScrollNodeAndClipChain,
         info: &LayoutPrimitiveInfo,
         clip_items: Vec<(LayoutPoint, ClipItemKey)>,
         prim: P,
     )
     where
         P: Internable<InternData = PrimitiveSceneData> + IsVisible,
         P::Source: AsInstanceKind<Handle<P::Marker>>,
         DocumentResources: InternerMut<P>,
         ShadowItem: From<PendingPrimitive<P>>
     {
         // If a shadow context is not active, then add the primitive
         // directly to the parent picture.
         if self.pending_shadow_items.is_empty() {
-            if prim.is_visible() {
-                let clip_chain_id = self.build_clip_chain(
-                    clip_items,
-                    clip_and_scroll.spatial_node_index,
-                    clip_and_scroll.clip_chain_id,
-                );
-                self.add_prim_to_draw_list(
-                    info,
-                    clip_chain_id,
-                    clip_and_scroll,
-                    prim
-                );
-            }
+            self.add_nonshadowable_primitive(clip_and_scroll, info, clip_items, prim);
         } else {
             debug_assert!(clip_items.is_empty(), "No per-prim clips expected for shadowed primitives");
 
             // There is an active shadow context. Store as a pending primitive
             // for processing during pop_all_shadows.
             self.pending_shadow_items.push_back(PendingPrimitive {
                 clip_and_scroll,
                 info: *info,
@@ -2070,66 +2086,80 @@ impl<'a> DisplayListFlattener<'a> {
                     slice: border.slice,
                     fill: border.fill,
                     repeat_horizontal: border.repeat_horizontal,
                     repeat_vertical: border.repeat_vertical,
                     outset: border.outset.into(),
                     widths: border_item.widths.into(),
                 };
 
-                let prim = match border.source {
+                match border.source {
                     NinePatchBorderSource::Image(image_key) => {
-                        PrimitiveKeyKind::ImageBorder {
+                        let prim = PrimitiveKeyKind::ImageBorder {
                             request: ImageRequest {
                                 key: image_key,
                                 rendering: ImageRendering::Auto,
                                 tile: None,
                             },
                             nine_patch,
-                        }
+                        };
+
+                        self.add_primitive(
+                            clip_and_scroll,
+                            info,
+                            Vec::new(),
+                            prim,
+                        );
                     }
                     NinePatchBorderSource::Gradient(gradient) => {
-                        match self.create_linear_gradient_prim(
+                        let prim = match self.create_linear_gradient_prim(
                             &info,
                             gradient.start_point,
                             gradient.end_point,
                             gradient_stops,
                             gradient.extend_mode,
                             LayoutSize::new(border.height as f32, border.width as f32),
                             LayoutSize::zero(),
                             pipeline_id,
                             Some(Box::new(nine_patch)),
                         ) {
                             Some(prim) => prim,
                             None => return,
-                        }
+                        };
+
+                        self.add_nonshadowable_primitive(
+                            clip_and_scroll,
+                            info,
+                            Vec::new(),
+                            prim,
+                        );
                     }
                     NinePatchBorderSource::RadialGradient(gradient) => {
-                        self.create_radial_gradient_prim(
+                        let prim = self.create_radial_gradient_prim(
                             &info,
                             gradient.center,
                             gradient.start_offset * gradient.radius.width,
                             gradient.end_offset * gradient.radius.width,
                             gradient.radius.width / gradient.radius.height,
                             gradient_stops,
                             gradient.extend_mode,
                             LayoutSize::new(border.height as f32, border.width as f32),
                             LayoutSize::zero(),
                             pipeline_id,
                             Some(Box::new(nine_patch)),
-                        )
+                        );
+
+                        self.add_nonshadowable_primitive(
+                            clip_and_scroll,
+                            info,
+                            Vec::new(),
+                            prim,
+                        );
                     }
                 };
-
-                self.add_primitive(
-                    clip_and_scroll,
-                    info,
-                    Vec::new(),
-                    prim,
-                );
             }
             BorderDetails::Normal(ref border) => {
                 self.add_normal_border(
                     info,
                     border,
                     border_item.widths,
                     clip_and_scroll,
                 );
@@ -2143,17 +2173,17 @@ impl<'a> DisplayListFlattener<'a> {
         start_point: LayoutPoint,
         end_point: LayoutPoint,
         stops: ItemRange<GradientStop>,
         extend_mode: ExtendMode,
         stretch_size: LayoutSize,
         mut tile_spacing: LayoutSize,
         pipeline_id: PipelineId,
         nine_patch: Option<Box<NinePatchDescriptor>>,
-    ) -> Option<PrimitiveKeyKind> {
+    ) -> Option<LinearGradient> {
         let mut prim_rect = info.rect;
         simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
 
         // TODO(gw): It seems like we should be able to look this up once in
         //           flatten_root() and pass to all children here to avoid
         //           some hash lookups?
         let display_list = self.scene.get_display_list_for_pipeline(pipeline_id);
         let mut max_alpha: f32 = 0.0;
@@ -2185,17 +2215,17 @@ impl<'a> DisplayListFlattener<'a> {
         // points, it's necessary to reverse the gradient
         // line in some cases.
         let (sp, ep) = if reverse_stops {
             (end_point, start_point)
         } else {
             (start_point, end_point)
         };
 
-        Some(PrimitiveKeyKind::LinearGradient {
+        Some(LinearGradient {
             extend_mode,
             start_point: sp.into(),
             end_point: ep.into(),
             stretch_size: stretch_size.into(),
             tile_spacing: tile_spacing.into(),
             stops,
             reverse_stops,
             nine_patch,
@@ -2210,17 +2240,17 @@ impl<'a> DisplayListFlattener<'a> {
         end_radius: f32,
         ratio_xy: f32,
         stops: ItemRange<GradientStop>,
         extend_mode: ExtendMode,
         stretch_size: LayoutSize,
         mut tile_spacing: LayoutSize,
         pipeline_id: PipelineId,
         nine_patch: Option<Box<NinePatchDescriptor>>,
-    ) -> PrimitiveKeyKind {
+    ) -> RadialGradient {
         let mut prim_rect = info.rect;
         simplify_repeated_primitive(&stretch_size, &mut tile_spacing, &mut prim_rect);
 
         // TODO(gw): It seems like we should be able to look this up once in
         //           flatten_root() and pass to all children here to avoid
         //           some hash lookups?
         let display_list = self.scene.get_display_list_for_pipeline(pipeline_id);
 
@@ -2232,17 +2262,17 @@ impl<'a> DisplayListFlattener<'a> {
 
         let stops = display_list.get(stops).map(|stop| {
             GradientStopKey {
                 offset: stop.offset,
                 color: stop.color.into(),
             }
         }).collect();
 
-        PrimitiveKeyKind::RadialGradient {
+        RadialGradient {
             extend_mode,
             center: center.into(),
             params,
             stretch_size: stretch_size.into(),
             tile_spacing: tile_spacing.into(),
             nine_patch,
             stops,
         }
--- a/gfx/wr/webrender/src/intern.rs
+++ b/gfx/wr/webrender/src/intern.rs
@@ -1,14 +1,15 @@
 /* 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 api::{LayoutPrimitiveInfo, LayoutRect};
 use internal_types::FastHashMap;
+use profiler::ResourceProfileCounter;
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::marker::PhantomData;
 use std::{mem, ops, u64};
 use std::sync::atomic::{AtomicUsize, Ordering};
 use util::VecHelper;
 
 /*
@@ -148,16 +149,17 @@ impl<S, T, M> ::std::default::Default fo
 
 impl<S, T, M> DataStore<S, T, M> where S: Debug, T: From<S>, M: Debug
 {
     /// Apply any updates from the scene builder thread to
     /// this data store.
     pub fn apply_updates(
         &mut self,
         update_list: UpdateList<S>,
+        profile_counter: &mut ResourceProfileCounter,
     ) {
         let mut data_iter = update_list.data.into_iter();
         for update in update_list.updates {
             match update.kind {
                 UpdateKind::Insert => {
                     self.items.entry(update.index).set(Item {
                         data: T::from(data_iter.next().unwrap()),
                         epoch: update_list.epoch,
@@ -166,16 +168,20 @@ impl<S, T, M> DataStore<S, T, M> where S
                 UpdateKind::Remove => {
                     self.items[update.index].epoch = Epoch::INVALID;
                 }
                 UpdateKind::UpdateEpoch => {
                     self.items[update.index].epoch = update_list.epoch;
                 }
             }
         }
+
+        let per_item_size = mem::size_of::<S>() + mem::size_of::<T>();
+        profile_counter.set(self.items.len(), per_item_size * self.items.len());
+
         debug_assert!(data_iter.next().is_none());
     }
 }
 
 /// Retrieve an item from the store via handle
 impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M>
 where M: Copy
 {
--- a/gfx/wr/webrender/src/picture.rs
+++ b/gfx/wr/webrender/src/picture.rs
@@ -1296,21 +1296,25 @@ impl PrimitiveList {
             let prim_data = match prim_instance.kind {
                 PrimitiveInstanceKind::Picture { data_handle, .. } |
                 PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
                 PrimitiveInstanceKind::NormalBorder { data_handle, .. } |
                 PrimitiveInstanceKind::ImageBorder { data_handle, .. } |
                 PrimitiveInstanceKind::Rectangle { data_handle, .. } |
                 PrimitiveInstanceKind::YuvImage { data_handle, .. } |
                 PrimitiveInstanceKind::Image { data_handle, .. } |
-                PrimitiveInstanceKind::LinearGradient { data_handle, .. } |
-                PrimitiveInstanceKind::RadialGradient { data_handle, ..} |
-                PrimitiveInstanceKind::Clear {  data_handle, .. } => {
+                PrimitiveInstanceKind::Clear { data_handle, .. } => {
                     &resources.prim_interner[data_handle]
                 }
+                PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
+                    &resources.linear_grad_interner[data_handle]
+                }
+                PrimitiveInstanceKind::RadialGradient { data_handle, ..} => {
+                    &resources.radial_grad_interner[data_handle]
+                }
                 PrimitiveInstanceKind::TextRun { data_handle, .. } => {
                     &resources.text_run_interner[data_handle]
                 }
             };
 
             // Get the key for the cluster that this primitive should
             // belong to.
             let key = PrimitiveClusterKey {
new file mode 100644
--- /dev/null
+++ b/gfx/wr/webrender/src/prim_store/gradient.rs
@@ -0,0 +1,729 @@
+/* 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 api::{
+    ColorF, ColorU,ExtendMode, GradientStop, LayoutPoint, LayoutSize,
+    LayoutPrimitiveInfo, LayoutRect, PremultipliedColorF
+};
+use display_list_flattener::{AsInstanceKind, IsVisible};
+use frame_builder::FrameBuildingState;
+use gpu_cache::{GpuCacheHandle, GpuDataRequest};
+use intern::{DataStore, Handle, Internable, Interner, UpdateList};
+use prim_store::{BrushSegment, GradientTileRange};
+use prim_store::{PrimitiveInstanceKind, PrimitiveOpacity, PrimitiveSceneData};
+use prim_store::{PrimKeyCommonData, PrimTemplateCommonData, PrimitiveStore};
+use prim_store::{NinePatchDescriptor, PointKey, SizeKey};
+use std::{hash, ops::{Deref, DerefMut}, mem};
+use util::pack_as_float;
+
+/// A hashable gradient stop that can be used in primitive keys.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug, Clone, PartialEq)]
+pub struct GradientStopKey {
+    pub offset: f32,
+    pub color: ColorU,
+}
+
+impl Eq for GradientStopKey {}
+
+impl hash::Hash for GradientStopKey {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        self.offset.to_bits().hash(state);
+        self.color.hash(state);
+    }
+}
+
+/// Identifying key for a line decoration.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct LinearGradientKey {
+    pub common: PrimKeyCommonData,
+    pub extend_mode: ExtendMode,
+    pub start_point: PointKey,
+    pub end_point: PointKey,
+    pub stretch_size: SizeKey,
+    pub tile_spacing: SizeKey,
+    pub stops: Vec<GradientStopKey>,
+    pub reverse_stops: bool,
+    pub nine_patch: Option<Box<NinePatchDescriptor>>,
+}
+
+impl LinearGradientKey {
+    pub fn new(
+        is_backface_visible: bool,
+        prim_size: LayoutSize,
+        prim_relative_clip_rect: LayoutRect,
+        linear_grad: LinearGradient,
+    ) -> Self {
+        LinearGradientKey {
+            common: PrimKeyCommonData {
+                is_backface_visible,
+                prim_size: prim_size.into(),
+                prim_relative_clip_rect: prim_relative_clip_rect.into(),
+            },
+            extend_mode: linear_grad.extend_mode,
+            start_point: linear_grad.start_point,
+            end_point: linear_grad.end_point,
+            stretch_size: linear_grad.stretch_size,
+            tile_spacing: linear_grad.tile_spacing,
+            stops: linear_grad.stops,
+            reverse_stops: linear_grad.reverse_stops,
+            nine_patch: linear_grad.nine_patch,
+        }
+    }
+}
+
+impl AsInstanceKind<LinearGradientDataHandle> for LinearGradientKey {
+    /// Construct a primitive instance that matches the type
+    /// of primitive key.
+    fn as_instance_kind(
+        &self,
+        data_handle: LinearGradientDataHandle,
+        _prim_store: &mut PrimitiveStore,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::LinearGradient {
+            data_handle,
+            visible_tiles_range: GradientTileRange::empty(),
+        }
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct LinearGradientTemplate {
+    pub common: PrimTemplateCommonData,
+    pub extend_mode: ExtendMode,
+    pub start_point: LayoutPoint,
+    pub end_point: LayoutPoint,
+    pub stretch_size: LayoutSize,
+    pub tile_spacing: LayoutSize,
+    pub stops_opacity: PrimitiveOpacity,
+    pub stops: Vec<GradientStop>,
+    pub brush_segments: Vec<BrushSegment>,
+    pub reverse_stops: bool,
+    pub stops_handle: GpuCacheHandle,
+}
+
+impl Deref for LinearGradientTemplate {
+    type Target = PrimTemplateCommonData;
+    fn deref(&self) -> &Self::Target {
+        &self.common
+    }
+}
+
+impl DerefMut for LinearGradientTemplate {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.common
+    }
+}
+
+impl From<LinearGradientKey> for LinearGradientTemplate {
+    fn from(item: LinearGradientKey) -> Self {
+        let common = PrimTemplateCommonData::with_key_common(item.common);
+        let mut min_alpha: f32 = 1.0;
+
+        // Convert the stops to more convenient representation
+        // for the current gradient builder.
+        let stops = item.stops.iter().map(|stop| {
+            let color: ColorF = stop.color.into();
+            min_alpha = min_alpha.min(color.a);
+
+            GradientStop {
+                offset: stop.offset,
+                color,
+            }
+        }).collect();
+
+        let mut brush_segments = Vec::new();
+
+        if let Some(ref nine_patch) = item.nine_patch {
+            brush_segments = nine_patch.create_segments(common.prim_size);
+        }
+
+        // Save opacity of the stops for use in
+        // selecting which pass this gradient
+        // should be drawn in.
+        let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
+
+        LinearGradientTemplate {
+            common,
+            extend_mode: item.extend_mode,
+            start_point: item.start_point.into(),
+            end_point: item.end_point.into(),
+            stretch_size: item.stretch_size.into(),
+            tile_spacing: item.tile_spacing.into(),
+            stops_opacity,
+            stops,
+            brush_segments,
+            reverse_stops: item.reverse_stops,
+            stops_handle: GpuCacheHandle::new(),
+        }
+    }
+}
+
+impl LinearGradientTemplate {
+    /// Update the GPU cache for a given primitive template. This may be called multiple
+    /// times per frame, by each primitive reference that refers to this interned
+    /// template. The initial request call to the GPU cache ensures that work is only
+    /// done if the cache entry is invalid (due to first use or eviction).
+    pub fn update(
+        &mut self,
+        frame_state: &mut FrameBuildingState,
+    ) {
+        if let Some(mut request) =
+            frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
+            // write_prim_gpu_blocks
+            request.push([
+                self.start_point.x,
+                self.start_point.y,
+                self.end_point.x,
+                self.end_point.y,
+            ]);
+            request.push([
+                pack_as_float(self.extend_mode as u32),
+                self.stretch_size.width,
+                self.stretch_size.height,
+                0.0,
+            ]);
+
+            // write_segment_gpu_blocks
+            for segment in &self.brush_segments {
+                // has to match VECS_PER_SEGMENT
+                request.write_segment(
+                    segment.local_rect,
+                    segment.extra_data,
+                );
+            }
+        }
+
+        if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) {
+            GradientGpuBlockBuilder::build(
+                self.reverse_stops,
+                &mut request,
+                &self.stops,
+            );
+        }
+
+        self.opacity = {
+            // If the coverage of the gradient extends to or beyond
+            // the primitive rect, then the opacity can be determined
+            // by the colors of the stops. If we have tiling / spacing
+            // then we just assume the gradient is translucent for now.
+            // (In the future we could consider segmenting in some cases).
+            let stride = self.stretch_size + self.tile_spacing;
+            if stride.width >= self.common.prim_size.width &&
+               stride.height >= self.common.prim_size.height {
+                self.stops_opacity
+            } else {
+               PrimitiveOpacity::translucent()
+            }
+        }
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub struct LinearGradientDataMarker;
+
+pub type LinearGradientDataStore = DataStore<LinearGradientKey, LinearGradientTemplate, LinearGradientDataMarker>;
+pub type LinearGradientDataHandle = Handle<LinearGradientDataMarker>;
+pub type LinearGradientDataUpdateList = UpdateList<LinearGradientKey>;
+pub type LinearGradientDataInterner = Interner<LinearGradientKey, PrimitiveSceneData, LinearGradientDataMarker>;
+
+pub struct LinearGradient {
+    pub extend_mode: ExtendMode,
+    pub start_point: PointKey,
+    pub end_point: PointKey,
+    pub stretch_size: SizeKey,
+    pub tile_spacing: SizeKey,
+    pub stops: Vec<GradientStopKey>,
+    pub reverse_stops: bool,
+    pub nine_patch: Option<Box<NinePatchDescriptor>>,
+}
+
+impl Internable for LinearGradient {
+    type Marker = LinearGradientDataMarker;
+    type Source = LinearGradientKey;
+    type StoreData = LinearGradientTemplate;
+    type InternData = PrimitiveSceneData;
+
+    /// Build a new key from self with `info`.
+    fn build_key(
+        self,
+        info: &LayoutPrimitiveInfo,
+        prim_relative_clip_rect: LayoutRect
+    ) -> LinearGradientKey {
+        LinearGradientKey::new(
+            info.is_backface_visible,
+            info.rect.size,
+            prim_relative_clip_rect,
+            self
+        )
+    }
+}
+
+impl IsVisible for LinearGradient {
+    fn is_visible(&self) -> bool {
+        true
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/// Hashable radial gradient parameters, for use during prim interning.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug, Clone, PartialEq)]
+pub struct RadialGradientParams {
+    pub start_radius: f32,
+    pub end_radius: f32,
+    pub ratio_xy: f32,
+}
+
+impl Eq for RadialGradientParams {}
+
+impl hash::Hash for RadialGradientParams {
+    fn hash<H: hash::Hasher>(&self, state: &mut H) {
+        self.start_radius.to_bits().hash(state);
+        self.end_radius.to_bits().hash(state);
+        self.ratio_xy.to_bits().hash(state);
+    }
+}
+
+/// Identifying key for a line decoration.
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct RadialGradientKey {
+    pub common: PrimKeyCommonData,
+    pub extend_mode: ExtendMode,
+    pub center: PointKey,
+    pub params: RadialGradientParams,
+    pub stretch_size: SizeKey,
+    pub stops: Vec<GradientStopKey>,
+    pub tile_spacing: SizeKey,
+    pub nine_patch: Option<Box<NinePatchDescriptor>>,
+}
+
+impl RadialGradientKey {
+    pub fn new(
+        is_backface_visible: bool,
+        prim_size: LayoutSize,
+        prim_relative_clip_rect: LayoutRect,
+        radial_grad: RadialGradient,
+    ) -> Self {
+        RadialGradientKey {
+            common: PrimKeyCommonData {
+                is_backface_visible,
+                prim_size: prim_size.into(),
+                prim_relative_clip_rect: prim_relative_clip_rect.into(),
+            },
+            extend_mode: radial_grad.extend_mode,
+            center: radial_grad.center,
+            params: radial_grad.params,
+            stretch_size: radial_grad.stretch_size,
+            stops: radial_grad.stops,
+            tile_spacing: radial_grad.tile_spacing,
+            nine_patch: radial_grad.nine_patch,
+        }
+    }
+}
+
+impl AsInstanceKind<RadialGradientDataHandle> for RadialGradientKey {
+    /// Construct a primitive instance that matches the type
+    /// of primitive key.
+    fn as_instance_kind(
+        &self,
+        data_handle: RadialGradientDataHandle,
+        _prim_store: &mut PrimitiveStore,
+    ) -> PrimitiveInstanceKind {
+        PrimitiveInstanceKind::RadialGradient {
+            data_handle,
+            visible_tiles_range: GradientTileRange::empty(),
+        }
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct RadialGradientTemplate {
+    pub common: PrimTemplateCommonData,
+    pub extend_mode: ExtendMode,
+    pub center: LayoutPoint,
+    pub params: RadialGradientParams,
+    pub stretch_size: LayoutSize,
+    pub tile_spacing: LayoutSize,
+    pub brush_segments: Vec<BrushSegment>,
+    pub stops: Vec<GradientStop>,
+    pub stops_handle: GpuCacheHandle,
+}
+
+impl Deref for RadialGradientTemplate {
+    type Target = PrimTemplateCommonData;
+    fn deref(&self) -> &Self::Target {
+        &self.common
+    }
+}
+
+impl DerefMut for RadialGradientTemplate {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.common
+    }
+}
+
+impl From<RadialGradientKey> for RadialGradientTemplate {
+    fn from(item: RadialGradientKey) -> Self {
+        let common = PrimTemplateCommonData::with_key_common(item.common);
+        let mut brush_segments = Vec::new();
+
+        if let Some(ref nine_patch) = item.nine_patch {
+            brush_segments = nine_patch.create_segments(common.prim_size);
+        }
+
+        let stops = item.stops.iter().map(|stop| {
+            GradientStop {
+                offset: stop.offset,
+                color: stop.color.into(),
+            }
+        }).collect();
+
+        RadialGradientTemplate {
+            common,
+            center: item.center.into(),
+            extend_mode: item.extend_mode,
+            params: item.params,
+            stretch_size: item.stretch_size.into(),
+            tile_spacing: item.tile_spacing.into(),
+            brush_segments: brush_segments,
+            stops,
+            stops_handle: GpuCacheHandle::new(),
+        }
+    }
+}
+
+impl RadialGradientTemplate {
+    /// Update the GPU cache for a given primitive template. This may be called multiple
+    /// times per frame, by each primitive reference that refers to this interned
+    /// template. The initial request call to the GPU cache ensures that work is only
+    /// done if the cache entry is invalid (due to first use or eviction).
+    pub fn update(
+        &mut self,
+        frame_state: &mut FrameBuildingState,
+    ) {
+        if let Some(mut request) =
+            frame_state.gpu_cache.request(&mut self.common.gpu_cache_handle) {
+            // write_prim_gpu_blocks
+            request.push([
+                self.center.x,
+                self.center.y,
+                self.params.start_radius,
+                self.params.end_radius,
+            ]);
+            request.push([
+                self.params.ratio_xy,
+                pack_as_float(self.extend_mode as u32),
+                self.stretch_size.width,
+                self.stretch_size.height,
+            ]);
+
+            // write_segment_gpu_blocks
+            for segment in &self.brush_segments {
+                // has to match VECS_PER_SEGMENT
+                request.write_segment(
+                    segment.local_rect,
+                    segment.extra_data,
+                );
+            }
+        }
+
+        if let Some(mut request) = frame_state.gpu_cache.request(&mut self.stops_handle) {
+            GradientGpuBlockBuilder::build(
+                false,
+                &mut request,
+                &self.stops,
+            );
+        }
+
+        self.opacity = PrimitiveOpacity::translucent();
+    }
+}
+
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub struct RadialGradientDataMarker;
+
+pub type RadialGradientDataStore = DataStore<RadialGradientKey, RadialGradientTemplate, RadialGradientDataMarker>;
+pub type RadialGradientDataHandle = Handle<RadialGradientDataMarker>;
+pub type RadialGradientDataUpdateList = UpdateList<RadialGradientKey>;
+pub type RadialGradientDataInterner = Interner<RadialGradientKey, PrimitiveSceneData, RadialGradientDataMarker>;
+
+pub struct RadialGradient {
+    pub extend_mode: ExtendMode,
+    pub center: PointKey,
+    pub params: RadialGradientParams,
+    pub stretch_size: SizeKey,
+    pub stops: Vec<GradientStopKey>,
+    pub tile_spacing: SizeKey,
+    pub nine_patch: Option<Box<NinePatchDescriptor>>,
+}
+
+impl Internable for RadialGradient {
+    type Marker = RadialGradientDataMarker;
+    type Source = RadialGradientKey;
+    type StoreData = RadialGradientTemplate;
+    type InternData = PrimitiveSceneData;
+
+    /// Build a new key from self with `info`.
+    fn build_key(
+        self,
+        info: &LayoutPrimitiveInfo,
+        prim_relative_clip_rect: LayoutRect,
+    ) -> RadialGradientKey {
+        RadialGradientKey::new(
+            info.is_backface_visible,
+            info.rect.size,
+            prim_relative_clip_rect,
+            self,
+        )
+    }
+}
+
+impl IsVisible for RadialGradient {
+    fn is_visible(&self) -> bool {
+        true
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+// The gradient entry index for the first color stop
+pub const GRADIENT_DATA_FIRST_STOP: usize = 0;
+// The gradient entry index for the last color stop
+pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1;
+
+// The start of the gradient data table
+pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1;
+// The exclusive bound of the gradient data table
+pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP;
+// The number of entries in the gradient data table.
+pub const GRADIENT_DATA_TABLE_SIZE: usize = 128;
+
+// The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry
+pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
+
+#[derive(Debug)]
+#[repr(C)]
+// An entry in a gradient data table representing a segment of the gradient color space.
+pub struct GradientDataEntry {
+    pub start_color: PremultipliedColorF,
+    pub end_color: PremultipliedColorF,
+}
+
+// TODO(gw): Tidy this up to be a free function / module?
+struct GradientGpuBlockBuilder {}
+
+impl GradientGpuBlockBuilder {
+    /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
+    /// from start_color to end_color.
+    fn fill_colors(
+        start_idx: usize,
+        end_idx: usize,
+        start_color: &PremultipliedColorF,
+        end_color: &PremultipliedColorF,
+        entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE],
+    ) {
+        // Calculate the color difference for individual steps in the ramp.
+        let inv_steps = 1.0 / (end_idx - start_idx) as f32;
+        let step_r = (end_color.r - start_color.r) * inv_steps;
+        let step_g = (end_color.g - start_color.g) * inv_steps;
+        let step_b = (end_color.b - start_color.b) * inv_steps;
+        let step_a = (end_color.a - start_color.a) * inv_steps;
+
+        let mut cur_color = *start_color;
+
+        // Walk the ramp writing start and end colors for each entry.
+        for index in start_idx .. end_idx {
+            let entry = &mut entries[index];
+            entry.start_color = cur_color;
+            cur_color.r += step_r;
+            cur_color.g += step_g;
+            cur_color.b += step_b;
+            cur_color.a += step_a;
+            entry.end_color = cur_color;
+        }
+    }
+
+    /// Compute an index into the gradient entry table based on a gradient stop offset. This
+    /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END].
+    #[inline]
+    fn get_index(offset: f32) -> usize {
+        (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 +
+            GRADIENT_DATA_TABLE_BEGIN as f32)
+            .round() as usize
+    }
+
+    // Build the gradient data from the supplied stops, reversing them if necessary.
+    fn build(
+        reverse_stops: bool,
+        request: &mut GpuDataRequest,
+        src_stops: &[GradientStop],
+    ) {
+        // Preconditions (should be ensured by DisplayListBuilder):
+        // * we have at least two stops
+        // * first stop has offset 0.0
+        // * last stop has offset 1.0
+        let mut src_stops = src_stops.into_iter();
+        let mut cur_color = match src_stops.next() {
+            Some(stop) => {
+                debug_assert_eq!(stop.offset, 0.0);
+                stop.color.premultiplied()
+            }
+            None => {
+                error!("Zero gradient stops found!");
+                PremultipliedColorF::BLACK
+            }
+        };
+
+        // A table of gradient entries, with two colors per entry, that specify the start and end color
+        // within the segment of the gradient space represented by that entry. To lookup a gradient result,
+        // first the entry index is calculated to determine which two colors to interpolate between, then
+        // the offset within that entry bucket is used to interpolate between the two colors in that entry.
+        // This layout preserves hard stops, as the end color for a given entry can differ from the start
+        // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
+        // format for texture upload. This table requires the gradient color stops to be normalized to the
+        // range [0, 1]. The first and last entries hold the first and last color stop colors respectively,
+        // while the entries in between hold the interpolated color stop values for the range [0, 1].
+        let mut entries: [GradientDataEntry; GRADIENT_DATA_SIZE] = unsafe { mem::uninitialized() };
+
+        if reverse_stops {
+            // Fill in the first entry (for reversed stops) with the first color stop
+            GradientGpuBlockBuilder::fill_colors(
+                GRADIENT_DATA_LAST_STOP,
+                GRADIENT_DATA_LAST_STOP + 1,
+                &cur_color,
+                &cur_color,
+                &mut entries,
+            );
+
+            // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
+            // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The
+            // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
+            let mut cur_idx = GRADIENT_DATA_TABLE_END;
+            for next in src_stops {
+                let next_color = next.color.premultiplied();
+                let next_idx = Self::get_index(1.0 - next.offset);
+
+                if next_idx < cur_idx {
+                    GradientGpuBlockBuilder::fill_colors(
+                        next_idx,
+                        cur_idx,
+                        &next_color,
+                        &cur_color,
+                        &mut entries,
+                    );
+                    cur_idx = next_idx;
+                }
+
+                cur_color = next_color;
+            }
+            if cur_idx != GRADIENT_DATA_TABLE_BEGIN {
+                error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
+                GradientGpuBlockBuilder::fill_colors(
+                    GRADIENT_DATA_TABLE_BEGIN,
+                    cur_idx,
+                    &PremultipliedColorF::WHITE,
+                    &cur_color,
+                    &mut entries,
+                );
+            }
+
+            // Fill in the last entry (for reversed stops) with the last color stop
+            GradientGpuBlockBuilder::fill_colors(
+                GRADIENT_DATA_FIRST_STOP,
+                GRADIENT_DATA_FIRST_STOP + 1,
+                &cur_color,
+                &cur_color,
+                &mut entries,
+            );
+        } else {
+            // Fill in the first entry with the first color stop
+            GradientGpuBlockBuilder::fill_colors(
+                GRADIENT_DATA_FIRST_STOP,
+                GRADIENT_DATA_FIRST_STOP + 1,
+                &cur_color,
+                &cur_color,
+                &mut entries,
+            );
+
+            // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
+            // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The
+            // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
+            let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN;
+            for next in src_stops {
+                let next_color = next.color.premultiplied();
+                let next_idx = Self::get_index(next.offset);
+
+                if next_idx > cur_idx {
+                    GradientGpuBlockBuilder::fill_colors(
+                        cur_idx,
+                        next_idx,
+                        &cur_color,
+                        &next_color,
+                        &mut entries,
+                    );
+                    cur_idx = next_idx;
+                }
+
+                cur_color = next_color;
+            }
+            if cur_idx != GRADIENT_DATA_TABLE_END {
+                error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
+                GradientGpuBlockBuilder::fill_colors(
+                    cur_idx,
+                    GRADIENT_DATA_TABLE_END,
+                    &PremultipliedColorF::WHITE,
+                    &cur_color,
+                    &mut entries,
+                );
+            }
+
+            // Fill in the last entry with the last color stop
+            GradientGpuBlockBuilder::fill_colors(
+                GRADIENT_DATA_LAST_STOP,
+                GRADIENT_DATA_LAST_STOP + 1,
+                &cur_color,
+                &cur_color,
+                &mut entries,
+            );
+        }
+
+        for entry in entries.iter() {
+            request.push(entry.start_color);
+            request.push(entry.end_color);
+        }
+    }
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_struct_sizes() {
+    use std::mem;
+    // The sizes of these structures are critical for performance on a number of
+    // talos stress tests. If you get a failure here on CI, there's two possibilities:
+    // (a) You made a structure smaller than it currently is. Great work! Update the
+    //     test expectations and move on.
+    // (b) You made a structure larger. This is not necessarily a problem, but should only
+    //     be done with care, and after checking if talos performance regresses badly.
+    assert_eq!(mem::size_of::<LinearGradient>(), 72, "LinearGradient size changed");
+    assert_eq!(mem::size_of::<LinearGradientTemplate>(), 168, "LinearGradientTemplate size changed");
+    assert_eq!(mem::size_of::<LinearGradientKey>(), 96, "LinearGradientKey size changed");
+
+    assert_eq!(mem::size_of::<RadialGradient>(), 72, "RadialGradient size changed");
+    assert_eq!(mem::size_of::<RadialGradientTemplate>(), 168, "RadialGradientTemplate size changed");
+    assert_eq!(mem::size_of::<RadialGradientKey>(), 104, "RadialGradientKey size changed");
+}
--- a/gfx/wr/webrender/src/prim_store/mod.rs
+++ b/gfx/wr/webrender/src/prim_store/mod.rs
@@ -1,15 +1,15 @@
 /* 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 api::{AlphaType, BorderRadius, ClipMode, ColorF, PictureRect, ColorU, LayoutVector2D};
-use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, ExtendMode, DeviceRect, LayoutSideOffsetsAu};
-use api::{FilterOp, GradientStop, ImageKey, ImageRendering, TileOffset, RepeatMode};
+use api::{DeviceIntRect, DeviceIntSize, DevicePixelScale, DeviceRect, LayoutSideOffsetsAu};
+use api::{FilterOp, ImageKey, ImageRendering, TileOffset, RepeatMode};
 use api::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize};
 use api::{PremultipliedColorF, PropertyBinding, Shadow, YuvColorSpace, YuvFormat};
 use api::{DeviceIntSideOffsets, WorldPixel, BoxShadowClipMode, NormalBorder, WorldRect, LayoutToWorldScale};
 use api::{PicturePixel, RasterPixel, ColorDepth, LineStyle, LineOrientation, LayoutSizeAu, AuHelpers};
 use api::LayoutPrimitiveInfo;
 use app_units::Au;
 use border::{get_max_scale_for_border, build_border_instances, create_border_segments};
 use border::{BorderSegmentCacheKey, NormalBorderAu};
@@ -23,35 +23,37 @@ use frame_builder::PrimitiveContext;
 use glyph_rasterizer::GlyphKey;
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle, GpuDataRequest, ToGpuBlocks};
 use gpu_types::BrushFlags;
 use image::{self, Repetition};
 use intern;
 use internal_types::FastHashMap;
 use picture::{PictureCompositeMode, PicturePrimitive, PictureUpdateState};
 use picture::{ClusterRange, PrimitiveList, SurfaceIndex, TileDescriptor};
+use prim_store::gradient::{LinearGradientDataHandle, RadialGradientDataHandle};
 use prim_store::text_run::{TextRunDataHandle, TextRunPrimitive};
 #[cfg(debug_assertions)]
 use render_backend::{FrameId};
 use render_backend::FrameResources;
 use render_task::{BlitSource, RenderTask, RenderTaskCacheKey, to_cache_size};
 use render_task::{RenderTaskCacheKeyKind, RenderTaskId, RenderTaskCacheEntryHandle};
 use renderer::{MAX_VERTEX_TEXTURE_WIDTH};
 use resource_cache::{ImageProperties, ImageRequest, ResourceCache};
 use scene::SceneProperties;
 use segment::SegmentBuilder;
-use std::{cmp, fmt, hash, mem, ops, u32, usize};
+use std::{cmp, fmt, hash, ops, u32, usize};
 #[cfg(debug_assertions)]
 use std::sync::atomic::{AtomicUsize, Ordering};
 use storage;
 use texture_cache::TextureCacheHandle;
 use util::{ScaleOffset, MatrixHelpers, MaxRect, recycle_vec};
 use util::{pack_as_float, project_rect, raster_rect_to_device_pixels};
 use smallvec::SmallVec;
 
+pub mod gradient;
 pub mod text_run;
 
 /// Counter for unique primitive IDs for debug tracing.
 #[cfg(debug_assertions)]
 static NEXT_PRIM_ID: AtomicUsize = AtomicUsize::new(0);
 
 #[cfg(debug_assertions)]
 static PRIM_CHASE_ID: AtomicUsize = AtomicUsize::new(usize::MAX);
@@ -386,53 +388,16 @@ pub enum PrimitiveKeyKind {
         key: ImageKey,
         stretch_size: SizeKey,
         tile_spacing: SizeKey,
         color: ColorU,
         sub_rect: Option<DeviceIntRect>,
         image_rendering: ImageRendering,
         alpha_type: AlphaType,
     },
-    LinearGradient {
-        extend_mode: ExtendMode,
-        start_point: PointKey,
-        end_point: PointKey,
-        stretch_size: SizeKey,
-        tile_spacing: SizeKey,
-        stops: Vec<GradientStopKey>,
-        reverse_stops: bool,
-        nine_patch: Option<Box<NinePatchDescriptor>>,
-    },
-    RadialGradient {
-        extend_mode: ExtendMode,
-        center: PointKey,
-        params: RadialGradientParams,
-        stretch_size: SizeKey,
-        stops: Vec<GradientStopKey>,
-        tile_spacing: SizeKey,
-        nine_patch: Option<Box<NinePatchDescriptor>>,
-    },
-}
-
-/// A hashable gradient stop that can be used in primitive keys.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Debug, Clone, PartialEq)]
-pub struct GradientStopKey {
-    pub offset: f32,
-    pub color: ColorU,
-}
-
-impl Eq for GradientStopKey {}
-
-impl hash::Hash for GradientStopKey {
-    fn hash<H: hash::Hasher>(&self, state: &mut H) {
-        self.offset.to_bits().hash(state);
-        self.color.hash(state);
-    }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, PartialEq)]
 pub struct RectangleKey {
     x: f32,
     y: f32,
@@ -563,36 +528,16 @@ impl SizeKey {
     pub fn zero() -> SizeKey {
         SizeKey {
             w: 0.0,
             h: 0.0,
         }
     }
 }
 
-/// Hashable radial gradient parameters, for use during prim interning.
-#[cfg_attr(feature = "capture", derive(Serialize))]
-#[cfg_attr(feature = "replay", derive(Deserialize))]
-#[derive(Debug, Clone, PartialEq)]
-pub struct RadialGradientParams {
-    pub start_radius: f32,
-    pub end_radius: f32,
-    pub ratio_xy: f32,
-}
-
-impl Eq for RadialGradientParams {}
-
-impl hash::Hash for RadialGradientParams {
-    fn hash<H: hash::Hasher>(&self, state: &mut H) {
-        self.start_radius.to_bits().hash(state);
-        self.end_radius.to_bits().hash(state);
-        self.ratio_xy.to_bits().hash(state);
-    }
-}
-
 /// A hashable point for using as a key during primitive interning.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Clone, PartialEq)]
 pub struct PointKey {
     x: f32,
     y: f32,
 }
@@ -722,28 +667,16 @@ impl AsInstanceKind<PrimitiveDataHandle>
                     visible_tiles: Vec::new(),
                 });
 
                 PrimitiveInstanceKind::Image {
                     data_handle,
                     image_instance_index,
                 }
             }
-            PrimitiveKeyKind::LinearGradient { .. } => {
-                PrimitiveInstanceKind::LinearGradient {
-                    data_handle,
-                    visible_tiles_range: GradientTileRange::empty(),
-                }
-            }
-            PrimitiveKeyKind::RadialGradient { .. } => {
-                PrimitiveInstanceKind::RadialGradient {
-                    data_handle,
-                    visible_tiles_range: GradientTileRange::empty(),
-                }
-            }
             PrimitiveKeyKind::Unused => {
                 // Should never be hit as this method should not be
                 // called for old style primitives.
                 unreachable!();
             }
         }
     }
 }
@@ -788,38 +721,16 @@ pub enum PrimitiveTemplateKind {
         stretch_size: LayoutSize,
         tile_spacing: LayoutSize,
         color: ColorF,
         source: ImageSource,
         image_rendering: ImageRendering,
         sub_rect: Option<DeviceIntRect>,
         alpha_type: AlphaType,
     },
-    LinearGradient {
-        extend_mode: ExtendMode,
-        start_point: LayoutPoint,
-        end_point: LayoutPoint,
-        stretch_size: LayoutSize,
-        tile_spacing: LayoutSize,
-        stops_opacity: PrimitiveOpacity,
-        stops: Vec<GradientStop>,
-        brush_segments: Vec<BrushSegment>,
-        reverse_stops: bool,
-        stops_handle: GpuCacheHandle,
-    },
-    RadialGradient {
-        extend_mode: ExtendMode,
-        center: LayoutPoint,
-        params: RadialGradientParams,
-        stretch_size: LayoutSize,
-        tile_spacing: LayoutSize,
-        brush_segments: Vec<BrushSegment>,
-        stops: Vec<GradientStop>,
-        stops_handle: GpuCacheHandle,
-    },
     Clear,
     Unused,
 }
 
 /// Construct the primitive template data from a primitive key. This
 /// is invoked when a primitive key is created and the interner
 /// doesn't currently contain a primitive with this key.
 impl PrimitiveKeyKind {
@@ -898,99 +809,16 @@ impl PrimitiveKeyKind {
                 }
             }
             PrimitiveKeyKind::LineDecoration { cache_key, color } => {
                 PrimitiveTemplateKind::LineDecoration {
                     cache_key,
                     color: color.into(),
                 }
             }
-            PrimitiveKeyKind::LinearGradient {
-                extend_mode,
-                tile_spacing,
-                start_point,
-                end_point,
-                stretch_size,
-                stops,
-                reverse_stops,
-                nine_patch,
-                ..
-            } => {
-                let mut min_alpha: f32 = 1.0;
-
-                // Convert the stops to more convenient representation
-                // for the current gradient builder.
-                let stops = stops.iter().map(|stop| {
-                    let color: ColorF = stop.color.into();
-                    min_alpha = min_alpha.min(color.a);
-
-                    GradientStop {
-                        offset: stop.offset,
-                        color,
-                    }
-                }).collect();
-
-                let mut brush_segments = Vec::new();
-
-                if let Some(ref nine_patch) = nine_patch {
-                    brush_segments = nine_patch.create_segments(size);
-                }
-
-                // Save opacity of the stops for use in
-                // selecting which pass this gradient
-                // should be drawn in.
-                let stops_opacity = PrimitiveOpacity::from_alpha(min_alpha);
-
-                PrimitiveTemplateKind::LinearGradient {
-                    extend_mode,
-                    start_point: start_point.into(),
-                    end_point: end_point.into(),
-                    stretch_size: stretch_size.into(),
-                    tile_spacing: tile_spacing.into(),
-                    stops_opacity,
-                    stops,
-                    brush_segments,
-                    reverse_stops,
-                    stops_handle: GpuCacheHandle::new(),
-                }
-            }
-            PrimitiveKeyKind::RadialGradient {
-                extend_mode,
-                params,
-                stretch_size,
-                tile_spacing,
-                nine_patch,
-                center,
-                stops,
-                ..
-            } => {
-                let mut brush_segments = Vec::new();
-
-                if let Some(ref nine_patch) = nine_patch {
-                    brush_segments = nine_patch.create_segments(size);
-                }
-
-                let stops = stops.iter().map(|stop| {
-                    GradientStop {
-                        offset: stop.offset,
-                        color: stop.color.into(),
-                    }
-                }).collect();
-
-                PrimitiveTemplateKind::RadialGradient {
-                    center: center.into(),
-                    extend_mode,
-                    params,
-                    stretch_size: stretch_size.into(),
-                    tile_spacing: tile_spacing.into(),
-                    brush_segments,
-                    stops_handle: GpuCacheHandle::new(),
-                    stops,
-                }
-            }
         }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct PrimTemplateCommonData {
     pub is_backface_visible: bool,
@@ -1118,50 +946,16 @@ impl PrimitiveTemplateKind {
                 request.push(PremultipliedColorF::WHITE);
                 request.push([
                     stretch_size.width + tile_spacing.width,
                     stretch_size.height + tile_spacing.height,
                     0.0,
                     0.0,
                 ]);
             }
-            PrimitiveTemplateKind::LinearGradient { stretch_size, start_point, end_point, extend_mode, .. } => {
-                request.push([
-                    start_point.x,
-                    start_point.y,
-                    end_point.x,
-                    end_point.y,
-                ]);
-                request.push([
-                    pack_as_float(extend_mode as u32),
-                    stretch_size.width,
-                    stretch_size.height,
-                    0.0,
-                ]);
-            }
-            PrimitiveTemplateKind::RadialGradient {
-                center,
-                ref params,
-                extend_mode,
-                stretch_size,
-                ..
-            } => {
-                request.push([
-                    center.x,
-                    center.y,
-                    params.start_radius,
-                    params.end_radius,
-                ]);
-                request.push([
-                    params.ratio_xy,
-                    pack_as_float(extend_mode as u32),
-                    stretch_size.width,
-                    stretch_size.height,
-                ]);
-            }
             PrimitiveTemplateKind::Unused => {}
         }
     }
 
     fn write_segment_gpu_blocks(
         &self,
         request: &mut GpuDataRequest,
     ) {
@@ -1179,26 +973,16 @@ impl PrimitiveTemplateKind {
                 for segment in brush_segments {
                     // has to match VECS_PER_SEGMENT
                     request.write_segment(
                         segment.local_rect,
                         segment.extra_data,
                     );
                 }
             }
-            PrimitiveTemplateKind::LinearGradient { ref brush_segments, .. } |
-            PrimitiveTemplateKind::RadialGradient { ref brush_segments, .. } => {
-                for segment in brush_segments {
-                    // has to match VECS_PER_SEGMENT
-                    request.write_segment(
-                        segment.local_rect,
-                        segment.extra_data,
-                    );
-                }
-            }
             PrimitiveTemplateKind::Clear |
             PrimitiveTemplateKind::LineDecoration { .. } |
             PrimitiveTemplateKind::Image { .. } |
             PrimitiveTemplateKind::Rectangle { .. } |
             PrimitiveTemplateKind::YuvImage { .. } |
             PrimitiveTemplateKind::Unused => {}
         }
     }
@@ -1233,58 +1017,16 @@ impl PrimitiveTemplate {
             }
             PrimitiveTemplateKind::Rectangle { ref color, .. } => {
                 PrimitiveOpacity::from_alpha(color.a)
             }
             PrimitiveTemplateKind::NormalBorder { .. } => {
                 // Shouldn't matter, since the segment opacity is used instead
                 PrimitiveOpacity::translucent()
             }
-            PrimitiveTemplateKind::LinearGradient {
-                stretch_size,
-                tile_spacing,
-                stops_opacity,
-                ref mut stops_handle,
-                reverse_stops,
-                ref stops,
-                ..
-            } => {
-                if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
-                    GradientGpuBlockBuilder::build(
-                        reverse_stops,
-                        &mut request,
-                        stops,
-                    );
-                }
-
-                // If the coverage of the gradient extends to or beyond
-                // the primitive rect, then the opacity can be determined
-                // by the colors of the stops. If we have tiling / spacing
-                // then we just assume the gradient is translucent for now.
-                // (In the future we could consider segmenting in some cases).
-                let stride = stretch_size + tile_spacing;
-                if stride.width >= self.common.prim_size.width &&
-                   stride.height >= self.common.prim_size.height {
-                    stops_opacity
-                } else {
-                    PrimitiveOpacity::translucent()
-                }
-            }
-            PrimitiveTemplateKind::RadialGradient { ref mut stops_handle, ref stops, .. } => {
-                if let Some(mut request) = frame_state.gpu_cache.request(stops_handle) {
-                    GradientGpuBlockBuilder::build(
-                        false,
-                        &mut request,
-                        stops,
-                    );
-                }
-
-                //TODO: can we make it opaque in some cases?
-                PrimitiveOpacity::translucent()
-            }
             PrimitiveTemplateKind::ImageBorder { request, .. } => {
                 let image_properties = frame_state
                     .resource_cache
                     .get_image_properties(request.key);
 
                 if let Some(image_properties) = image_properties {
                     frame_state.resource_cache.request_image(
                         request,
@@ -1703,224 +1445,16 @@ pub enum ImageSource {
     // An image that is pre-rendered into the texture cache
     // via a render task.
     Cache {
         size: DeviceIntSize,
         handle: Option<RenderTaskCacheEntryHandle>,
     },
 }
 
-// The gradient entry index for the first color stop
-pub const GRADIENT_DATA_FIRST_STOP: usize = 0;
-// The gradient entry index for the last color stop
-pub const GRADIENT_DATA_LAST_STOP: usize = GRADIENT_DATA_SIZE - 1;
-
-// The start of the gradient data table
-pub const GRADIENT_DATA_TABLE_BEGIN: usize = GRADIENT_DATA_FIRST_STOP + 1;
-// The exclusive bound of the gradient data table
-pub const GRADIENT_DATA_TABLE_END: usize = GRADIENT_DATA_LAST_STOP;
-// The number of entries in the gradient data table.
-pub const GRADIENT_DATA_TABLE_SIZE: usize = 128;
-
-// The number of entries in a gradient data: GRADIENT_DATA_TABLE_SIZE + first stop entry + last stop entry
-pub const GRADIENT_DATA_SIZE: usize = GRADIENT_DATA_TABLE_SIZE + 2;
-
-#[derive(Debug)]
-#[repr(C)]
-// An entry in a gradient data table representing a segment of the gradient color space.
-pub struct GradientDataEntry {
-    pub start_color: PremultipliedColorF,
-    pub end_color: PremultipliedColorF,
-}
-
-// TODO(gw): Tidy this up to be a free function / module?
-struct GradientGpuBlockBuilder {}
-
-impl GradientGpuBlockBuilder {
-    /// Generate a color ramp filling the indices in [start_idx, end_idx) and interpolating
-    /// from start_color to end_color.
-    fn fill_colors(
-        start_idx: usize,
-        end_idx: usize,
-        start_color: &PremultipliedColorF,
-        end_color: &PremultipliedColorF,
-        entries: &mut [GradientDataEntry; GRADIENT_DATA_SIZE],
-    ) {
-        // Calculate the color difference for individual steps in the ramp.
-        let inv_steps = 1.0 / (end_idx - start_idx) as f32;
-        let step_r = (end_color.r - start_color.r) * inv_steps;
-        let step_g = (end_color.g - start_color.g) * inv_steps;
-        let step_b = (end_color.b - start_color.b) * inv_steps;
-        let step_a = (end_color.a - start_color.a) * inv_steps;
-
-        let mut cur_color = *start_color;
-
-        // Walk the ramp writing start and end colors for each entry.
-        for index in start_idx .. end_idx {
-            let entry = &mut entries[index];
-            entry.start_color = cur_color;
-            cur_color.r += step_r;
-            cur_color.g += step_g;
-            cur_color.b += step_b;
-            cur_color.a += step_a;
-            entry.end_color = cur_color;
-        }
-    }
-
-    /// Compute an index into the gradient entry table based on a gradient stop offset. This
-    /// function maps offsets from [0, 1] to indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END].
-    #[inline]
-    fn get_index(offset: f32) -> usize {
-        (offset.max(0.0).min(1.0) * GRADIENT_DATA_TABLE_SIZE as f32 +
-            GRADIENT_DATA_TABLE_BEGIN as f32)
-            .round() as usize
-    }
-
-    // Build the gradient data from the supplied stops, reversing them if necessary.
-    fn build(
-        reverse_stops: bool,
-        request: &mut GpuDataRequest,
-        src_stops: &[GradientStop],
-    ) {
-        // Preconditions (should be ensured by DisplayListBuilder):
-        // * we have at least two stops
-        // * first stop has offset 0.0
-        // * last stop has offset 1.0
-        let mut src_stops = src_stops.into_iter();
-        let mut cur_color = match src_stops.next() {
-            Some(stop) => {
-                debug_assert_eq!(stop.offset, 0.0);
-                stop.color.premultiplied()
-            }
-            None => {
-                error!("Zero gradient stops found!");
-                PremultipliedColorF::BLACK
-            }
-        };
-
-        // A table of gradient entries, with two colors per entry, that specify the start and end color
-        // within the segment of the gradient space represented by that entry. To lookup a gradient result,
-        // first the entry index is calculated to determine which two colors to interpolate between, then
-        // the offset within that entry bucket is used to interpolate between the two colors in that entry.
-        // This layout preserves hard stops, as the end color for a given entry can differ from the start
-        // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8
-        // format for texture upload. This table requires the gradient color stops to be normalized to the
-        // range [0, 1]. The first and last entries hold the first and last color stop colors respectively,
-        // while the entries in between hold the interpolated color stop values for the range [0, 1].
-        let mut entries: [GradientDataEntry; GRADIENT_DATA_SIZE] = unsafe { mem::uninitialized() };
-
-        if reverse_stops {
-            // Fill in the first entry (for reversed stops) with the first color stop
-            GradientGpuBlockBuilder::fill_colors(
-                GRADIENT_DATA_LAST_STOP,
-                GRADIENT_DATA_LAST_STOP + 1,
-                &cur_color,
-                &cur_color,
-                &mut entries,
-            );
-
-            // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
-            // of gradient stops. Each iteration of a loop will fill the indices in [next_idx, cur_idx). The
-            // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
-            let mut cur_idx = GRADIENT_DATA_TABLE_END;
-            for next in src_stops {
-                let next_color = next.color.premultiplied();
-                let next_idx = Self::get_index(1.0 - next.offset);
-
-                if next_idx < cur_idx {
-                    GradientGpuBlockBuilder::fill_colors(
-                        next_idx,
-                        cur_idx,
-                        &next_color,
-                        &cur_color,
-                        &mut entries,
-                    );
-                    cur_idx = next_idx;
-                }
-
-                cur_color = next_color;
-            }
-            if cur_idx != GRADIENT_DATA_TABLE_BEGIN {
-                error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
-                GradientGpuBlockBuilder::fill_colors(
-                    GRADIENT_DATA_TABLE_BEGIN,
-                    cur_idx,
-                    &PremultipliedColorF::WHITE,
-                    &cur_color,
-                    &mut entries,
-                );
-            }
-
-            // Fill in the last entry (for reversed stops) with the last color stop
-            GradientGpuBlockBuilder::fill_colors(
-                GRADIENT_DATA_FIRST_STOP,
-                GRADIENT_DATA_FIRST_STOP + 1,
-                &cur_color,
-                &cur_color,
-                &mut entries,
-            );
-        } else {
-            // Fill in the first entry with the first color stop
-            GradientGpuBlockBuilder::fill_colors(
-                GRADIENT_DATA_FIRST_STOP,
-                GRADIENT_DATA_FIRST_STOP + 1,
-                &cur_color,
-                &cur_color,
-                &mut entries,
-            );
-
-            // Fill in the center of the gradient table, generating a color ramp between each consecutive pair
-            // of gradient stops. Each iteration of a loop will fill the indices in [cur_idx, next_idx). The
-            // loop will then fill indices in [GRADIENT_DATA_TABLE_BEGIN, GRADIENT_DATA_TABLE_END).
-            let mut cur_idx = GRADIENT_DATA_TABLE_BEGIN;
-            for next in src_stops {
-                let next_color = next.color.premultiplied();
-                let next_idx = Self::get_index(next.offset);
-
-                if next_idx > cur_idx {
-                    GradientGpuBlockBuilder::fill_colors(
-                        cur_idx,
-                        next_idx,
-                        &cur_color,
-                        &next_color,
-                        &mut entries,
-                    );
-                    cur_idx = next_idx;
-                }
-
-                cur_color = next_color;
-            }
-            if cur_idx != GRADIENT_DATA_TABLE_END {
-                error!("Gradient stops abruptly at {}, auto-completing to white", cur_idx);
-                GradientGpuBlockBuilder::fill_colors(
-                    cur_idx,
-                    GRADIENT_DATA_TABLE_END,
-                    &PremultipliedColorF::WHITE,
-                    &cur_color,
-                    &mut entries,
-                );
-            }
-
-            // Fill in the last entry with the last color stop
-            GradientGpuBlockBuilder::fill_colors(
-                GRADIENT_DATA_LAST_STOP,
-                GRADIENT_DATA_LAST_STOP + 1,
-                &cur_color,
-                &cur_color,
-                &mut entries,
-            );
-        }
-
-        for entry in entries.iter() {
-            request.push(entry.start_color);
-            request.push(entry.end_color);
-        }
-    }
-}
-
 #[derive(Debug)]
 #[repr(C)]
 struct ClipRect {
     rect: LayoutRect,
     mode: f32,
 }
 
 #[derive(Debug)]
@@ -2148,18 +1682,16 @@ impl IsVisible for PrimitiveKeyKind {
     //           we should move the logic for all other
     //           primitive types to use this.
     fn is_visible(&self) -> bool {
         match *self {
             PrimitiveKeyKind::NormalBorder { .. } |
             PrimitiveKeyKind::ImageBorder { .. } |
             PrimitiveKeyKind::YuvImage { .. } |
             PrimitiveKeyKind::Image { .. } |
-            PrimitiveKeyKind::LinearGradient { .. } |
-            PrimitiveKeyKind::RadialGradient { .. } |
             PrimitiveKeyKind::Clear |
             PrimitiveKeyKind::Unused => {
                 true
             }
             PrimitiveKeyKind::Rectangle { ref color, .. } |
             PrimitiveKeyKind::LineDecoration { ref color, .. } => {
                 color.a > 0
             }
@@ -2202,18 +1734,16 @@ impl CreateShadow for PrimitiveKeyKind {
                     sub_rect,
                     image_rendering,
                     alpha_type,
                     color: shadow.color.into(),
                 }
             }
             PrimitiveKeyKind::ImageBorder { .. } |
             PrimitiveKeyKind::YuvImage { .. } |
-            PrimitiveKeyKind::LinearGradient { .. } |
-            PrimitiveKeyKind::RadialGradient { .. } |
             PrimitiveKeyKind::Unused |
             PrimitiveKeyKind::Clear => {
                 panic!("bug: this prim is not supported in shadow contexts");
             }
         }
     }
 }
 
@@ -2293,22 +1823,22 @@ pub enum PrimitiveInstanceKind {
     },
     Image {
         /// Handle to the common interned data for this primitive.
         data_handle: PrimitiveDataHandle,
         image_instance_index: ImageInstanceIndex,
     },
     LinearGradient {
         /// Handle to the common interned data for this primitive.
-        data_handle: PrimitiveDataHandle,
+        data_handle: LinearGradientDataHandle,
         visible_tiles_range: GradientTileRange,
     },
     RadialGradient {
         /// Handle to the common interned data for this primitive.
-        data_handle: PrimitiveDataHandle,
+        data_handle: RadialGradientDataHandle,
         visible_tiles_range: GradientTileRange,
     },
     /// Clear out a rect, used for special effects.
     Clear {
         /// Handle to the common interned data for this primitive.
         data_handle: PrimitiveDataHandle,
     },
 }
@@ -2396,18 +1926,22 @@ impl PrimitiveInstance {
         match &self.kind {
             PrimitiveInstanceKind::Picture { data_handle, .. } |
             PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
             PrimitiveInstanceKind::Clear { data_handle, .. } |
             PrimitiveInstanceKind::NormalBorder { data_handle, .. } |
             PrimitiveInstanceKind::ImageBorder { data_handle, .. } |
             PrimitiveInstanceKind::Rectangle { data_handle, .. } |
             PrimitiveInstanceKind::YuvImage { data_handle, .. } |
-            PrimitiveInstanceKind::Image { data_handle, .. } |
-            PrimitiveInstanceKind::LinearGradient { data_handle, .. } |
+            PrimitiveInstanceKind::Image { data_handle, .. } => {
+                data_handle.uid()
+            }
+            PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
+                data_handle.uid()
+            }
             PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
                 data_handle.uid()
             }
             PrimitiveInstanceKind::TextRun { data_handle, .. } => {
                 data_handle.uid()
             }
         }
     }
@@ -3491,105 +3025,85 @@ impl PrimitiveStore {
                             prim_instance.bounding_rect = None;
                         }
                     }
                 }
 
                 write_segment(prim_data, image_instance.segment_instance_index, frame_state, scratch);
             }
             PrimitiveInstanceKind::LinearGradient { data_handle, ref mut visible_tiles_range, .. } => {
-                let prim_data = &mut resources.prim_data_store[*data_handle];
+                let prim_data = &mut resources.linear_grad_data_store[*data_handle];
 
                 // Update the template this instane references, which may refresh the GPU
                 // cache with any shared template data.
-                prim_data.update(
-                    pic_context.surface_index,
-                    frame_state,
-                );
-
-                let (extend_mode, stretch_size, start_point, end_point, tile_spacing) = match prim_data.kind {
-                    PrimitiveTemplateKind::LinearGradient { ref extend_mode, ref stretch_size, ref start_point, ref end_point, ref tile_spacing, .. } => {
-                        (extend_mode, stretch_size, start_point, end_point, tile_spacing)
-                    }
-                    _ => unreachable!()
-                };
-
-                if *tile_spacing != LayoutSize::zero() {
+                prim_data.update(frame_state);
+
+                if prim_data.tile_spacing != LayoutSize::zero() {
                     *visible_tiles_range = decompose_repeated_primitive(
                         &prim_instance.combined_local_clip_rect,
                         &prim_local_rect,
-                        &stretch_size,
-                        &tile_spacing,
+                        &prim_data.stretch_size,
+                        &prim_data.tile_spacing,
                         prim_context,
                         frame_state,
                         &pic_context.dirty_world_rect,
                         &mut scratch.gradient_tiles,
                         &mut |_, mut request| {
                             request.push([
-                                start_point.x,
-                                start_point.y,
-                                end_point.x,
-                                end_point.y,
+                                prim_data.start_point.x,
+                                prim_data.start_point.y,
+                                prim_data.end_point.x,
+                                prim_data.end_point.y,
                             ]);
                             request.push([
-                                pack_as_float(*extend_mode as u32),
-                                stretch_size.width,
-                                stretch_size.height,
+                                pack_as_float(prim_data.extend_mode as u32),
+                                prim_data.stretch_size.width,
+                                prim_data.stretch_size.height,
                                 0.0,
                             ]);
                         }
                     );
 
                     if visible_tiles_range.is_empty() {
                         prim_instance.bounding_rect = None;
                     }
                 }
 
                 // TODO(gw): Consider whether it's worth doing segment building
                 //           for gradient primitives.
             }
             PrimitiveInstanceKind::RadialGradient { data_handle, ref mut visible_tiles_range, .. } => {
-                let prim_data = &mut resources.prim_data_store[*data_handle];
+                let prim_data = &mut resources.radial_grad_data_store[*data_handle];
 
                 // Update the template this instane references, which may refresh the GPU
                 // cache with any shared template data.
-                prim_data.update(
-                    pic_context.surface_index,
-                    frame_state,
-                );
-
-                let (params, extend_mode, stretch_size, tile_spacing, center) = match prim_data.kind {
-                    PrimitiveTemplateKind::RadialGradient { ref params, ref extend_mode, stretch_size, ref tile_spacing, center, .. } => {
-                        (params, extend_mode, stretch_size, tile_spacing, center)
-                    }
-                    _ => unreachable!()
-                };
-
-                if *tile_spacing != LayoutSize::zero() {
+                prim_data.update(frame_state);
+
+                if prim_data.tile_spacing != LayoutSize::zero() {
                     *visible_tiles_range = decompose_repeated_primitive(
                         &prim_instance.combined_local_clip_rect,
                         &prim_local_rect,
-                        &stretch_size,
-                        &tile_spacing,
+                        &prim_data.stretch_size,
+                        &prim_data.tile_spacing,
                         prim_context,
                         frame_state,
                         &pic_context.dirty_world_rect,
                         &mut scratch.gradient_tiles,
                         &mut |_, mut request| {
                             request.push([
-                                center.x,
-                                center.y,
-                                params.start_radius,
-                                params.end_radius,
+                                prim_data.center.x,
+                                prim_data.center.y,
+                                prim_data.params.start_radius,
+                                prim_data.params.end_radius,
                             ]);
                             request.push([
-                                params.ratio_xy,
-                                pack_as_float(*extend_mode as u32),
-                                stretch_size.width,
-                                stretch_size.height,
+                                prim_data.params.ratio_xy,
+                                pack_as_float(prim_data.extend_mode as u32),
+                                prim_data.stretch_size.width,
+                                prim_data.stretch_size.height,
                             ]);
                         },
                     );
 
                     if visible_tiles_range.is_empty() {
                         prim_instance.bounding_rect = None;
                     }
                 }
@@ -4025,50 +3539,36 @@ impl PrimitiveInstance {
                         template.brush_segments.as_slice()
                     }
                     _ => {
                         unreachable!();
                     }
                 }
             }
             PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
-                let prim_data = &resources.prim_data_store[data_handle];
+                let prim_data = &resources.linear_grad_data_store[data_handle];
 
                 // TODO: This is quite messy - once we remove legacy primitives we
                 //       can change this to be a tuple match on (instance, template)
-                match prim_data.kind {
-                    PrimitiveTemplateKind::LinearGradient { ref brush_segments, .. } => {
-                        if brush_segments.is_empty() {
-                            return false;
-                        }
-
-                        brush_segments.as_slice()
-                    }
-                    _ => {
-                        unreachable!();
-                    }
+                if prim_data.brush_segments.is_empty() {
+                    return false;
                 }
+
+                prim_data.brush_segments.as_slice()
             }
             PrimitiveInstanceKind::RadialGradient { data_handle, .. } => {
-                let prim_data = &resources.prim_data_store[data_handle];
+                let prim_data = &resources.radial_grad_data_store[data_handle];
 
                 // TODO: This is quite messy - once we remove legacy primitives we
                 //       can change this to be a tuple match on (instance, template)
-                match prim_data.kind {
-                    PrimitiveTemplateKind::RadialGradient { ref brush_segments, .. } => {
-                        if brush_segments.is_empty() {
-                            return false;
-                        }
-
-                        brush_segments.as_slice()
-                    }
-                    _ => {
-                        unreachable!();
-                    }
+                if prim_data.brush_segments.is_empty() {
+                    return false;
                 }
+
+                prim_data.brush_segments.as_slice()
             }
         };
 
         // If there are no segments, early out to avoid setting a valid
         // clip task instance location below.
         if segments.is_empty() {
             return true;
         }
@@ -4312,21 +3812,22 @@ fn update_opacity_binding(
         binding.update(scene_properties);
         binding.current
     }
 }
 
 #[test]
 #[cfg(target_os = "linux")]
 fn test_struct_sizes() {
+    use std::mem;
     // The sizes of these structures are critical for performance on a number of
     // talos stress tests. If you get a failure here on CI, there's two possibilities:
     // (a) You made a structure smaller than it currently is. Great work! Update the
     //     test expectations and move on.
     // (b) You made a structure larger. This is not necessarily a problem, but should only
     //     be done with care, and after checking if talos performance regresses badly.
     assert_eq!(mem::size_of::<PrimitiveInstance>(), 128, "PrimitiveInstance size changed");
     assert_eq!(mem::size_of::<PrimitiveInstanceKind>(), 40, "PrimitiveInstanceKind size changed");
-    assert_eq!(mem::size_of::<PrimitiveTemplate>(), 168, "PrimitiveTemplate size changed");
-    assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 112, "PrimitiveTemplateKind size changed");
-    assert_eq!(mem::size_of::<PrimitiveKey>(), 128, "PrimitiveKey size changed");
+    assert_eq!(mem::size_of::<PrimitiveTemplate>(), 144, "PrimitiveTemplate size changed");
+    assert_eq!(mem::size_of::<PrimitiveTemplateKind>(), 88, "PrimitiveTemplateKind size changed");
+    assert_eq!(mem::size_of::<PrimitiveKey>(), 124, "PrimitiveKey size changed");
     assert_eq!(mem::size_of::<PrimitiveKeyKind>(), 96, "PrimitiveKeyKind size changed");
 }
--- a/gfx/wr/webrender/src/profiler.rs
+++ b/gfx/wr/webrender/src/profiler.rs
@@ -376,16 +376,17 @@ impl GpuCacheProfileCounters {
     }
 }
 
 #[derive(Clone)]
 pub struct BackendProfileCounters {
     pub total_time: TimeProfileCounter,
     pub resources: ResourceProfileCounters,
     pub ipc: IpcProfileCounters,
+    pub intern: InternProfileCounters,
 }
 
 #[derive(Clone)]
 pub struct ResourceProfileCounters {
     pub font_templates: ResourceProfileCounter,
     pub image_templates: ResourceProfileCounter,
     pub texture_cache: TextureCacheProfileCounters,
     pub gpu_cache: GpuCacheProfileCounters,
@@ -395,16 +396,25 @@ pub struct ResourceProfileCounters {
 pub struct IpcProfileCounters {
     pub build_time: TimeProfileCounter,
     pub consume_time: TimeProfileCounter,
     pub send_time: TimeProfileCounter,
     pub total_time: TimeProfileCounter,
     pub display_lists: ResourceProfileCounter,
 }
 
+#[derive(Clone)]
+pub struct InternProfileCounters {
+    pub prims: ResourceProfileCounter,
+    pub linear_gradients: ResourceProfileCounter,
+    pub radial_gradients: ResourceProfileCounter,
+    pub text_runs: ResourceProfileCounter,
+    pub clips: ResourceProfileCounter,
+}
+
 impl IpcProfileCounters {
     pub fn set(
         &mut self,
         build_start: u64,
         build_end: u64,
         send_start: u64,
         consume_start: u64,
         consume_end: u64,
@@ -433,16 +443,23 @@ impl BackendProfileCounters {
             },
             ipc: IpcProfileCounters {
                 build_time: TimeProfileCounter::new("Display List Build Time", false),
                 consume_time: TimeProfileCounter::new("Display List Consume Time", false),
                 send_time: TimeProfileCounter::new("Display List Send Time", false),
                 total_time: TimeProfileCounter::new("Total Display List Time", false),
                 display_lists: ResourceProfileCounter::new("Display Lists Sent"),
             },
+            intern: InternProfileCounters {
+                prims: ResourceProfileCounter::new("Interned primitives"),
+                linear_gradients: ResourceProfileCounter::new("Interned linear gradients"),
+                radial_gradients: ResourceProfileCounter::new("Interned radial gradients"),
+                text_runs: ResourceProfileCounter::new("Interned text runs"),
+                clips: ResourceProfileCounter::new("Interned clips"),
+            },
         }
     }
 
     pub fn reset(&mut self) {
         self.total_time.reset();
         self.ipc.total_time.reset();
         self.ipc.build_time.reset();
         self.ipc.consume_time.reset();
@@ -1079,16 +1096,29 @@ impl Profiler {
             ],
             debug_renderer,
             true,
             &mut self.draw_state
         );
 
         Profiler::draw_counters(
             &[
+                &backend_profile.intern.clips,
+                &backend_profile.intern.prims,
+                &backend_profile.intern.linear_gradients,
+                &backend_profile.intern.radial_gradients,
+                &backend_profile.intern.text_runs,
+            ],
+            debug_renderer,
+            true,
+            &mut self.draw_state
+        );
+
+        Profiler::draw_counters(
+            &[
                 &backend_profile.resources.texture_cache.pages_a8_linear,
                 &backend_profile.resources.texture_cache.pages_rgba8_linear,
                 &backend_profile.resources.texture_cache.pages_rgba8_nearest,
                 &backend_profile.ipc.display_lists,
             ],
             debug_renderer,
             true,
             &mut self.draw_state
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -27,16 +27,17 @@ use clip_scroll_tree::{SpatialNodeIndex,
 #[cfg(feature = "debugger")]
 use debug_server;
 use frame_builder::{FrameBuilder, FrameBuilderConfig};
 use gpu_cache::GpuCache;
 use hit_test::{HitTest, HitTester};
 use internal_types::{DebugOutput, FastHashMap, FastHashSet, RenderedDocument, ResultMsg};
 use prim_store::{PrimitiveDataStore, PrimitiveScratchBuffer, PrimitiveInstance};
 use prim_store::{PrimitiveInstanceKind, PrimTemplateCommonData};
+use prim_store::gradient::{LinearGradientDataStore, RadialGradientDataStore};
 use prim_store::text_run::TextRunDataStore;
 use profiler::{BackendProfileCounters, IpcProfileCounters, ResourceProfileCounters};
 use record::ApiRecordingReceiver;
 use renderer::{AsyncPropertySampler, PipelineInfo};
 use resource_cache::ResourceCache;
 #[cfg(feature = "replay")]
 use resource_cache::PlainCacheOwn;
 #[cfg(any(feature = "capture", feature = "replay"))]
@@ -198,38 +199,46 @@ impl FrameStamp {
 pub struct FrameResources {
     /// The store of currently active / available clip nodes. This is kept
     /// in sync with the clip interner in the scene builder for each document.
     pub clip_data_store: ClipDataStore,
 
     /// Currently active / available primitives. Kept in sync with the
     /// primitive interner in the scene builder, per document.
     pub prim_data_store: PrimitiveDataStore,
+    pub linear_grad_data_store: LinearGradientDataStore,
+    pub radial_grad_data_store: RadialGradientDataStore,
     pub text_run_data_store: TextRunDataStore,
 }
 
 impl FrameResources {
     pub fn as_common_data(
         &self,
         prim_inst: &PrimitiveInstance
     ) -> &PrimTemplateCommonData {
         match prim_inst.kind {
             PrimitiveInstanceKind::Picture { data_handle, .. } |
             PrimitiveInstanceKind::LineDecoration { data_handle, .. } |
             PrimitiveInstanceKind::NormalBorder { data_handle, .. } |
             PrimitiveInstanceKind::ImageBorder { data_handle, .. } |
             PrimitiveInstanceKind::Rectangle { data_handle, .. } |
             PrimitiveInstanceKind::YuvImage { data_handle, .. } |
             PrimitiveInstanceKind::Image { data_handle, .. } |
-            PrimitiveInstanceKind::LinearGradient { data_handle, .. } |
-            PrimitiveInstanceKind::RadialGradient { data_handle, .. } |
             PrimitiveInstanceKind::Clear { data_handle, .. } => {
                 let prim_data = &self.prim_data_store[data_handle];
                 &prim_data.common
             }
+            PrimitiveInstanceKind::LinearGradient { data_handle, .. } => {
+                let prim_data = &self.linear_grad_data_store[data_handle];
+                &prim_data.common
+            }
+            PrimitiveInstanceKind::RadialGradient { data_handle, .. } =>{
+                let prim_data = &self.radial_grad_data_store[data_handle];
+                &prim_data.common
+            }
             PrimitiveInstanceKind::TextRun { data_handle, .. }  => {
                 let prim_data = &self.text_run_data_store[data_handle];
                 &prim_data.common
             }
         }
     }
 }
 
@@ -1198,19 +1207,36 @@ impl RenderBackend {
         }
 
         let doc = self.documents.get_mut(&document_id).unwrap();
         doc.has_built_scene |= has_built_scene;
 
         // If there are any additions or removals of clip modes
         // during the scene build, apply them to the data store now.
         if let Some(updates) = doc_resource_updates {
-            doc.resources.clip_data_store.apply_updates(updates.clip_updates);
-            doc.resources.prim_data_store.apply_updates(updates.prim_updates);
-            doc.resources.text_run_data_store.apply_updates(updates.text_run_updates);
+            doc.resources.clip_data_store.apply_updates(
+                updates.clip_updates,
+                &mut profile_counters.intern.clips,
+            );
+            doc.resources.prim_data_store.apply_updates(
+                updates.prim_updates,
+                &mut profile_counters.intern.prims,
+            );
+            doc.resources.linear_grad_data_store.apply_updates(
+                updates.linear_grad_updates,
+                &mut profile_counters.intern.linear_gradients,
+            );
+            doc.resources.radial_grad_data_store.apply_updates(
+                updates.radial_grad_updates,
+                &mut profile_counters.intern.radial_gradients,
+            );
+            doc.resources.text_run_data_store.apply_updates(
+                updates.text_run_updates,
+                &mut profile_counters.intern.text_runs,
+            );
         }
 
         // TODO: this scroll variable doesn't necessarily mean we scrolled. It is only used
         // for something wrench specific and we should remove it.
         let mut scroll = false;
         for frame_msg in frame_ops {
             let _timer = profile_counters.total_time.timer();
             let op = doc.process_frame_msg(frame_msg);
--- a/gfx/wr/webrender/src/scene_builder.rs
+++ b/gfx/wr/webrender/src/scene_builder.rs
@@ -11,31 +11,37 @@ use capture::CaptureConfig;
 use frame_builder::{FrameBuilderConfig, FrameBuilder};
 use clip::{ClipDataInterner, ClipDataUpdateList};
 use clip_scroll_tree::ClipScrollTree;
 use display_list_flattener::DisplayListFlattener;
 use intern::{Internable, Interner};
 use internal_types::{FastHashMap, FastHashSet};
 use prim_store::{PrimitiveDataInterner, PrimitiveDataUpdateList, PrimitiveKeyKind};
 use prim_store::PrimitiveStoreStats;
+use prim_store::gradient::{
+    LinearGradient, LinearGradientDataInterner, LinearGradientDataUpdateList,
+    RadialGradient, RadialGradientDataInterner, RadialGradientDataUpdateList
+};
 use prim_store::text_run::{TextRunDataInterner, TextRun, TextRunDataUpdateList};
 use resource_cache::FontInstanceMap;
 use render_backend::DocumentView;
 use renderer::{PipelineInfo, SceneBuilderHooks};
 use scene::Scene;
 use std::sync::mpsc::{channel, Receiver, Sender};
 use std::mem::replace;
 use time::precise_time_ns;
 use util::drain_filter;
 use std::thread;
 use std::time::Duration;
 
 pub struct DocumentResourceUpdates {
     pub clip_updates: ClipDataUpdateList,
     pub prim_updates: PrimitiveDataUpdateList,
+    pub linear_grad_updates: LinearGradientDataUpdateList,
+    pub radial_grad_updates: RadialGradientDataUpdateList,
     pub text_run_updates: TextRunDataUpdateList,
 }
 
 /// Represents the work associated to a transaction before scene building.
 pub struct Transaction {
     pub document_id: DocumentId,
     pub display_list_updates: Vec<DisplayListUpdate>,
     pub removed_pipelines: Vec<PipelineId>,
@@ -162,31 +168,45 @@ pub enum SceneSwapResult {
 // - Comparison of primitives and pictures between two
 //   display lists is (a) fast (b) done during scene building.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Default)]
 pub struct DocumentResources {
     pub clip_interner: ClipDataInterner,
     pub prim_interner: PrimitiveDataInterner,
+    pub linear_grad_interner: LinearGradientDataInterner,
+    pub radial_grad_interner: RadialGradientDataInterner,
     pub text_run_interner: TextRunDataInterner,
 }
 
 // Access to `DocumentResources` interners by `Internable`
 pub trait InternerMut<I: Internable>
 {
     fn interner_mut(&mut self) -> &mut Interner<I::Source, I::InternData, I::Marker>;
 }
 
 impl InternerMut<PrimitiveKeyKind> for DocumentResources {
     fn interner_mut(&mut self) -> &mut PrimitiveDataInterner {
         &mut self.prim_interner
     }
 }
 
+impl InternerMut<LinearGradient> for DocumentResources {
+    fn interner_mut(&mut self) -> &mut LinearGradientDataInterner {
+        &mut self.linear_grad_interner
+    }
+}
+
+impl InternerMut<RadialGradient> for DocumentResources {
+    fn interner_mut(&mut self) -> &mut RadialGradientDataInterner {
+        &mut self.radial_grad_interner
+    }
+}
+
 impl InternerMut<TextRun> for DocumentResources {
     fn interner_mut(&mut self) -> &mut TextRunDataInterner {
         &mut self.text_run_interner
     }
 }
 
 
 // A document in the scene builder contains the current scene,
@@ -342,35 +362,49 @@ impl SceneBuilder {
                     &item.view,
                     &item.output_pipelines,
                     &self.config,
                     &mut new_scene,
                     &mut item.doc_resources,
                     &PrimitiveStoreStats::empty(),
                 );
 
+                // TODO(djg): Can we do better than this?  Use a #[derive] to
+                // write the code for us, or unify updates into one enum/list?
                 let clip_updates = item
                     .doc_resources
                     .clip_interner
                     .end_frame_and_get_pending_updates();
 
                 let prim_updates = item
                     .doc_resources
                     .prim_interner
                     .end_frame_and_get_pending_updates();
 
+                let linear_grad_updates = item
+                    .doc_resources
+                    .linear_grad_interner
+                    .end_frame_and_get_pending_updates();
+
+                let radial_grad_updates = item
+                    .doc_resources
+                    .radial_grad_interner
+                    .end_frame_and_get_pending_updates();
+
                 let text_run_updates = item
                     .doc_resources
                     .text_run_interner
                     .end_frame_and_get_pending_updates();
 
                 doc_resource_updates = Some(
                     DocumentResourceUpdates {
                         clip_updates,
                         prim_updates,
+                        linear_grad_updates,
+                        radial_grad_updates,
                         text_run_updates,
                     }
                 );
 
                 built_scene = Some(BuiltScene {
                     scene: new_scene,
                     frame_builder,
                     clip_scroll_tree,
@@ -470,25 +504,37 @@ impl SceneBuilder {
                     .clip_interner
                     .end_frame_and_get_pending_updates();
 
                 let prim_updates = doc
                     .resources
                     .prim_interner
                     .end_frame_and_get_pending_updates();
 
+                let linear_grad_updates = doc
+                    .resources
+                    .linear_grad_interner
+                    .end_frame_and_get_pending_updates();
+
+                let radial_grad_updates = doc
+                    .resources
+                    .radial_grad_interner
+                    .end_frame_and_get_pending_updates();
+
                 let text_run_updates = doc
                     .resources
                     .text_run_interner
                     .end_frame_and_get_pending_updates();
 
                 doc_resource_updates = Some(
                     DocumentResourceUpdates {
                         clip_updates,
                         prim_updates,
+                        linear_grad_updates,
+                        radial_grad_updates,
                         text_run_updates,
                     }
                 );
 
                 built_scene = Some(BuiltScene {
                     scene: new_scene,
                     frame_builder,
                     clip_scroll_tree,
index f10110eae371b4480aacd3fd25b453fc569aa64d..65adb6f8faf6f3aa7613ee56e83c91cc120a658f
GIT binary patch
literal 16127
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
Hj;H+}msaaW
index 8096b4e8e531f8f6652844fd21f6d3b80f0691a4..6e242ffd10f1482724238ad61bad3793a28d1399
GIT binary patch
literal 86416
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
XzG!XO0=s(`@MolNb|vq!UC4g`$Q}R_
--- a/gfx/wr/wrench/reftests/boxshadow/reftest.list
+++ b/gfx/wr/wrench/reftests/boxshadow/reftest.list
@@ -18,17 +18,17 @@ platform(linux,mac) == box-shadow-border
 == box-shadow-spread.yaml box-shadow-spread.png
 == box-shadow-spread-radii.yaml box-shadow-spread-radii-ref.yaml
 == invalid.yaml invalid-ref.yaml
 == inset-empty.yaml blank.yaml
 platform(linux,mac) == inset-subpx.yaml inset-subpx.png
 platform(linux,mac) fuzzy(1,4) == inset-downscale.yaml inset-downscale.png
 platform(linux,mac) fuzzy(1,50) == box-shadow-cache.yaml box-shadow-cache.png
 platform(linux,mac) fuzzy(1,685) == overlap1.yaml overlap1.png
-== overlap2.yaml overlap2.png
+fuzzy(1,61) == overlap2.yaml overlap2.png
 platform(linux,mac) fuzzy(1,48) == no-stretch.yaml no-stretch.png
 platform(linux,mac) fuzzy(1,9) == box-shadow-stretch-mode-x.yaml box-shadow-stretch-mode-x.png
 platform(linux,mac) fuzzy(1,41) == box-shadow-stretch-mode-y.yaml box-shadow-stretch-mode-y.png
 platform(linux,mac) fuzzy(1,14) == inset-mask-region.yaml inset-mask-region.png
 == box-shadow-blurred-overlapping-radii.yaml box-shadow-blurred-overlapping-radii-ref.yaml
 
 platform(linux,mac) == box-shadow-huge-radius.yaml box-shadow-huge-radius.png
 
--- a/gfx/wr/wrench/reftests/filters/reftest.list
+++ b/gfx/wr/wrench/reftests/filters/reftest.list
@@ -24,17 +24,17 @@ platform(linux,mac) fuzzy(1,133) == filt
 == filter-saturate-blue-1.yaml filter-saturate-blue-1-ref.yaml
 == filter-saturate-blue-2.yaml filter-saturate-blue-2-ref.yaml
 == filter-saturate-blue-3.yaml filter-saturate-blue-3-ref.yaml
 == filter-saturate-red-alpha-1.yaml filter-saturate-red-alpha-1-ref.yaml
 == filter-saturate-green-alpha-1.yaml filter-saturate-green-alpha-1-ref.yaml
 == filter-saturate-blue-alpha-1.yaml filter-saturate-blue-alpha-1-ref.yaml
 == filter-hue-rotate-1.yaml filter-hue-rotate-1-ref.yaml
 == filter-hue-rotate-alpha-1.yaml filter-hue-rotate-alpha-1-ref.yaml
-== filter-long-chain.yaml filter-long-chain.png
+fuzzy(1,14) == filter-long-chain.yaml filter-long-chain.png
 platform(linux,mac) == filter-drop-shadow.yaml filter-drop-shadow.png
 platform(linux,mac) == filter-drop-shadow-on-viewport-edge.yaml filter-drop-shadow-on-viewport-edge.png
 platform(linux,mac) == blend-clipped.yaml blend-clipped.png
 == filter-segments.yaml filter-segments-ref.yaml
 == iframe-dropshadow.yaml iframe-dropshadow-ref.yaml
 == filter-mix-blend-mode.yaml filter-mix-blend-mode-ref.yaml
 == fuzzy(3,20000) srgb-to-linear.yaml srgb-to-linear-ref.yaml
 != srgb-to-linear-2.yaml srgb-to-linear-ref.yaml
--- a/gfx/wr/wrench/reftests/text/reftest.list
+++ b/gfx/wr/wrench/reftests/text/reftest.list
@@ -9,17 +9,17 @@
 == shadow.yaml shadow-ref.yaml
 == shadow-huge.yaml shadow-huge-ref.yaml
 != shadow-cover-1.yaml shadow-cover-2.yaml
 != shadow-many.yaml shadow.yaml
 != shadow-complex.yaml shadow-many.yaml
 != shadow-clipped-text.yaml blank.yaml
 != non-opaque.yaml non-opaque-notref.yaml
 == decorations.yaml decorations-ref.yaml
-fuzzy(1,100) == decorations-suite.yaml decorations-suite.png
+fuzzy(1,173) == decorations-suite.yaml decorations-suite.png
 == 1658.yaml 1658-ref.yaml
 == split-batch.yaml split-batch-ref.yaml
 == shadow-red.yaml shadow-red-ref.yaml
 fuzzy(1,735) == shadow-grey.yaml shadow-grey-ref.yaml
 fuzzy(1,663) == shadow-grey-transparent.yaml shadow-grey-ref.yaml
 == subtle-shadow.yaml subtle-shadow-ref.yaml
 == shadow-atomic.yaml shadow-atomic-ref.yaml
 == shadow-clip-rect.yaml shadow-atomic-ref.yaml
@@ -49,17 +49,17 @@ platform(linux) == subpixel-rotate.yaml 
 platform(linux) == subpixel-scale.yaml subpixel-scale.png
 platform(linux) == subpixel-skew.yaml subpixel-skew.png
 != shadow-rotate.yaml blank.yaml
 platform(linux) == embedded-bitmaps.yaml embedded-bitmaps.png
 platform(linux) == clipped-transform.yaml clipped-transform.png
 platform(mac) == color-bitmap-shadow.yaml color-bitmap-shadow-ref.yaml
 platform(linux) == writing-modes.yaml writing-modes-ref.yaml
 platform(linux) == blurred-shadow-local-clip-rect.yaml blurred-shadow-local-clip-rect-ref.png
-platform(linux) == two-shadows.yaml two-shadows.png
+fuzzy(1,1) platform(linux) == two-shadows.yaml two-shadows.png
 == shadow-clip.yaml shadow-clip-ref.yaml
 == shadow-fast-clip.yaml shadow-fast-clip-ref.yaml
 == shadow-partial-glyph.yaml shadow-partial-glyph-ref.yaml
 fuzzy(1,107) platform(linux) == shadow-transforms.yaml shadow-transforms.png
 fuzzy(1,113) platform(linux) == raster-space.yaml raster-space.png
 != allow-subpixel.yaml allow-subpixel-ref.yaml
 == bg-color.yaml bg-color-ref.yaml
 != large-glyphs.yaml blank.yaml
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -13,71 +13,102 @@
 #include "jit/JSJitFrameIter.h"
 #include "vm/Stack.h"
 
 namespace js {
 namespace jit {
 
 // [SMDOC] IonMonkey Bailouts
 //
-// A "bailout" is a condition in which we need to recover a baseline frame from
-// an IonFrame. Bailouts can happen for the following reasons:
-//   (1) A deoptimization guard, for example, an add overflows or a type check
-//       fails.
-//   (2) A check or assumption held by the JIT is invalidated by the VM, and
-//       JIT code must be thrown away. This includes the GC possibly deciding
-//       to evict live JIT code, or a Type Inference reflow.
+// A "bailout" is the process of recovering a baseline frame from an IonFrame.
+// Bailouts are implemented in js::jit::BailoutIonToBaseline, which has the
+// following callers:
+//
+// *   js::jit::Bailout - This is used when a guard fails in the Ion code
+//     itself; for example, an LGuardShape fails or an LAddI overflows. See
+//     callers of CodeGenerator::bailoutFrom() for more examples.
+//
+// *   js::jit::ExceptionHandlerBailout - Something called from Ion code
+//     failed. Ion doesn't implement `catch`; it handles all exceptions by
+//     bailing out.
 //
-// Note that bailouts as described here do not include normal Ion frame
-// inspection, for example, if an exception must be built or the GC needs to
-// scan an Ion frame for gcthings.
+// *   js::jit::InvalidationBailout - We returned to Ion code that was
+//     invalidated while it was on the stack. See "OSI" below. Ion code can be
+//     invalidated for several reasons: when GC evicts Ion code to save memory,
+//     for example, or when assumptions baked into the jitted code are
+//     invalidated by the VM (see callers of IonBuilder::constraints()).
+//
+// (Some stack inspection can be done without bailing out, including GC stack
+// marking, Error object construction, and Gecko profiler sampling.)
 //
-// The second type of bailout needs a different name - "deoptimization" or
-// "deep bailout". Here we are concerned with eager (or maybe "shallow")
-// bailouts, that happen from JIT code. These happen from guards, like:
+// Consider the first case. When an Ion guard fails, we can't continue in
+// Ion. There's no IC fallback case coming to save us; we've got a broken
+// assumption baked into the code we're running. So we jump to an out-of-line
+// code path that's responsible for abandoning Ion execution and resuming in
+// baseline: the bailout path.
 //
-//  cmp [obj + shape], 0x50M37TH1NG
-//  jmp _bailout
+// We were in the midst of optimized Ion code, so bits of program state may be
+// in registers or spilled to the native stack; values may be unboxed; some
+// objects may have been optimized away; thanks to inlining, whole call frames
+// may be missing. The bailout path must put all these pieces back together
+// into the structure the baseline code expects.
 //
-// The bailout target needs to somehow translate the Ion frame (whose state
-// will differ at each program point) to a baseline frame. This state is
-// captured into the IonScript's snapshot buffer, and for each bailout we know
-// which snapshot corresponds to its state.
+// The data structure that makes this possible is called a *snapshot*.
+// Snapshots are created during Ion codegen and associated with the IonScript;
+// they tell how to recover each value in a BaselineFrame from the current
+// machine state at a given point in the Ion JIT code. This is potentially
+// different at every place in an Ion script where we might bail out. (See
+// Snapshots.h.)
+//
+// The bailout path performs roughly the following steps:
+//
+// 1.  Push a snapshot index and the frame size to the native stack.
+// 2.  Spill all registers.
+// 3.  Call js::jit::Bailout to reconstruct the baseline frame(s).
+// 4.  memmove() those to the right place on the native stack.
+// 5.  Jump to baseline code.
+//
+// (This last step requires baseline JIT code to have an entry point at each pc
+// where an eventual Ion guard may be inserted.)
 //
-// Roughly, the following needs to happen at the bailout target.
-//   (1) Move snapshot ID into a known stack location (registers cannot be
-//       mutated).
-//   (2) Spill all registers to the stack.
-//   (3) Call a Bailout() routine, whose argument is the stack pointer.
-//   (4) Bailout() will find the IonScript on the stack, use the snapshot ID
-//       to find the structure of the frame, and then use the stack and spilled
-//       registers to perform frame conversion.
-//   (5) Bailout() returns, and the JIT must immediately return to the
-//       baseline JIT code (all frames are converted at once).
+// When C++ code invalidates Ion code, we do on-stack invalidation, or OSI, to
+// arrange for every affected Ion frame on the stack to bail out as soon as
+// control returns to it. OSI patches every instruction in the JIT code that's
+// at a return address currently on the stack. See InvalidateActivation.
+//
+//
+// ## Bailout path implementation details
+//
+// Ion code has a lot of guards, so each bailout path must be small. Steps 2
+// and 3 above are therefore implemented by a shared per-Runtime trampoline,
+// rt->jitRuntime()->getGenericBailoutHandler().
 //
-// (2) and (3) are implemented by a trampoline held in the compartment.
-// Naively, we could implement (1) like:
+// Naively, we could implement step 1 like:
 //
-//   _bailout_ID_1:
-//     push 1
-//     jmp _global_bailout_handler
-//   _bailout_ID_2:
-//     push 2
-//     jmp _global_bailout_handler
+//     _bailout_ID_1:
+//       push 1
+//       jmp _deopt
+//     _bailout_ID_2:
+//       push 2
+//       jmp _deopt
+//     ...
+//     _deopt:
+//       push imm(FrameSize)
+//       call _global_bailout_handler
 //
 // This takes about 10 extra bytes per guard. On some platforms, we can reduce
 // this overhead to 4 bytes by creating a global jump table, shared again in
 // the compartment:
 //
-//     call _global_bailout_handler
-//     call _global_bailout_handler
-//     call _global_bailout_handler
-//     call _global_bailout_handler
-//      ...
-//    _global_bailout_handler:
+//       call _global_bailout_handler
+//       call _global_bailout_handler
+//       call _global_bailout_handler
+//       call _global_bailout_handler
+//       ...
+//     _global_bailout_handler:
 //
 // In the bailout handler, we can recompute which entry in the table was
 // selected by subtracting the return addressed pushed by the call, from the
 // start of the table, and then dividing by the size of a (call X) entry in the
 // table. This gives us a number in [0, TableSize), which we call a
 // "BailoutId".
 //
 // Then, we can provide a per-script mapping from BailoutIds to snapshots,
--- a/layout/reftests/css-break/reftest.list
+++ b/layout/reftests/css-break/reftest.list
@@ -1,12 +1,12 @@
 default-preferences pref(layout.css.box-decoration-break.enabled,true)
 
 == box-decoration-break-1.html box-decoration-break-1-ref.html
-fuzzy(0-1,0-20) fuzzy-if(skiaContent,0-1,0-700) fuzzy-if(webrender,21-26,8908-12357) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html
+fuzzy(0-1,0-20) fuzzy-if(skiaContent,0-1,0-700) fuzzy-if(webrender,21-26,8908-12681) == box-decoration-break-with-inset-box-shadow-1.html box-decoration-break-with-inset-box-shadow-1-ref.html
 skip-if(verify) fuzzy(0-45,0-460) fuzzy-if(skiaContent,0-57,0-439) fuzzy-if(Android,0-57,0-1330) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-with-outset-box-shadow-1.html box-decoration-break-with-outset-box-shadow-1-ref.html # Bug 1386543, bug 1392106
 random-if(!gtkWidget) == box-decoration-break-border-image.html box-decoration-break-border-image-ref.html
 == box-decoration-break-block-border-padding.html box-decoration-break-block-border-padding-ref.html
 == box-decoration-break-block-margin.html box-decoration-break-block-margin-ref.html
 fuzzy-if(!Android,0-1,0-62) fuzzy-if(Android,0-8,0-6627) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-first-letter.html box-decoration-break-first-letter-ref.html #Bug 1313773 # Bug 1392106
 == box-decoration-break-with-bidi.html box-decoration-break-with-bidi-ref.html
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-bug-1235152.html box-decoration-break-bug-1235152-ref.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == box-decoration-break-bug-1249913.html box-decoration-break-bug-1249913-ref.html # Bug 1392106
--- a/layout/reftests/svg/filters/css-filter-chains/reftest.list
+++ b/layout/reftests/svg/filters/css-filter-chains/reftest.list
@@ -1,7 +1,7 @@
 # These tests verify that CSS filter chains behave properly.
 # e.g. filter: blur(3px) grayscale(0.5) invert(0.2);
 
 # Some platforms render this complex filter chain a little differently, and that's ok.
-fuzzy(0-5,0-13638) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&layersGPUAccelerated,0-35,0-13638) fuzzy-if(webrender,4-6,12004-18853) == long-chain.html long-chain-ref.html # Win10: Bug 1258241
+fuzzy(0-5,0-13638) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&layersGPUAccelerated,0-35,0-13638) fuzzy-if(webrender,4-6,12000-18853) == long-chain.html long-chain-ref.html # Win10: Bug 1258241
 == moz-element.html moz-element-ref.html
 == same-filter.html same-filter-ref.html
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
@@ -33,33 +33,37 @@ const UINT DWM_EC_DISABLECOMPOSITION = 0
 const UINT DWM_EC_ENABLECOMPOSITION = 1;
 
 const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
 
 }  // namespace
 
 ScreenCapturerWinGdi::ScreenCapturerWinGdi(
     const DesktopCaptureOptions& options) {
-  // Load dwmapi.dll dynamically since it is not available on XP.
-  if (!dwmapi_library_)
-    dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);
+  if (options.disable_effects()) {
+    // Load dwmapi.dll dynamically since it is not available on XP.
+    if (!dwmapi_library_)
+      dwmapi_library_ = LoadLibrary(kDwmapiLibraryName);
 
-  if (dwmapi_library_) {
-    composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
-      GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
-    composition_enabled_func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>
-      (GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
+    if (dwmapi_library_) {
+      composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
+        GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
+      composition_enabled_func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>
+        (GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
+    }
   }
-
-  disable_composition_ = options.disable_effects();
 }
 
 ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
   Stop();
 
+  // Restore Aero.
+  if (composition_func_)
+    (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
+
   if (dwmapi_library_)
     FreeLibrary(dwmapi_library_);
 }
 
 void ScreenCapturerWinGdi::SetSharedMemoryFactory(
     std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
   shared_memory_factory_ = std::move(shared_memory_factory);
 }
@@ -108,37 +112,33 @@ void ScreenCapturerWinGdi::Start(Callbac
   RTC_DCHECK(!callback_);
   RTC_DCHECK(callback);
 
   callback_ = callback;
 
   // Vote to disable Aero composited desktop effects while capturing. Windows
   // will restore Aero automatically if the process exits. This has no effect
   // under Windows 8 or higher.  See crbug.com/124018.
-  if (disable_composition_) {
-    if (composition_func_)
-      (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
-  }
+  if (composition_func_)
+    (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
 }
 
 void ScreenCapturerWinGdi::Stop() {
   if (desktop_dc_) {
     ReleaseDC(NULL, desktop_dc_);
     desktop_dc_ = NULL;
   }
   if (memory_dc_) {
     DeleteDC(memory_dc_);
     memory_dc_ = NULL;
   }
 
-  if (disable_composition_) {
-    // Restore Aero.
-    if (composition_func_)
-      (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
-  }
+  // Restore Aero.
+  if (composition_func_)
+    (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
   callback_ = NULL;
 }
 
 void ScreenCapturerWinGdi::PrepareCaptureResources() {
   // Switch to the desktop receiving user input if different from the current
   // one.
   std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
   if (input_desktop && !desktop_.IsSame(*input_desktop)) {
--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
+++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
@@ -73,16 +73,14 @@ class ScreenCapturerWinGdi : public Desk
   ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
 
   DisplayConfigurationMonitor display_configuration_monitor_;
 
   HMODULE dwmapi_library_ = NULL;
   DwmEnableCompositionFunc composition_func_ = nullptr;
   DwmIsCompositionEnabledFunc composition_enabled_func_;
 
-  bool disable_composition_;
-
   RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerWinGdi);
 };
 
 }  // namespace webrtc
 
 #endif  // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
new file mode 100644
--- /dev/null
+++ b/mfbt/Buffer.h
@@ -0,0 +1,167 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_Buffer_h
+#define mozilla_Buffer_h
+
+#include <algorithm>
+#include "mozilla/Maybe.h"
+#include "mozilla/Span.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
+
+namespace mozilla {
+
+/**
+ * A move-only type that wraps a mozilla::UniquePtr<T[]> and the length of
+ * the T[].
+ *
+ * Unlike mozilla::Array, the length is a run-time property.
+ * Unlike mozilla::Vector and nsTArray, does not have capacity and
+ * assocatiated growth functionality.
+ * Unlike mozilla::Span, mozilla::Buffer owns the allocation it points to.
+ */
+template <typename T>
+class Buffer final {
+ private:
+  mozilla::UniquePtr<T[]> mData;
+  size_t mLength;
+
+ public:
+  Buffer(const Buffer<T>& aOther) = delete;
+  Buffer<T>& operator=(const Buffer<T>& aOther) = delete;
+
+  /**
+   * Construct zero-lenth Buffer (without actually pointing to a heap
+   * allocation).
+   */
+  Buffer() : mData(nullptr), mLength(0){};
+
+  /**
+   * Construct from raw parts.
+   *
+   * aLength must not be greater than the actual length of the buffer pointed
+   * to by aData.
+   */
+  Buffer(mozilla::UniquePtr<T[]>&& aData, size_t aLength)
+      : mData(std::move(aData)), mLength(aLength) {}
+
+  /**
+   * Move constructor. Sets the moved-from Buffer to zero-length
+   * state.
+   */
+  Buffer(Buffer<T>&& aOther)
+      : mData(std::move(aOther.mData)), mLength(aOther.mLength) {
+    aOther.mLength = 0;
+  }
+
+  /**
+   * Construct by copying the elements of a Span.
+   *
+   * Allocates the internal buffer infallibly. Use CopyFrom for fallible
+   * allocation.
+   */
+  explicit Buffer(mozilla::Span<const T> aSpan)
+      : mData(mozilla::MakeUnique<T[]>(aSpan.Length())),
+        mLength(aSpan.Length()) {
+    std::copy(aSpan.cbegin(), aSpan.cend(), mData.get());
+  }
+
+  /**
+   * Create a new Buffer by copying the elements of a Span.
+   *
+   * Allocates the internal buffer fallibly.
+   */
+  static mozilla::Maybe<Buffer<T>> CopyFrom(mozilla::Span<const T> aSpan) {
+    auto data = mozilla::MakeUniqueFallible<T[]>(aSpan.Length());
+    if (!data) {
+      return mozilla::Nothing();
+    }
+    std::copy(aSpan.cbegin(), aSpan.cend(), data.get());
+    return mozilla::Some(Buffer(std::move(data), aSpan.Length()));
+  }
+
+  /**
+   * Construct a buffer of requested length.
+   *
+   * The contents will be initialized or uninitialized according
+   * to the behavior of mozilla::MakeUnique<T[]>(aLength) for T.
+   *
+   * Allocates the internal buffer infallibly. Use Alloc for fallible
+   * allocation.
+   */
+  explicit Buffer(size_t aLength)
+      : mData(mozilla::MakeUnique<T[]>(aLength)), mLength(aLength) {}
+
+  /**
+   * Create a new Buffer with an internal buffer of requested length.
+   *
+   * The contents will be initialized or uninitialized according to the
+   * behavior of mozilla::MakeUnique<T[]>(aLength) for T.
+   *
+   * Allocates the internal buffer fallibly.
+   */
+  static mozilla::Maybe<Buffer<T>> Alloc(size_t aLength) {
+    auto data = mozilla::MakeUniqueFallible<T[]>(aLength);
+    if (!data) {
+      return mozilla::Nothing();
+    }
+    return mozilla::Some(Buffer(std::move(data), aLength));
+  }
+
+  mozilla::Span<const T> AsSpan() const {
+    return mozilla::MakeSpan(mData.get(), mLength);
+  }
+  mozilla::Span<T> AsWritableSpan() {
+    return mozilla::MakeSpan(mData.get(), mLength);
+  }
+  operator mozilla::Span<const T>() const { return AsSpan(); }
+  operator mozilla::Span<T>() { return AsWritableSpan(); }
+
+  /**
+   * Guarantees a non-null and aligned pointer
+   * even for the zero-length case.
+   */
+  T* Elements() { return AsWritableSpan().Elements(); }
+  size_t Length() const { return mLength; }
+
+  T& operator[](size_t aIndex) {
+    MOZ_ASSERT(aIndex < mLength);
+    return mData.get()[aIndex];
+  }
+
+  const T& operator[](size_t aIndex) const {
+    MOZ_ASSERT(aIndex < mLength);
+    return mData.get()[aIndex];
+  }
+
+  typedef T* iterator;
+  typedef const T* const_iterator;
+  typedef ReverseIterator<T*> reverse_iterator;
+  typedef ReverseIterator<const T*> const_reverse_iterator;
+
+  // Methods for range-based for loops.
+  iterator begin() { return mData.get(); }
+  const_iterator begin() const { return mData.get(); }
+  const_iterator cbegin() const { return begin(); }
+  iterator end() { return mData.get() + mLength; }
+  const_iterator end() const { return mData.get() + mLength; }
+  const_iterator cend() const { return end(); }
+
+  // Methods for reverse iterating.
+  reverse_iterator rbegin() { return reverse_iterator(end()); }
+  const_reverse_iterator rbegin() const {
+    return const_reverse_iterator(end());
+  }
+  const_reverse_iterator crbegin() const { return rbegin(); }
+  reverse_iterator rend() { return reverse_iterator(begin()); }
+  const_reverse_iterator rend() const {
+    return const_reverse_iterator(begin());
+  }
+  const_reverse_iterator crend() const { return rend(); }
+};
+
+} /* namespace mozilla */
+
+#endif /* mozilla_Buffer_h */
--- a/mfbt/moz.build
+++ b/mfbt/moz.build
@@ -18,16 +18,17 @@ EXPORTS.mozilla = [
     'AlreadyAddRefed.h',
     'Array.h',
     'ArrayUtils.h',
     'Assertions.h',
     'Atomics.h',
     'Attributes.h',
     'BinarySearch.h',
     'BloomFilter.h',
+    'Buffer.h',
     'BufferList.h',
     'Casting.h',
     'ChaosMode.h',
     'Char16.h',
     'CheckedInt.h',
     'Compiler.h',
     'Compression.h',
     'DebugOnly.h',
new file mode 100644
--- /dev/null
+++ b/mfbt/tests/gtest/TestBuffer.cpp
@@ -0,0 +1,95 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "mozilla/Buffer.h"
+#include "mozilla/Array.h"
+
+using namespace mozilla;
+
+TEST(Buffer, TestBufferInfallible)
+{
+	const size_t LEN = 8;
+	Array<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8};
+	Buffer<int32_t> buf(arr);
+
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(buf[i], arr[i]);
+	}
+
+	auto iter = buf.begin();
+	auto end = buf.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*iter, arr[i]);
+		iter++;
+	}
+	ASSERT_EQ(iter, end);
+
+	Span<int32_t> span = buf;
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(span[i], arr[i]);
+	}
+
+	auto spanIter = span.begin();
+	auto spanEnd = span.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*spanIter, arr[i]);
+		spanIter++;
+	}
+	ASSERT_EQ(spanIter, spanEnd);
+
+	span[3] = 42;
+	ASSERT_EQ(buf[3], 42);
+
+	Buffer<int32_t> another(std::move(buf));
+	ASSERT_EQ(another[3], 42);
+	ASSERT_EQ(buf.Length(), 0U);
+}
+
+TEST(Buffer, TestBufferFallible)
+{
+	const size_t LEN = 8;
+	Array<int32_t, LEN> arr = {1, 2, 3, 4, 5, 6, 7, 8};
+	auto maybe = Buffer<int32_t>::CopyFrom(arr);
+	ASSERT_TRUE(maybe.isSome());
+	Buffer<int32_t> buf(std::move(*maybe));
+
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(buf[i], arr[i]);
+	}
+
+	auto iter = buf.begin();
+	auto end = buf.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*iter, arr[i]);
+		iter++;
+	}
+	ASSERT_EQ(iter, end);
+
+	Span<int32_t> span = buf;
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(span[i], arr[i]);
+	}
+
+	auto spanIter = span.begin();
+	auto spanEnd = span.end();
+	for (size_t i = 0; i < LEN; i++) {
+		ASSERT_EQ(*spanIter, arr[i]);
+		spanIter++;
+	}
+	ASSERT_EQ(spanIter, spanEnd);
+
+	span[3] = 42;
+	ASSERT_EQ(buf[3], 42);
+
+	Buffer<int32_t> another(std::move(buf));
+	ASSERT_EQ(another[3], 42);
+	ASSERT_EQ(buf.Length(), 0U);
+}
+
+TEST(Buffer, TestBufferElements)
+{
+	ASSERT_EQ(Buffer<int32_t>().Elements(), reinterpret_cast<int32_t*>(alignof(int32_t)));
+}
--- a/mfbt/tests/gtest/moz.build
+++ b/mfbt/tests/gtest/moz.build
@@ -1,15 +1,16 @@
 # -*- 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/.
 
 UNIFIED_SOURCES += [
+    'TestBuffer.cpp',
     'TestLinkedList.cpp',
     'TestSpan.cpp',
 ]
 
 #LOCAL_INCLUDES += [
 #    '../../base',
 #]
 
--- a/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/media/IMediaDrmBridge.aidl
+++ b/mobile/android/geckoview/src/main/aidl/org/mozilla/gecko/media/IMediaDrmBridge.aidl
@@ -17,9 +17,11 @@ interface IMediaDrmBridge {
 
     oneway void updateSession(int promiseId,
                               String sessionId,
                               in byte[] response);
 
     oneway void closeSession(int promiseId, String sessionId);
 
     oneway void release();
+
+    void setServerCertificate(in byte[] cert);
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrm.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrm.java
@@ -27,9 +27,10 @@ public interface GeckoMediaDrm {
     void createSession(int createSessionToken,
                        int promiseId,
                        String initDataType,
                        byte[] initData);
     void updateSession(int promiseId, String sessionId, byte[] response);
     void closeSession(int promiseId, String sessionId);
     void release();
     MediaCrypto getMediaCrypto();
+    void setServerCertificate(final byte[] cert);
 }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/GeckoMediaDrmBridgeV21.java
@@ -296,16 +296,26 @@ public class GeckoMediaDrmBridgeV21 impl
     }
 
     @Override
     public MediaCrypto getMediaCrypto() {
         if (DEBUG) Log.d(LOGTAG, "getMediaCrypto()");
         return mCrypto;
     }
 
+    @Override
+    public void setServerCertificate(final byte[] cert) {
+        if (DEBUG) Log.d(LOGTAG, "setServerCertificate()");
+        if (mDrm == null) {
+            throw new IllegalStateException("MediaDrm instance doesn't exist !!");
+        }
+        mDrm.setPropertyByteArray("serviceCertificate", cert);
+        return;
+    }
+
     protected void HandleKeyStatusChangeByDummyKey(String sessionId)
     {
         SessionKeyInfo[] keyInfos = new SessionKeyInfo[1];
         keyInfos[0] = new SessionKeyInfo(DUMMY_KEY_ID,
                                          MediaDrm.KeyStatus.STATUS_USABLE);
         onSessionBatchedKeyChanged(sessionId.getBytes(), keyInfos);
         if (DEBUG) Log.d(LOGTAG, "Key successfully added for session " + sessionId);
     }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/MediaDrmProxy.java
@@ -77,33 +77,16 @@ public final class MediaDrmProxy {
         }
         if (keySystem.equals(WIDEVINE_KEY_SYSTEM)) {
             return MediaDrm.isCryptoSchemeSupported(WIDEVINE_SCHEME_UUID, container);
         }
         if (DEBUG) Log.d(LOGTAG, "cannot decrypt key sytem = " + keySystem + ", container = " + container);
         return false;
     }
 
-    @WrapForJNI
-    public static boolean CanDecode(String mimeType) {
-        for (int i = 0; i < MediaCodecList.getCodecCount(); ++i) {
-            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
-            if (info.isEncoder()) {
-                continue;
-            }
-            for (String m : info.getSupportedTypes()) {
-                if (m.equals(mimeType)) {
-                  return true;
-                }
-            }
-        }
-        if (DEBUG) Log.d(LOGTAG, "cannot decode mimetype = " + mimeType);
-        return false;
-    }
-
      // Interface for callback to native.
     public interface Callbacks {
         void onSessionCreated(int createSessionToken,
                               int promiseId,
                               byte[] sessionId,
                               byte[] request);
 
         void onSessionUpdated(int promiseId, byte[] sessionId);
@@ -294,16 +277,26 @@ public final class MediaDrmProxy {
         mImpl.closeSession(promiseId, sessionId);
     }
 
     @WrapForJNI(calledFrom = "gecko")
     private String getStubId() {
         return mDrmStubId;
     }
 
+    @WrapForJNI
+    public boolean setServerCertificate(final byte[] cert) {
+        try {
+            mImpl.setServerCertificate(cert);
+            return true;
+        } catch (RuntimeException e) {
+            return false;
+        }
+    }
+
     // Get corresponding MediaCrypto object by a generated UUID for MediaCodec.
     // Will be called on MediaFormatReader's TaskQueue.
     @WrapForJNI
     public static MediaCrypto getMediaCrypto(String stubId) {
         for (MediaDrmProxy proxy : sProxyList) {
             if (proxy.getStubId().equals(stubId)) {
                 return proxy.getMediaCryptoFromBridge();
             }
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/RemoteMediaDrmBridge.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/RemoteMediaDrmBridge.java
@@ -145,9 +145,20 @@ final class RemoteMediaDrmBridge impleme
     }
 
     @Override
     public synchronized MediaCrypto getMediaCrypto() {
         if (DEBUG) Log.d(LOGTAG, "getMediaCrypto(), should not enter here!");
         assertTrue(false);
         return null;
     }
-}
\ No newline at end of file
+
+    @Override
+    public synchronized void setServerCertificate(final byte[] cert) {
+        try {
+            mRemote.setServerCertificate(cert);
+        } catch (Exception e) {
+            Log.e(LOGTAG, "Got exception while setting server certificate.", e);
+            throw new RuntimeException(e);
+        }
+    }
+}
+
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/RemoteMediaDrmBridgeStub.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/RemoteMediaDrmBridgeStub.java
@@ -238,9 +238,19 @@ final class RemoteMediaDrmBridgeStub ext
         if (mBridge != null) {
             mBridge.release();
             mBridge = null;
         }
         mCallbacks.asBinder().unlinkToDeath(this, 0);
         mCallbacks = null;
         mStubId = "";
     }
+
+    @Override
+    public synchronized void setServerCertificate(final byte[] cert) {
+        try {
+            mBridge.setServerCertificate(cert);
+        } catch (IllegalStateException e) {
+            Log.e(LOGTAG, "Failed to setServerCertificate.", e);
+            throw e;
+        }
+    }
 }
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -307,32 +307,27 @@ void nsHtml5StreamParser::SetViewSourceT
         mViewSourceTitle.AssignLiteral("\xE2\x80\xA6");
       }
     }
   }
 }
 
 nsresult
 nsHtml5StreamParser::SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-    const uint8_t* aFromSegment,  // can be null
-    uint32_t aCount, uint32_t* aWriteCount) {
+    Span<const uint8_t> aFromSegment) {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   nsresult rv = NS_OK;
   mUnicodeDecoder = mEncoding->NewDecoderWithBOMRemoval();
   if (mSniffingBuffer) {
-    uint32_t writeCount;
-    rv = WriteStreamBytes(mSniffingBuffer.get(), mSniffingLength, &writeCount);
+    rv = WriteStreamBytes(MakeSpan(mSniffingBuffer.get(), mSniffingLength));
     NS_ENSURE_SUCCESS(rv, rv);
     mSniffingBuffer = nullptr;
   }
   mMetaScanner = nullptr;
-  if (aFromSegment) {
-    rv = WriteStreamBytes(aFromSegment, aCount, aWriteCount);
-  }
-  return rv;
+  return WriteStreamBytes(aFromSegment);
 }
 
 nsresult nsHtml5StreamParser::SetupDecodingFromBom(
     NotNull<const Encoding*> aEncoding) {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   mEncoding = aEncoding;
   mUnicodeDecoder = mEncoding->NewDecoderWithoutBOMHandling();
   mCharsetSource = kCharsetFromByteOrderMark;
@@ -340,23 +335,23 @@ nsresult nsHtml5StreamParser::SetupDecod
   mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
   mSniffingBuffer = nullptr;
   mMetaScanner = nullptr;
   mBomState = BOM_SNIFFING_OVER;
   return NS_OK;
 }
 
 void nsHtml5StreamParser::SniffBOMlessUTF16BasicLatin(
-    const uint8_t* aFromSegment, uint32_t aCountToSniffingLimit) {
+    Span<const uint8_t> aFromSegment) {
   // Avoid underspecified heuristic craziness for XHR
   if (mMode == LOAD_AS_DATA) {
     return;
   }
   // Make sure there's enough data. Require room for "<title></title>"
-  if (mSniffingLength + aCountToSniffingLimit < 30) {
+  if (mSniffingLength + aFromSegment.Length() < 30) {
     return;
   }
   // even-numbered bytes tracked at 0, odd-numbered bytes tracked at 1
   bool byteZero[2] = {false, false};
   bool byteNonZero[2] = {false, false};
   uint32_t i = 0;
   if (mSniffingBuffer) {
     for (; i < mSniffingLength; ++i) {
@@ -368,29 +363,27 @@ void nsHtml5StreamParser::SniffBOMlessUT
       } else {
         if (byteZero[1 - (i % 2)]) {
           return;
         }
         byteZero[i % 2] = true;
       }
     }
   }
-  if (aFromSegment) {
-    for (uint32_t j = 0; j < aCountToSniffingLimit; ++j) {
-      if (aFromSegment[j]) {
-        if (byteNonZero[1 - ((i + j) % 2)]) {
-          return;
-        }
-        byteNonZero[(i + j) % 2] = true;
-      } else {
-        if (byteZero[1 - ((i + j) % 2)]) {
-          return;
-        }
-        byteZero[(i + j) % 2] = true;
+  for (size_t j = 0; j < aFromSegment.Length(); ++j) {
+    if (aFromSegment[j]) {
+      if (byteNonZero[1 - ((i + j) % 2)]) {
+        return;
       }
+      byteNonZero[(i + j) % 2] = true;
+    } else {
+      if (byteZero[1 - ((i + j) % 2)]) {
+        return;
+      }
+      byteZero[(i + j) % 2] = true;
     }
   }
 
   if (byteNonZero[0]) {
     mEncoding = UTF_16LE_ENCODING;
   } else {
     mEncoding = UTF_16BE_ENCODING;
   }
@@ -458,19 +451,19 @@ static void HandleComment(void* aUserDat
 
 static void HandleProcessingInstruction(void* aUserData,
                                         const XML_Char* aTarget,
                                         const XML_Char* aData) {
   UserData* ud = static_cast<UserData*>(aUserData);
   XML_StopParser(ud->mExpat, false);
 }
 
-nsresult nsHtml5StreamParser::FinalizeSniffing(
-    const uint8_t* aFromSegment,  // can be null
-    uint32_t aCount, uint32_t* aWriteCount, uint32_t aCountToSniffingLimit) {
+nsresult nsHtml5StreamParser::FinalizeSniffing(Span<const uint8_t> aFromSegment,
+                                               uint32_t aCountToSniffingLimit,
+                                               bool aEof) {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   NS_ASSERTION(mCharsetSource < kCharsetFromParentForced,
                "Should not finalize sniffing when using forced charset.");
   if (mMode == VIEW_SOURCE_XML) {
     static const XML_Memory_Handling_Suite memsuite = {
         (void* (*)(size_t))moz_xmalloc, (void* (*)(void*, size_t))moz_xrealloc,
         free};
 
@@ -506,70 +499,69 @@ nsresult nsHtml5StreamParser::FinalizeSn
     // first 1024 bytes of the file (or the file as a whole if the file is
     // 1024 bytes long or shorter). Thus, we parse both buffers, but if the
     // first call succeeds already, we skip parsing the second buffer.
     if (mSniffingBuffer) {
       status = XML_Parse(ud.mExpat,
                          reinterpret_cast<const char*>(mSniffingBuffer.get()),
                          mSniffingLength, false);
     }
-    if (status == XML_STATUS_OK && mCharsetSource < kCharsetFromMetaTag &&
-        aFromSegment) {
-      status = XML_Parse(ud.mExpat, reinterpret_cast<const char*>(aFromSegment),
-                         aCountToSniffingLimit, false);
+    if (status == XML_STATUS_OK && mCharsetSource < kCharsetFromMetaTag) {
+      mozilla::Unused << XML_Parse(
+          ud.mExpat, reinterpret_cast<const char*>(aFromSegment.Elements()),
+          aCountToSniffingLimit, false);
     }
     XML_ParserFree(ud.mExpat);
 
     if (mCharsetSource < kCharsetFromMetaTag) {
       // Failed to get an encoding from the XML declaration. XML defaults
       // confidently to UTF-8 in this case.
       // It is also possible that the document has an XML declaration that is
       // longer than 1024 bytes, but that case is not worth worrying about.
       mEncoding = UTF_8_ENCODING;
       mCharsetSource = kCharsetFromMetaTag;  // means confident
     }
 
-    return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-        aFromSegment, aCount, aWriteCount);
+    return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment);
   }
 
   // meta scan failed.
   if (mCharsetSource >= kCharsetFromHintPrevDoc) {
     mFeedChardet = false;
-    return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-        aFromSegment, aCount, aWriteCount);
+    return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment);
   }
   // Check for BOMless UTF-16 with Basic
   // Latin content for compat with IE. See bug 631751.
-  SniffBOMlessUTF16BasicLatin(aFromSegment, aCountToSniffingLimit);
+  SniffBOMlessUTF16BasicLatin(aFromSegment.To(aCountToSniffingLimit));
   // the charset may have been set now
   // maybe try chardet now;
   if (mFeedChardet) {
     bool dontFeed;
     nsresult rv;
     if (mSniffingBuffer) {
       rv = mChardet->DoIt((const char*)mSniffingBuffer.get(), mSniffingLength,
                           &dontFeed);
       mFeedChardet = !dontFeed;
       NS_ENSURE_SUCCESS(rv, rv);
     }
-    if (mFeedChardet && aFromSegment) {
-      rv = mChardet->DoIt((const char*)aFromSegment,
-                          // Avoid buffer boundary-dependent behavior when
-                          // reparsing is forbidden. If reparse is forbidden,
-                          // act as if we only saw the first 1024 bytes.
-                          // When reparsing isn't forbidden, buffer boundaries
-                          // can have an effect on whether the page is loaded
-                          // once or twice. :-(
-                          mReparseForbidden ? aCountToSniffingLimit : aCount,
-                          &dontFeed);
+    if (mFeedChardet && !aFromSegment.IsEmpty()) {
+      rv = mChardet->DoIt(
+          (const char*)aFromSegment.Elements(),
+          // Avoid buffer boundary-dependent behavior when
+          // reparsing is forbidden. If reparse is forbidden,
+          // act as if we only saw the first 1024 bytes.
+          // When reparsing isn't forbidden, buffer boundaries
+          // can have an effect on whether the page is loaded
+          // once or twice. :-(
+          mReparseForbidden ? aCountToSniffingLimit : aFromSegment.Length(),
+          &dontFeed);
       mFeedChardet = !dontFeed;
       NS_ENSURE_SUCCESS(rv, rv);
     }
-    if (mFeedChardet && (!aFromSegment || mReparseForbidden)) {
+    if (mFeedChardet && (aEof || mReparseForbidden)) {
       // mReparseForbidden is checked so that we get to use the sniffing
       // buffer with the best guess so far if we aren't allowed to guess
       // better later.
       mFeedChardet = false;
       rv = mChardet->Done();
       NS_ENSURE_SUCCESS(rv, rv);
     }
     // fall thru; callback may have changed charset
@@ -582,93 +574,75 @@ nsresult nsHtml5StreamParser::FinalizeSn
   } else if (mMode == LOAD_AS_DATA && mCharsetSource == kCharsetFromFallback) {
     NS_ASSERTION(mReparseForbidden, "Reparse should be forbidden for XHR");
     NS_ASSERTION(!mFeedChardet, "Should not feed chardet for XHR");
     NS_ASSERTION(mEncoding == UTF_8_ENCODING, "XHR should default to UTF-8");
     // Now mark charset source as non-weak to signal that we have a decision
     mCharsetSource = kCharsetFromDocTypeDefault;
     mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
   }
-  return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-      aFromSegment, aCount, aWriteCount);
+  return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment);
 }
 
-nsresult nsHtml5StreamParser::SniffStreamBytes(const uint8_t* aFromSegment,
-                                               uint32_t aCount,
-                                               uint32_t* aWriteCount) {
+nsresult nsHtml5StreamParser::SniffStreamBytes(
+    Span<const uint8_t> aFromSegment) {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   nsresult rv = NS_OK;
-  uint32_t writeCount;
-
   // mEncoding and mCharsetSource potentially have come from channel or higher
   // by now. If we find a BOM, SetupDecodingFromBom() will overwrite them.
   // If we don't find a BOM, the previously set values of mEncoding and
   // mCharsetSource are not modified by the BOM sniffing here.
-  for (uint32_t i = 0; i < aCount && mBomState != BOM_SNIFFING_OVER; i++) {
+  for (uint32_t i = 0;
+       i < aFromSegment.Length() && mBomState != BOM_SNIFFING_OVER; i++) {
     switch (mBomState) {
       case BOM_SNIFFING_NOT_STARTED:
         NS_ASSERTION(i == 0, "Bad BOM sniffing state.");
-        switch (*aFromSegment) {
+        switch (aFromSegment[0]) {
           case 0xEF:
             mBomState = SEEN_UTF_8_FIRST_BYTE;
             break;
           case 0xFF:
             mBomState = SEEN_UTF_16_LE_FIRST_BYTE;
             break;
           case 0xFE:
             mBomState = SEEN_UTF_16_BE_FIRST_BYTE;
             break;
           default:
             mBomState = BOM_SNIFFING_OVER;
             break;
         }
         break;
       case SEEN_UTF_16_LE_FIRST_BYTE:
         if (aFromSegment[i] == 0xFE) {
-          rv = SetupDecodingFromBom(
-              UTF_16LE_ENCODING);  // upper case is the raw form
+          rv = SetupDecodingFromBom(UTF_16LE_ENCODING);
           NS_ENSURE_SUCCESS(rv, rv);
-          uint32_t count = aCount - (i + 1);
-          rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
-          NS_ENSURE_SUCCESS(rv, rv);
-          *aWriteCount = writeCount + (i + 1);
-          return rv;
+          return WriteStreamBytes(aFromSegment.From(i + 1));
         }
         mBomState = BOM_SNIFFING_OVER;
         break;
       case SEEN_UTF_16_BE_FIRST_BYTE:
         if (aFromSegment[i] == 0xFF) {
-          rv = SetupDecodingFromBom(
-              UTF_16BE_ENCODING);  // upper case is the raw form
+          rv = SetupDecodingFromBom(UTF_16BE_ENCODING);
           NS_ENSURE_SUCCESS(rv, rv);
-          uint32_t count = aCount - (i + 1);
-          rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
-          NS_ENSURE_SUCCESS(rv, rv);
-          *aWriteCount = writeCount + (i + 1);
-          return rv;
+          return WriteStreamBytes(aFromSegment.From(i + 1));
         }
         mBomState = BOM_SNIFFING_OVER;
         break;
       case SEEN_UTF_8_FIRST_BYTE:
         if (aFromSegment[i] == 0xBB) {
           mBomState = SEEN_UTF_8_SECOND_BYTE;
         } else {
           mBomState = BOM_SNIFFING_OVER;
         }
         break;
       case SEEN_UTF_8_SECOND_BYTE:
         if (aFromSegment[i] == 0xBF) {
-          rv = SetupDecodingFromBom(
-              UTF_8_ENCODING);  // upper case is the raw form
+          rv = SetupDecodingFromBom(UTF_8_ENCODING);
           NS_ENSURE_SUCCESS(rv, rv);
-          uint32_t count = aCount - (i + 1);
-          rv = WriteStreamBytes(aFromSegment + (i + 1), count, &writeCount);
-          NS_ENSURE_SUCCESS(rv, rv);
-          *aWriteCount = writeCount + (i + 1);
-          return rv;
+          return WriteStreamBytes(aFromSegment.From(i + 1));
         }
         mBomState = BOM_SNIFFING_OVER;
         break;
       default:
         mBomState = BOM_SNIFFING_OVER;
         break;
     }
   }
@@ -683,122 +657,121 @@ nsresult nsHtml5StreamParser::SniffStrea
   if (mBomState == BOM_SNIFFING_OVER && mCharsetSource == kCharsetFromChannel) {
     // There was no BOM and the charset came from channel. mEncoding
     // still contains the charset from the channel as set by an
     // earlier call to SetDocumentCharset(), since we didn't find a BOM and
     // overwrite mEncoding. (Note that if the user has overridden the charset,
     // we don't come here but check <meta> for XSS-dangerous charsets first.)
     mFeedChardet = false;
     mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
-    return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-        aFromSegment, aCount, aWriteCount);
+    return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment);
   }
 
   if (!mMetaScanner &&
       (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA)) {
     mMetaScanner = new nsHtml5MetaScanner(mTreeBuilder);
   }
 
-  if (mSniffingLength + aCount >= NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE) {
+  if (mSniffingLength + aFromSegment.Length() >=
+      NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE) {
     // this is the last buffer
     uint32_t countToSniffingLimit =
         NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE - mSniffingLength;
     if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
-      nsHtml5ByteReadable readable(aFromSegment,
-                                   aFromSegment + countToSniffingLimit);
+      nsHtml5ByteReadable readable(
+          aFromSegment.Elements(),
+          aFromSegment.Elements() + countToSniffingLimit);
       nsAutoCString charset;
       auto encoding = mMetaScanner->sniff(&readable);
       // Due to the way nsHtml5Portability reports OOM, ask the tree buider
       nsresult rv;
       if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
         MarkAsBroken(rv);
         return rv;
       }
       if (encoding) {
         // meta scan successful; honor overrides unless meta is XSS-dangerous
         if ((mCharsetSource == kCharsetFromParentForced ||
              mCharsetSource == kCharsetFromUserForced) &&
             (encoding->IsAsciiCompatible() ||
              encoding == ISO_2022_JP_ENCODING)) {
           // Honor override
           return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-              aFromSegment, aCount, aWriteCount);
+              aFromSegment);
         }
         mEncoding = WrapNotNull(encoding);
         mCharsetSource = kCharsetFromMetaPrescan;
         mFeedChardet = false;
         mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
         return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-            aFromSegment, aCount, aWriteCount);
+            aFromSegment);
       }
     }
     if (mCharsetSource == kCharsetFromParentForced ||
         mCharsetSource == kCharsetFromUserForced) {
       // meta not found, honor override
-      return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-          aFromSegment, aCount, aWriteCount);
+      return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment);
     }
-    return FinalizeSniffing(aFromSegment, aCount, aWriteCount,
-                            countToSniffingLimit);
+    return FinalizeSniffing(aFromSegment, countToSniffingLimit, false);
   }
 
   // not the last buffer
   if (mMode == NORMAL || mMode == VIEW_SOURCE_HTML || mMode == LOAD_AS_DATA) {
-    nsHtml5ByteReadable readable(aFromSegment, aFromSegment + aCount);
+    nsHtml5ByteReadable readable(
+        aFromSegment.Elements(),
+        aFromSegment.Elements() + aFromSegment.Length());
     auto encoding = mMetaScanner->sniff(&readable);
     // Due to the way nsHtml5Portability reports OOM, ask the tree buider
     nsresult rv;
     if (NS_FAILED((rv = mTreeBuilder->IsBroken()))) {
       MarkAsBroken(rv);
       return rv;
     }
     if (encoding) {
       // meta scan successful; honor overrides unless meta is XSS-dangerous
       if ((mCharsetSource == kCharsetFromParentForced ||
            mCharsetSource == kCharsetFromUserForced) &&
           (encoding->IsAsciiCompatible() || encoding == ISO_2022_JP_ENCODING)) {
         // Honor override
         return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-            aFromSegment, aCount, aWriteCount);
+            aFromSegment);
       }
       mEncoding = WrapNotNull(encoding);
       mCharsetSource = kCharsetFromMetaPrescan;
       mFeedChardet = false;
       mTreeBuilder->SetDocumentCharset(mEncoding, mCharsetSource);
-      return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-          aFromSegment, aCount, aWriteCount);
+      return SetupDecodingAndWriteSniffingBufferAndCurrentSegment(aFromSegment);
     }
   }
 
   if (!mSniffingBuffer) {
     mSniffingBuffer = MakeUniqueFallible<uint8_t[]>(
         NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE);
     if (!mSniffingBuffer) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
-  memcpy(&mSniffingBuffer[mSniffingLength], aFromSegment, aCount);
-  mSniffingLength += aCount;
-  *aWriteCount = aCount;
+  memcpy(&mSniffingBuffer[mSniffingLength], aFromSegment.Elements(),
+         aFromSegment.Length());
+  mSniffingLength += aFromSegment.Length();
   return NS_OK;
 }
 
-nsresult nsHtml5StreamParser::WriteStreamBytes(const uint8_t* aFromSegment,
-                                               uint32_t aCount,
-                                               uint32_t* aWriteCount) {
+nsresult nsHtml5StreamParser::WriteStreamBytes(
+    Span<const uint8_t> aFromSegment) {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   // mLastBuffer should always point to a buffer of the size
   // NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
   if (!mLastBuffer) {
     NS_WARNING("mLastBuffer should not be null!");
     MarkAsBroken(NS_ERROR_NULL_POINTER);
     return NS_ERROR_NULL_POINTER;
   }
   size_t totalRead = 0;
-  auto src = MakeSpan(aFromSegment, aCount);
+  auto src = aFromSegment;
   for (;;) {
     auto dst = mLastBuffer->TailAsSpan(NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
     uint32_t result;
     size_t read;
     size_t written;
     bool hadErrors;
     Tie(result, read, written, hadErrors) =
         mUnicodeDecoder->DecodeToUTF16(src, dst, false);
@@ -819,19 +792,18 @@ nsresult nsHtml5StreamParser::WriteStrea
           nsHtml5OwningUTF16Buffer::FalliblyCreate(
               NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE);
       if (!newBuf) {
         MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
         return NS_ERROR_OUT_OF_MEMORY;
       }
       mLastBuffer = (mLastBuffer->next = newBuf.forget());
     } else {
-      MOZ_ASSERT(totalRead == aCount,
+      MOZ_ASSERT(totalRead == aFromSegment.Length(),
                  "The Unicode decoder consumed the wrong number of bytes.");
-      *aWriteCount = totalRead;
       return NS_OK;
     }
   }
 }
 
 class MaybeRunCollector : public Runnable {
  public:
   explicit MaybeRunCollector(nsIDocShell* aDocShell)
@@ -1016,19 +988,19 @@ void nsHtml5StreamParser::DoStopRequest(
 
   if (IsTerminated()) {
     return;
   }
 
   mStreamState = STREAM_ENDED;
 
   if (!mUnicodeDecoder) {
-    uint32_t writeCount;
     nsresult rv;
-    if (NS_FAILED(rv = FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
+    Span<const uint8_t> empty;
+    if (NS_FAILED(rv = FinalizeSniffing(empty, 0, true))) {
       MarkAsBroken(rv);
       return;
     }
   } else if (mFeedChardet) {
     mChardet->Done();
   }
 
   MOZ_ASSERT(mUnicodeDecoder,
@@ -1107,45 +1079,42 @@ nsresult nsHtml5StreamParser::OnStopRequ
   }
   nsCOMPtr<nsIRunnable> stopper = new nsHtml5RequestStopper(this);
   if (NS_FAILED(mEventTarget->Dispatch(stopper, nsIThread::DISPATCH_NORMAL))) {
     NS_WARNING("Dispatching StopRequest event failed.");
   }
   return NS_OK;
 }
 
-void nsHtml5StreamParser::DoDataAvailable(const uint8_t* aBuffer,
-                                          uint32_t aLength) {
+void nsHtml5StreamParser::DoDataAvailable(Span<const uint8_t> aBuffer) {
   NS_ASSERTION(IsParserThread(), "Wrong thread!");
   MOZ_RELEASE_ASSERT(STREAM_BEING_READ == mStreamState,
                      "DoDataAvailable called when stream not open.");
   mTokenizerMutex.AssertCurrentThreadOwns();
 
   if (IsTerminated()) {
     return;
   }
 
-  uint32_t writeCount;
   nsresult rv;
   if (HasDecoder()) {
     if (mFeedChardet) {
       bool dontFeed;
-      mChardet->DoIt((const char*)aBuffer, aLength, &dontFeed);
+      mChardet->DoIt((const char*)aBuffer.Elements(), aBuffer.Length(),
+                     &dontFeed);
       mFeedChardet = !dontFeed;
     }
-    rv = WriteStreamBytes(aBuffer, aLength, &writeCount);
+    rv = WriteStreamBytes(aBuffer);
   } else {
-    rv = SniffStreamBytes(aBuffer, aLength, &writeCount);
+    rv = SniffStreamBytes(aBuffer);
   }
   if (NS_FAILED(rv)) {
     MarkAsBroken(rv);
     return;
   }
-  NS_ASSERTION(writeCount == aLength,
-               "Wrong number of stream bytes written/sniffed.");
 
   if (IsTerminatedOrInterrupted()) {
     return;
   }
 
   ParseAvailableData();
 
   if (mFlushTimerArmed || mSpeculating) {
@@ -1161,29 +1130,27 @@ void nsHtml5StreamParser::DoDataAvailabl
         nsITimer::TYPE_ONE_SHOT, "nsHtml5StreamParser::DoDataAvailable");
   }
   mFlushTimerArmed = true;
 }
 
 class nsHtml5DataAvailable : public Runnable {
  private:
   nsHtml5StreamParserPtr mStreamParser;
-  UniquePtr<uint8_t[]> mData;
-  uint32_t mLength;
+  Buffer<uint8_t> mData;
 
  public:
   nsHtml5DataAvailable(nsHtml5StreamParser* aStreamParser,
-                       UniquePtr<uint8_t[]> aData, uint32_t aLength)
+                       Buffer<uint8_t>&& aData)
       : Runnable("nsHtml5DataAvailable"),
         mStreamParser(aStreamParser),
-        mData(std::move(aData)),
-        mLength(aLength) {}
+        mData(std::move(aData)) {}
   NS_IMETHOD Run() override {
     mozilla::MutexAutoLock autoLock(mStreamParser->mTokenizerMutex);
-    mStreamParser->DoDataAvailable(mData.get(), mLength);
+    mStreamParser->DoDataAvailable(mData);
     return NS_OK;
   }
 };
 
 nsresult nsHtml5StreamParser::OnDataAvailable(nsIRequest* aRequest,
                                               nsISupports* aContext,
                                               nsIInputStream* aInStream,
                                               uint64_t aSourceOffset,
@@ -1192,27 +1159,28 @@ nsresult nsHtml5StreamParser::OnDataAvai
   if (NS_FAILED(rv = mExecutor->IsBroken())) {
     return rv;
   }
 
   NS_ASSERTION(mRequest == aRequest, "Got data on wrong stream.");
   uint32_t totalRead;
   // Main thread to parser thread dispatch requires copying to buffer first.
   if (NS_IsMainThread()) {
-    auto data = MakeUniqueFallible<uint8_t[]>(aLength);
-    if (!data) {
+    Maybe<Buffer<uint8_t>> maybe = Buffer<uint8_t>::Alloc(aLength);
+    if (maybe.isNothing()) {
       return mExecutor->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
     }
-    rv = aInStream->Read(reinterpret_cast<char*>(data.get()), aLength,
-                         &totalRead);
+    Buffer<uint8_t> data(std::move(*maybe));
+    rv = aInStream->Read(reinterpret_cast<char*>(data.Elements()),
+                         data.Length(), &totalRead);
     NS_ENSURE_SUCCESS(rv, rv);
-    NS_ASSERTION(totalRead <= aLength, "Read more bytes than were available?");
+    MOZ_ASSERT(totalRead == aLength);
 
     nsCOMPtr<nsIRunnable> dataAvailable =
-        new nsHtml5DataAvailable(this, std::move(data), totalRead);
+        new nsHtml5DataAvailable(this, std::move(data));
     if (NS_FAILED(mEventTarget->Dispatch(dataAvailable,
                                          nsIThread::DISPATCH_NORMAL))) {
       NS_WARNING("Dispatching DataAvailable event failed.");
     }
     return rv;
   } else {
     NS_ASSERTION(IsParserThread(), "Wrong thread!");
     mozilla::MutexAutoLock autoLock(mTokenizerMutex);
@@ -1228,17 +1196,17 @@ nsresult nsHtml5StreamParser::OnDataAvai
   }
 }
 
 /* static */ nsresult nsHtml5StreamParser::CopySegmentsToParser(
     nsIInputStream* aInStream, void* aClosure, const char* aFromSegment,
     uint32_t aToOffset, uint32_t aCount, uint32_t* aWriteCount) {
   nsHtml5StreamParser* parser = static_cast<nsHtml5StreamParser*>(aClosure);
 
-  parser->DoDataAvailable((const uint8_t*)aFromSegment, aCount);
+  parser->DoDataAvailable(AsBytes(MakeSpan(aFromSegment, aCount)));
   // Assume DoDataAvailable consumed all available bytes.
   *aWriteCount = aCount;
   return NS_OK;
 }
 
 const Encoding* nsHtml5StreamParser::PreferredForInternalEncodingDecl(
     const nsACString& aEncoding) {
   const Encoding* newEncoding = Encoding::ForLabel(aEncoding);
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -17,16 +17,17 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/UniquePtr.h"
 #include "nsHtml5AtomTable.h"
 #include "nsHtml5Speculation.h"
 #include "nsISerialEventTarget.h"
 #include "nsITimer.h"
 #include "nsICharsetDetector.h"
 #include "mozilla/dom/DocGroup.h"
+#include "mozilla/Buffer.h"
 
 class nsHtml5Parser;
 
 #define NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE 1024
 #define NS_HTML5_STREAM_PARSER_SNIFFING_BUFFER_SIZE 1024
 
 enum eParserMode {
   /**
@@ -238,17 +239,17 @@ class nsHtml5StreamParser final : public
    * timer.
    */
   void FlushTreeOpsAndDisarmTimer();
 
   void ParseAvailableData();
 
   void DoStopRequest();
 
-  void DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength);
+  void DoDataAvailable(mozilla::Span<const uint8_t> aBuffer);
 
   static nsresult CopySegmentsToParser(nsIInputStream* aInStream,
                                        void* aClosure, const char* aFromSegment,
                                        uint32_t aToOffset, uint32_t aCount,
                                        uint32_t* aWriteCount);
 
   bool IsTerminatedOrInterrupted() {
     mozilla::MutexAutoLock autoLock(mTerminatedMutex);
@@ -263,61 +264,48 @@ class nsHtml5StreamParser final : public
   /**
    * True when there is a Unicode decoder already
    */
   inline bool HasDecoder() { return !!mUnicodeDecoder; }
 
   /**
    * Push bytes from network when there is no Unicode decoder yet
    */
-  nsresult SniffStreamBytes(const uint8_t* aFromSegment, uint32_t aCount,
-                            uint32_t* aWriteCount);
+  nsresult SniffStreamBytes(mozilla::Span<const uint8_t> aFromSegment);
 
   /**
    * Push bytes from network when there is a Unicode decoder already
    */
-  nsresult WriteStreamBytes(const uint8_t* aFromSegment, uint32_t aCount,
-                            uint32_t* aWriteCount);
+  nsresult WriteStreamBytes(mozilla::Span<const uint8_t> aFromSegment);
 
   /**
    * Check whether every other byte in the sniffing buffer is zero.
    */
-  void SniffBOMlessUTF16BasicLatin(const uint8_t* aFromSegment,
-                                   uint32_t aCountToSniffingLimit);
+  void SniffBOMlessUTF16BasicLatin(mozilla::Span<const uint8_t> aFromSegment);
 
   /**
    * <meta charset> scan failed. Try chardet if applicable. After this, the
    * the parser will have some encoding even if a last resolt fallback.
    *
-   * @param aFromSegment The current network buffer or null if the sniffing
-   *                     buffer is being flushed due to network stream ending.
-   * @param aCount       The number of bytes in aFromSegment (ignored if
-   *                     aFromSegment is null)
-   * @param aWriteCount  Return value for how many bytes got read from the
-   *                     buffer.
+   * @param aFromSegment The current network buffer
    * @param aCountToSniffingLimit The number of unfilled slots in
    *                              mSniffingBuffer
+   * @param aEof true iff called upon end of stream
    */
-  nsresult FinalizeSniffing(const uint8_t* aFromSegment, uint32_t aCount,
-                            uint32_t* aWriteCount,
-                            uint32_t aCountToSniffingLimit);
+  nsresult FinalizeSniffing(mozilla::Span<const uint8_t> aFromSegment,
+                            uint32_t aCountToSniffingLimit, bool aEof);
 
   /**
    * Set up the Unicode decoder and write the sniffing buffer into it
    * followed by the current network buffer.
    *
-   * @param aFromSegment The current network buffer or null if the sniffing
-   *                     buffer is being flushed due to network stream ending.
-   * @param aCount       The number of bytes in aFromSegment (ignored if
-   *                     aFromSegment is null)
-   * @param aWriteCount  Return value for how many bytes got read from the
-   *                     buffer.
+   * @param aFromSegment The current network buffer
    */
   nsresult SetupDecodingAndWriteSniffingBufferAndCurrentSegment(
-      const uint8_t* aFromSegment, uint32_t aCount, uint32_t* aWriteCount);
+      mozilla::Span<const uint8_t> aFromSegment);
 
   /**
    * Initialize the Unicode decoder, mark the BOM as the source and
    * drop the sniffer.
    *
    * @param aDecoderCharsetName The name for the decoder's charset
    *                            (UTF-16BE, UTF-16LE or UTF-8; the BOM has
    *                            been swallowed)
--- a/taskcluster/ci/packages/kind.yml
+++ b/taskcluster/ci/packages/kind.yml
@@ -268,18 +268,19 @@ jobs:
 
   deb7-nasm:
     description: "nasm for Debian wheezy"
     treeherder:
       symbol: Deb7(nasm)
     run:
       using: debian-package
       dsc:
-        url: http://snapshot.debian.org/archive/debian/20160426T155816Z/pool/main/n/nasm/nasm_2.12.01-1.dsc
-        sha256: 70b0478243ca8b36e163af15fb8a0a9be504b3321a9fb7d67ba0767dbfcc32ec
+        url: http://snapshot.debian.org/archive/debian/20170704T094954Z/pool/main/n/nasm/nasm_2.13.01-1.dsc
+        sha256: 76528365eddc646f3f53c9f501ae9c2ba1678a163303d297e9014e3da36643c8
+      patch: nasm-wheezy.diff
 
   deb7-pcre3:
     description: "pcre3 8.31 for Debian Wheezy"
     treeherder:
       symbol: Deb7(pcre3)
     run:
       using: debian-package
       dsc:
--- a/toolkit/components/printing/tests/browser.ini
+++ b/toolkit/components/printing/tests/browser.ini
@@ -2,14 +2,14 @@
 support-files =
   file_page_change_print_original_1.html
   file_page_change_print_original_2.html
 skip-if = os == "mac"
 
 [browser_preview_print_simplify_non_article.js]
 support-files =
     simplifyNonArticleSample.html
-skip-if = os == "mac" || (verify && (os == 'win' || os == 'linux')) || (debug && os == 'linux') # linux debug due to leaks, bug 1503887
+skip-if = os == "mac" || (verify && (os == 'win' || os == 'linux'))
 
 [browser_preview_switch_print_selected.js]
 support-files =
     simplifyArticleSample.html
-skip-if = os == "mac" || (verify && !debug && (os == 'linux')) || (debug && os == 'linux') # linux debug due to leaks, bug 1503887
\ No newline at end of file
+skip-if = os == "mac" || (verify && !debug && (os == 'linux'))
--- a/toolkit/modules/Promise-backend.js
+++ b/toolkit/modules/Promise-backend.js
@@ -55,18 +55,18 @@ var Ci = this.require ? require("chrome"
 // parent stack trace; see scheduleWalkerLoop.  However, as it might
 // not be available (see above), users of this must check it first.
 var Components_ = this.require ? require("chrome").components : Components;
 
 // If Cu is defined, use it to lazily define the FinalizationWitnessService.
 if (Cu) {
   // If we're in a devtools module environment, ChromeUtils won't exist.
   /* eslint "mozilla/use-chromeutils-import": ["error", {allowCu: true}] */
-  Cu.import("resource://gre/modules/Services.jsm");
-  Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+  Cu.import("resource://gre/modules/Services.jsm", this);
+  Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
   XPCOMUtils.defineLazyServiceGetter(this, "FinalizationWitnessService",
                                      "@mozilla.org/toolkit/finalizationwitness;1",
                                      "nsIFinalizationWitnessService");
 
   // For now, we're worried about add-ons using Promises with CPOWs, so we'll
   // permit them in this scope, but this support will go away soon.
   Cu.permitCPOWsInScope(this);
deleted file mode 100644
index 45b9548fc5d348867f136cc222fe86e03546f115..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/linux/mozapps/jar.mn
+++ b/toolkit/themes/linux/mozapps/jar.mn
@@ -1,9 +1,8 @@
 # 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/.
 
 toolkit.jar:
 #include ../../shared/mozapps.inc.mn
 * skin/classic/mozapps/extensions/extensions.css           (extensions/extensions.css)
-  skin/classic/mozapps/extensions/heart.png                (extensions/heart.png)
   skin/classic/mozapps/update/updates.css                  (update/updates.css)
deleted file mode 100644
index 45b9548fc5d348867f136cc222fe86e03546f115..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/osx/mozapps/jar.mn
+++ b/toolkit/themes/osx/mozapps/jar.mn
@@ -6,15 +6,14 @@ toolkit.jar:
 #include ../../shared/mozapps.inc.mn
   skin/classic/mozapps/downloads/buttons.png                      (downloads/buttons.png)
   skin/classic/mozapps/downloads/unknownContentType.css           (downloads/unknownContentType.css)
   skin/classic/mozapps/extensions/discover-logo.png               (extensions/discover-logo.png)
   skin/classic/mozapps/extensions/rating-won.png                  (extensions/rating-won.png)
   skin/classic/mozapps/extensions/rating-not-won.png              (extensions/rating-not-won.png)
   skin/classic/mozapps/extensions/cancel.png                      (extensions/cancel.png)
   skin/classic/mozapps/extensions/toolbarbutton-dropmarker.png    (extensions/toolbarbutton-dropmarker.png)
-  skin/classic/mozapps/extensions/heart.png                       (extensions/heart.png)
 * skin/classic/mozapps/extensions/extensions.css                  (extensions/extensions.css)
   skin/classic/mozapps/extensions/blocklist.css                   (extensions/blocklist.css)
   skin/classic/mozapps/profile/profileSelection.css               (profile/profileSelection.css)
   skin/classic/mozapps/update/buttons.png                         (update/buttons.png)
 * skin/classic/mozapps/update/updates.css                         (update/updates.css)
   skin/classic/mozapps/handling/handling.css                      (handling/handling.css)
--- a/toolkit/themes/shared/extensions/extensions.inc.css
+++ b/toolkit/themes/shared/extensions/extensions.inc.css
@@ -716,35 +716,25 @@ button.warning {
 }
 
 #detail-contrib-suggested {
   color: grey;
   font-weight: bold;
 }
 
 #detail-contrib-btn {
-  color: var(--in-content-selected-text);
-  text-shadow: none;
-  border: 1px solid transparent;
-  list-style-image: url("chrome://mozapps/skin/extensions/heart.png");
-  background-color: var(--in-content-primary-button-background);
+  -moz-context-properties: fill;
+  fill: currentColor;
+  list-style-image: url("chrome://global/skin/icons/heart.svg");
 }
 
 #detail-contrib-btn .button-icon {
   margin-inline-end: 5px;
 }
 
-#detail-contrib-btn:not(:active):hover {
-  background-color: var(--in-content-primary-button-background-hover);
-}
-
-#detail-contrib-btn:active:hover {
-  background-color: var(--in-content-primary-button-background-active);
-}
-
 #detail-grid {
   margin-bottom: 2em;
 }
 
 #detail-grid > columns > column:first-child {
   min-width: 15em;
   max-width: 25em;
 }
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/icons/heart.svg
@@ -0,0 +1,7 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="context-fill">
+  <path d="M8 6s0-4 3.5-4S15 5 15 6c0 4.5-7 9-7 9z"/>
+  <path d="M8 6s0-4-3.5-4S1 5 1 6c0 4.5 7 9 7 9l1-6z"/>
+</svg>
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -30,16 +30,17 @@ toolkit.jar:
   skin/classic/global/icons/calendar-arrow-left.svg        (../../shared/icons/calendar-arrow-left.svg)
   skin/classic/global/icons/calendar-arrow-right.svg       (../../shared/icons/calendar-arrow-right.svg)
   skin/classic/global/icons/check.svg                      (../../shared/icons/check.svg)
   skin/classic/global/icons/check-partial.svg              (../../shared/icons/check-partial.svg)
   skin/classic/global/icons/close.svg                      (../../shared/icons/close.svg)
   skin/classic/global/icons/error.svg                      (../../shared/icons/error.svg)
   skin/classic/global/icons/find-previous-arrow.svg        (../../shared/icons/find-previous-arrow.svg)
   skin/classic/global/icons/find-next-arrow.svg            (../../shared/icons/find-next-arrow.svg)
+  skin/classic/global/icons/heart.svg                      (../../shared/icons/heart.svg)
   skin/classic/global/icons/help.svg                       (../../shared/icons/help.svg)
   skin/classic/global/icons/info.svg                       (../../shared/incontent-icons/info.svg)
   skin/classic/global/icons/loading.png                    (../../shared/icons/loading.png)
   skin/classic/global/icons/loading@2x.png                 (../../shared/icons/loading@2x.png)
   skin/classic/global/icons/performance.svg                (../../shared/icons/performance.svg)
   skin/classic/global/icons/resizer.svg                    (../../shared/icons/resizer.svg)
   skin/classic/global/icons/shortcut.svg                   (../../shared/icons/shortcut.svg)
   skin/classic/global/icons/spinner-arrow-down.svg         (../../shared/icons/spinner-arrow-down.svg)
deleted file mode 100644
index 45b9548fc5d348867f136cc222fe86e03546f115..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/windows/mozapps/jar.mn
+++ b/toolkit/themes/windows/mozapps/jar.mn
@@ -1,9 +1,8 @@
 # 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/.
 
 toolkit.jar:
 #include ../../shared/mozapps.inc.mn
 * skin/classic/mozapps/extensions/extensions.css             (extensions/extensions.css)
-  skin/classic/mozapps/extensions/heart.png                  (extensions/heart.png)
   skin/classic/mozapps/update/updates.css                    (update/updates.css)