Merge mozilla-central to inbound. a=merge CLOSED TREE
authorNoemi Erli <nerli@mozilla.com>
Sat, 08 Dec 2018 00:18:17 +0200
changeset 449668 904080f44dbd2ae3cc92c794a817b281d6cb7237
parent 449667 b2eb9000daed2a2cff623eae8ca2387bb59e4601 (current diff)
parent 449612 ca0f00593e38cdab54c3a990059bbf1150e77365 (diff)
child 449669 a72dafbb2e8089c38cf155f846e2256b90c1aa9e
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone65.0a1
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
zc%1Eedsx!v|2Nf|Wh-lGmU)=<EmM}pnwgsNovXQ4>NXQkpe0!|Jc5c!NV8mJd1%cb
zkE@lg6!3(K$4-ihhspvK6idMj!9;}+k>~!<w%_ls=db6#XV+Dq%gfJw-|y4w{d&J{
z<ns64o@))w3}G<X+QZ)-{1FCQfd~H$SA7hA^PnWr3kGv<KYZ}3V@U<FVN%6i9A-dW
z_e!`R^eNst@eQ$f1?}-N_QwBa360aXeYVH*#QLjg8@FBFdG)KCPkzg#oh&(eG`F(k
zN37ki#y4|vj+*=4MC5k<i3q`dX|^Zsll2bP2UlEL9&)>PRyNEmuO1vGH;|^?wNqO4
zG3{G2KWMt~r{hdy%Y4&x(8bi40N@hsmz)*gdG+7tzt4Z4|33eH{`>s@>zP!)xIh!R
zIJY`WoZ8QxIV$kUifE0Ry^OG_^H>Li9q(sm+Lq^~ohjV**7!t17*E?HOF27H7g|oA
z6<||j=Bu1xu+i84n@vz&Kh$`~9s7jdAJ-#Ua&fD3E)<ZyO!u1}o?ATR-LVV?n}}}y
zvSPj6r96*6ZExgldmCSESzXj$tD1DnA8F;Q$&fweA8CKW>@H;7F8S}76#AZ_2*c`H
zX?$dVvUIM<Yg&Xu$3m;@Yr^iWviqgh0Ok8%dVi&BRDM>JOWkC1O5aH1*g)L{a~Ler
zF30dz+Mj#ua`MvdP0nwN7~UsclGI}Jd!tgABh@N-!hR!g)H!y!Xji~y$3s6pJZ8?q
ze0^W03@o0mR|*nSr+rU4Hm@^vwl;u0klS6@_jOr7P-)WJ`Mo_UkpkJRmxR~{tDRxz
z&c7MMX8cj|y<Jw`>w7sv1*iu@(Z%!cdt)-nOD4{_?HC8{qS{a=j{V0Gx8E`SSoJq3
zL5a2}uy1nlHa&t>&UsJ+{C%L_^=k%w`Mv#i1$o=<^Lh1W8p$awll8q3DcK{;2&>};
zz}fwqGw#0rbJUKOR|LPqvt<OW#5FoM^Wx7=I5zwA&%#V;prZEnZO_cm^lwePRvvr!
z!Wh+KhL$E4W=*~?A&`noCYC8zgMbPeJ<8sk5g7Y;Jv(;HF&$M+ZpHF!>s?yobekS*
z0b%X`HseoJsbOi!r89<QC46GuJZ&4@wkD-G)MCYhl{VwB(J;Ty>1CKh{>xY&`}rPs
zY&$0CnVip(74*v7QkcqRw4$69=Z23R18(?k@H=Ya*Y?uIqQ6|z+~PeEA3gmH2dh|N
zZSdeB>dS=7#|5WINhZF6(h1j+$@j0z-n}IcEq`FS5fAGw9S!>9-X*&~^Ol!A9V*Dq
zj1b_wUcHPDnNXR-j(*dzEcTVLSDUXj>S$Cu-wYj<ugnjbu0NH@EHXH^;wX51s_oc}
z-?t{m-J%We-^OX?&N59yEkwyH1A<|rO{<;HY0G1SR^0#Hj*%CJE6mP{X!PE&^8sx1
z_RevrYR5MCea{L#XA;<knqfJ+@o`?9L~r;{g)3lFt{&@-JJh84U104lH!LrC6Ev|M
zJGAM97n3+;c0vXg{I_G-)SXLqk23C~ScdeHcXki*5?Uwnvx58JT>$5oa#koy)6QUC
z+Ep0RAC;+@Si_2h6vn$VhIJn0yI~Iweb=$fl)1d@_n}WJAC=j*=Yhx^o<J`NV7INc
z!4!R4S-o9(2Hn!B^^21A!KL5gGAduwH%l$|0+D?_XGL%MDmY_^fk2%#!S*zWW^q?d
zV!}Rw9rsz~460iJ5^C3wx4c{}MnvToO%G(jM_mkH=dS#cbAAi!3l!QUh<=W>4RtB1
z*X(5TNZiUm_(>S-s87ek$MgZaZbL>|IZWe#&8SQe5G?q1fy?+aFqnCO$GSw{O23mP
z;cfmKP*)wTEc?fb90r1Rz`GVQ&ca~(*LbY+zI$ZD8v5<>cOW;FJ(KJZ+}-kXgvo6%
znCZEkvC&;!8FW-nkO?k#3K`RVipC1!#lc{)8$QK%moZrmy@rfO<?G=c?(z5(boqP`
z{1m->8SH`kXXEwt@y^(?6Z8y|6X}B#7w5LX>|jUuNDu}t0Tgv^1AeNxr5l@Bjp{V4
z3N`aBaCEaD%Bao)#N>UXBTmlanmCP>tJ@GOgZhYp*kHhY{8i5GzCZK20+yk2w2wM%
zpBl1?rsuN&YufL(Cg#Ln9&1QL<(QmCQt%lyawKjg9SZ{)ypbbtSWvd3Ivbu;{#TQ|
z)wjkZqWNQe@_K%cIfdR%e_3s0&sF@4utL|~rhhBtdVp;!zUshyvhWSZo;amwH@d1e
z!j2$MVZ5YxP8h_)>+z-@twi4az_N%#Lw$t>M)tv>JHs5;>T?=hSA*w;XQdDI6%{B+
zj7z}7fgK+5$oP9>2JOZXy6Em!E8lX~eI7LvTzb$jkLX5JpZlJY?@6N0$uBxkLU#o4
zZ}dcB?AMIk8We9yMk_X(-HUbKBiRl@g4u^@kmF$ew;CsGD!!Y<IU8Hq07Zn;T@ZS5
zY91*!VwYahZp(CAXJ-&#gQlavTE)eVxJo=ZiygGvbP)#lv>#@_l%sh<v(H?-g+T6+
zaN5rv{{-9%mnF=k3mvj2pLghp-KtFWB%;_1EPt@th|)oh>^90)7a??gv_~M=bG|7_
z9t_bbjSaR1ca(Z$ISlqL2UDM;nL$uux{>DnMv-7=thaLCc<PC6k>zYJLi;6f2sRNF
z<G~<}EGcNJtDxk^{bkV|+Ho53s=BouysofGbQoXirXi6ataENGS8G)pcInw>@JcCE
zUlj5))i<EP(Ca6x@Sg2w<AL8W(<kbS=8dn36k^~4<@GUvNL@(Ro+7kP&Pb-_gcMCo
zu;A2iXkAYOg(2t}oMiV<`WsrKolcidJlB03SQn_`NAa|UwO)xs`yvE)qOSKuU9a^5
zKR0?0?Hd&S^E>eGVnYwuS>3<Xuo6t$|BTyzy0Y6vi+rA_F?Q_n*5uFB$YY}Od$fg;
zItmF#+Sl(a5D`l`r}ql_4(sv@y6%MtCe;@eLM3N#NvL-3RZjHI?Vp$xzD0+w00+i>
z_O<+&F4L(P2`m6h<opE6@Tp;uaMQ`s{IpZUjB4$S*?(Q5th$WDqlkIF&AeGx?deE&
zerh$FQZjg86VMSAD@^cGO214{BQo{`i+cFIHLc{vUM0t?&+*U^LBc+(;)qt+<f*+^
zdD{M{`~=-GkewK4fi!Z-wXdc%dMm)-!*xeH=(^nk#`RjMu~B}tmVNuS%q2gYuT2n?
zYotmb8v1JkRZew+(^^$jON)HEW;nuIH8j{Q(M;8d5({P7`TcGNFxY{CpAFglJ!Wf5
z1<|1qLuZ>nXm0)G)0F#@^CgrCN$sFe6p=!nb<6AFoUU$-&i9qY^h##6u0ssMzU*bN
za|W)l*8ZC#qw|$lvU&!uRw#*iawA-0??Te>zI;uOL=z<v70CMLr-km?M!`N;SqyZB
zQ{#M+UTP#3f^&y}BbHPDO-G-2I#m<8zcAXc3X~`n_t!Bp=5%FrenM-MZ-t|DsUd2C
zs4sp|&zz`E=1sWI?^UY%y@uppMl=GM_29e(EeF1zH`y8|=&}71$c?CKcIef@=+s#!
ztEQ-Zs{DEK5O7U<n(kZA!jy8#mSklK6b<mkTd=cKu+Mq8wsSDLTFwNS6x2Zn%D6d#
z$<`k#dB%tGfhq|*Q|MU1i}L+V_N7I?(kT^-)0Za>lrv3stiRO=G!6^&(Py}fqQcsh
z&+e6cFK7XVuh0`n_XjP{fHDyTzymK`pf<QJh7TGoK$f2aPc*#UWBbd{KHPrRu)CWm
z{_J#rqGoR4=Be4{JXtoW80r!)9lJEo!}u=?yUwhPER<3A?Q`RoYw`s>v>{<==>%^Q
zStj$Y*0OpyQcXF4j{`5yGA8;TJMHfC_rlfVHg&a7yMSsi5v(oWOH&2DUK1?LM0_?e
zg*nfz_0q-E3>Orw_^=BXF5^?Q{j-;SBBS_|Gn0~I2o_ruX<6r--&cHwLa1$>UZ@9|
z7J-g0S9*1@8k95H6v^USW^@!q#;+~;6RKwY=v&I=p2I-N1Qg0=B&ncdZtjfAF9&nv
z+!S!F!(CZPSHJN@5*j}qZUB8>+lH#uG2<FI`I^DAXYXqZCgT!Q0Mmb_**V^Dpz1TY
zFb_20rbw@{7^pOk;}8!gf|m=Aes%2Hcbd#FX)GBPM8Ls|bAiBwl<S>QCC|k{!f^lx
zHA^C8kqLI0e+2ECk=-u;T_D^ibJ8Asc&`@q&o<SNVO3f*=o?L(+&4<a(HR0{d{qI8
zP;9hI!W&$w4=jfEfpm3;i7udZyBiMRhxMQW{ksMR+jE5sq#6unkpBPldeWVIQFtrH
zrN+Ev{2l(y(_7+e7cJQb6dSV9_Q_9naKp2kBH4@As1E*Ud>j5{xc_cGI<W^Y6Z7qz
z%v(;dXWnIUz5GXNg!q@cTaiwo3A6aeCQhI8L*ukb{=i%BaDR_zClkV<5gA|DV&1qk
zfLE<0I%GR5XyT&ns?W1q<j=TK{-S+ZV8-mPw{OUCy)LzHjb=+58gFoh-`9&**Bv;a
zo(e{ik{5fjL$gAN@odF&IA;Si{{->YL_5sLpy9}M%lSLvEp<q2a=S&}{tZc(_SInA
z6obsW=8~f8PbUw_e1heaIW3nUPw7>>5EQu-tpwroTa4H;M$6ROja00=ZeW&bYDsye
zm=hGIzQ%h&wKK2J5gew5r7pP@w=Uk|EZ*99f*+m-jU~8sofq=@yQ#*a=CjYnT7F9I
z#D-KIP<CKB)zE7rYvRZ0LQ{A78mj-W=Ke<2An2b1!VAK{H!4}fqT+~L^Xo<xU(E(p
z7=88Gc<(b7HI%gNZHR7=iHzH_6Tl;V+j!!`wf@42rp4-zmZq7`7<5SN)XK3fz_I4-
z<B5ZX{1JAO{4z6m<oz9|qW50`e4HEJIj(%VcRER;kd(G0Cy%xmc%w6*WNf`4T=~?;
z%$N|9+#Y<RbXZw41IYsRM!abN(tU|{x+Nj`@%52CmcY*fH*3`%w)lSV4JWsGFK%$r
z(v6L(mrwx5JmlM+QvYa@4aNi)r3idE+kwq~aK<%L3(7Nva-xN-UTj2C4uE<N+{|fQ
z<)&4ZI)v(k%+L_>beVp+0a#vXBUD@vZ!OZSwXtvh2ynz|_G%%iznNRSBt-6h16FOk
zAUs%<#N9|mY8H{wUylHMj9w64C`#t+r6P5YkkdzgfE4Ga_>H7aH;2HbM^~28ig!aA
z@0$+UiqNHThtMVAnxz7(O+aw3@aT|j^bbi9E0lf78bxNGrhR4=@UZWK(4DZjFvbmC
zXlkuF5Gz_S29Q-@wVF+S+ooztQo|k8a4y+N<OHQfb2&#avGCPQ%X)yF0ptbYve>@0
zs?RC&M%tI+@NCjx5{RVRyTjr$Ch4`*8y%f2^{dPB1X(TzYYAhow)#8~ZY`So1+q=M
zmgD++$V?zJphdf;mCPQ?b8QFNIlj|F4r_dsvDI7XdO876m1Nua{+WD&XlbRWMHnyk
zf*g#k+=wrT)i~BUP_*HcVW;5bum>MqFgH@L`WA-7!J!H|7EF=)fYr|KkNcxZLyBI9
z)Cs((N0kXV)AmhA<sXl!<{?W%ri&~wyhvf9KNPV6U%>Ns7sIL3NowL%H;~>i>+$`6
z*dX6_az)AWqCk1`<Ot+@QvGUY%U^N!jglu_Xrv#&`+=ql=BLFLs#dI8VwyTT08njd
z?~zrlRP?2&6?HA|V|Mc~sl!l$&_8)(5uR)D)5)W5+_9L{Mg1?o1;6BF2pdDR4DlK(
zO}gHrTdSOV1$E7Hf*W=2vmatwe*uWWsJ>KzVz<N6jY=^3ryQ+0jLFi~OLE*u^ZP4M
zbL<!&@GB<G5vY`RJ9M!SOQo&TUk9=Ld9NEHZ3CsrymhIbwgRZo&&CHf5<^H63Jg;#
z7u#m1D3ZQh@`mJU<JTRPr=#m%|032U$%wmE$b}`55jz2pae=Efr^w}O=PU6vr4tzt
zvOuFQr9Pp9F*GV%<!pbcRrP>$1GN3yjT`tUhe0cx_UwpzxJdVJ#wE)(h7z}p@;ZUY
zgjt$db5_nfg{XVQ^FAq$6_+YS<rFt5P<s3Kf;TD)=3?&#Ld<~;crVIVUOa&A*fSnk
z7u$$ebJ_gNT()PHHjELd`xSaq0d_o=#I(G{a4D)M{K8A@Q<?0Y&{;`)z?-)*$ah6z
zv8pA79Y4iH@XINF(7X3vf_L(1ol197-Nb2{JJ^l+q9g~mzzs6^>m|c#Ek#8o_bCcI
zMXl(4mZk*M1c>v+1#{E%t&I8;YEsPN1b3s={J2;#Q~x_$Vuk=)jdqUL|0Il)6>>$|
z$D&ptSvw=nn5jRqJHLm9tBPI*8~yB4Jo=ctSv|ugX3<D5UTvj59*ct$+R)VCO@y8W
z(>iF;?;G&w_#y(Rq;8(u8pBSR3Uxl!{Gy5`Ko@dB2G5;8*b(<bN*meebBe@qI)t%B
z<DWVCBR7oklKqdN^>}p6$l_Gp?$DXnF@X#V4X_bZ>*C+d0{<PQp`*}2T5oWW<`*ge
z)^k9G?C7b+7oWmlwV<3SI<9nSMp45_eRY`@?7)glh$eMA&X}%-JxH|~uYZFfsuQ@O
zi(OV|c?$?<ek|^P9iNV-WJ*EPrS2G~JsOK{AgUXjv`_KUOAXt{;t>3|XawK%)>OO*
z4LU}1EeC7nuux8Q2vyt_v4s1TSJ<eD47NNUE~yPY!(Iw!0Ei7ZTrl@lS7}6*2c+qu
zCgGP#J`I-;J=+4-J~0QU+|0qcgwEy>9XQjZ(SzOeGD4d$MI=<xaIYOqK%^cWh(gf2
zr_|eHmad6!<jXP9mf33>wzijx&IEQR+!LSbp7(E9r>8Q+T@KX!N0u%Y2Sjht;6PAy
z?cg-vHkknK%;&TRI}mh-q%MDZRnwwtDpHac#?VM0p9NQeLkl<zK*K2z{E#$s$>|0T
zkc|z%ES7O(cVkR+CRn$T0D9QeBkP_8&>)KY%!Nwv{rqWHfO!P}?hRb`{El6aS$_Bp
zFA5vnPP@P9Ks0LrtKs|nwPhQZ`FGH(+tI2X69RelcZfPh_ho0j#R1cn(ZJ9~^OVlI
z)Ni`y>x*p$uClqDOVHb$;{#4D?^&Ye#rCm~#IPNVON|Lf1&)%1Pk#$z#MBrAm%5L3
zRK^^5n<t<m-+3<e;MKWKvdwxEe5W7=oG`rCMgmwoGY1>W*0mVi@M)0@+n`4GVWHqO
z5~vb;H{z#U9mrx=su6N>txZ}~CfhR;yq)0Xvgf}z0{Y~^(F^99-gz+Ng|nWN6!kDp
zK7f!6O*=#H%E7zm)kvuc_hRduRpEOJwxcAYzU23fe|twBfs5FS7j2qffhw!IP4GRZ
zx=m69SO#cdX*5LN+r|g#vdmlJ*y=J-<08+muILgwjZPXjtoL9<=#C-yU1-WM1kW~)
zj!LGR3TTN`rQfv6hM%;%k{+S}cJMvk2;$&o$e*_FIx45#R8ZEY#zJb*j>_Phf~Iao
zEyA@;xX}NRH2W8of6F?(%rV!D{1H=sT0LN;V~aB?TelGC-6nBT<3zA><*qhi!xwxl
z-fOXK1?*kyM!fkdha|bv=g6hot?x7lQ0X=_JZ>elC>3ee;wA~Y2_MgUUuFQ5(tpPH
z@3GW*>ST&5P9owq3B$5_3*nH^8^Mpmbtha7uU=|nf+neddcoYFy<eU@qiEq%`Ys>Y
zaitn1$za`%+AGPkRGnoK(H!CkFc3-xjb~(F&G}j3%uH4(n4o8#7zV=$827<0w5r`C
z<`CNV#e6+YIu8iN&%1jW@c2`_`7R=ca#r0JI=i$Hj%!25p}JWLRChvPxHZ2V!532j
zZoNPl6Dn%!x)0-R#l*@!U5VJBNR?@m7P(ilF!^&B;~WS~sY&Dl=w*WP2Hli0DHg!}
z?}7WhxzrH0d9t)MbcxF%76k;tyA5j`C5Cm&L4k*+*<p-g`(2AJr8McM+@0<~<Dqu}
zE0b)WC58B6$c&Ru`F)d<>46CUKTRUjgO?FCG+=-ZpEGR9igIMGpK@C;{t`?S>s8L{
z$~lym*$%p;+R~y+l_d_yN%#6Nc{`#Fy`c`YR^L2Y)o((uXl+NA;mMzx0S>|eOVS8c
z(@FJZzD{Jbt=x|V(pwFB|5XP2HT@FQ{D1{gv5YZS<}9h$eJ$wRsU0BA${GhvDc47+
zRfAo*tlQ<J(Ttif#!v7OLv{&Gn*aHgrVxh(&_+IwUBFcH2Z<OW6AuuQMfs`Y2a`;~
zyr@Su>8le=^-au1>+{_lkNE{qS2o5vVEw*Hw`H*74j`(g)6mSjI8C0v48s{#J&Mc(
z;HSYyfuV|EECcIOOK_=Ez43hp8X#mn-aIM0S$vt>*DNk=A3<Gk#MlMW=4HRp8xm~8
zU)63Q$cU5SqLYJnX>I7TWcu5AOY#>`Dxy5r{W8%6#;%duOo9#cF*0C3iu8G>{32Y^
z5Xv~$h&>858iFrv1E33;FeOZCfzWeW?YvpuGC3mFhH6_jK6`0=qAlPD-gm**cv}3S
z;2G2taBafG`H!0r=%cJ(`N_?m{hDR42hpG6V}C5eBkL>e{4XT*1w+{wUc(RnEnmf@
zgWjQfw#RQd#o0tC6p-dZpsa7QHn4G%+_3o^sk%{YNgA787_YJO&jIJwnw)XUvK8vt
zAeZp~f<_p_kKHRH^Ok|D#AoBjA6I*)o)<6p4`&RS5r%aD;j6=`0f~pj4R-umnpAE7
zaO%fylsJRkJ58=?aRAcD$l745yjeTR?Q2qV+DG0CMra<4U2q8~GW{o<ALA0pd_As>
zF#ulxyLL-bia&<IdZ{_*qly;7b@6IGk+L+fl_Ih!HDQI7ANjm7vWjcdXv5&zUSYd(
zj6oF!!<h*nqt1T^2$>Wd5Wl~2qd4g?aw{o#VNFa%C5mrYy`1h>mqz!iM~%tNeIa)E
z8L$q0gq1;@r1d&To<sR$e~Pa-k9_BcY*}ms*Ft5x=snH&gx3$K0*qq`)LH<|!21S0
zX%rm!d>L%O%VS;Qy8@e*X!S#&=%%73nP&^rrk*1(VS^dL@EH07hz9(hf&{;?*wXKW
zNsL7JcbJ@S_5oBg;nDHXR;aW|J)wTex;uDd>3P0KIZzdqpT97yeMAR*;8Iore`na7
z#-zbveA<y|Fh5Tp0h<o!mWZE=G>>8|NyWTSlJ8X$0wyf5n=XPL*8IfgCslk*7!W1V
z8}PA)PM8F)0<Igo$Qdc6Q)Ki%O|aA~=L01sb*Pb`pK&_?g#_J#%Y!O$V#XWX4km$=
zJ;DS<ueKf^{nC9RcN)DU2Dd?_JH7RN$$DG@eGgn>$V#KferO-5&1%R+zozA}ne4e#
z80_8mtDW~}azsm4#pIZ!XO*hs+Lumk0SNw%>ll~89lv3|8{CnL2@33Bfs&_c>H;?5
z_4zQ$%{jlfURAdAQn9zrEozE9wBR3l38b!q^_-V|u1>)-M9-i)9Kl3LpbsQ;=VBqk
zYliT75u|d9Q1=@Xz0}5S#ZI{eP}DtU1bP*n8ZffI1b8Yx;EahLbc;_9DZ{6SBKYhN
zCetC43stxqUsDvO)X>JJgQSY3h5WGcm#o+2-%3C2x*bLEV@&?a-Nxhx=Sr6vdf^|F
z^e{cx@$l<dovAj4+nV$?zkTF9ouW+-K={*4AVv!R4Itn<AqH#6RtDdbvRdBXF=<Nh
zW2ib7iU85td91Tc)5yWqe8frHF`zk0UY_SM^($c6vM74P{7M`8qlC+15WsU*5MN3k
zNjQ88!B^{DF5NypYTYoA-9l6wIU!T0$R+M8z2LY*fo1qZL8`|lQS>J!ac$@y{DlDI
zaH4<KWWlxyfE{qhDBT9Ori@H1-V<xwrzrqD5n5y8u$6%)P`{g;SqVsVQ*1igcfcem
zJ%k#-0Es$2{Spx3y;aUhm!cb$z2dJauT9%WdKdolC9r@hVA=6^{B|gqdJL?M0_l0q
z1$^D`pV@0zmsV8-AT}G1b-j<Si{AH9iw`K%DpfbvWgfOh0lvA&sd>$#=9dGf0UM@<
z{_-4W(O+5E&u;byF#^RGyQxTX$H^Y~u4@|Yeb?t}eb1If(ra07d2a<uKenalKnXv%
zS^B<Lx;Rn_Fd(~NUXU(jf@{ct0QrT|o9=Vanpg9PXIczQX=CYK2^26PYV7y~dKbpE
z1nXJ~=xw?gc<E|waC-74Q;Vcpk%gw2&4u%q14>`AP=i6a@D~fOucKZW)_Z10_%SMQ
ze)0tbI4K4*0s0K2W+$|DP~}#!_!Kwd1{pon-xc(MyYHh_Z6+~v2(Tc`kV<0GRX?CO
zy*)b~1~y0(II1XFaPfvu$dHSmm*;Z`<b`GDuP=lsAn<_+-s^<_C>=uUvH`U|EpTgt
ziP0_4-IQags;VTOA%Bq<R=x|~3lZuO+beb3qCw}^!xb6Lx0&zZ%UxSlATOY+2=APW
zwF~0V)ESW_sgz&($9mE@&7S)CQ>cx(wrc?C{|=uD%9Sd(Y0P(Un5({epMl~%yata6
za#tj@9#DESeLMlvgp;pz`eD66=}m7LC%1(>JM9Owf8i?U{hr0zi>(y(Jx>u5TKp{t
z2)NI`I%#9jP*4TvOm5a+7Z{Yg0yYY?ZowZDZc-d|n$%gB)!Z-L_}mx^VVLao9Y|Ji
z83e=^^8@$IKOo-R!c{RKK2MoC%^A5lZ)^J`?@@Vftw)sRKANDREob_%%Q(iVE00eo
z-N6_z$j>=H8yk5+XyEy@@Nn_3C7a=08EpH)NIwQi+;FGr2z(8@wr$u8o6(FC+go6v
z2tb-<-?1IEwPH=Nnpa6YMgB1_tvp8%;U|9s##TkJzu3^d3=e}vf~jj+l-PHLYDLki
zt~T$LUf25`hq_Z3?H8?S#up0a++i?tG|1oMmsCvkVlSVZq<*pEF7M`igx>Y)g^#)a
z5GefJK7Vmgk5mZ3c=R|pzR2=z-}*Z~M|`hFF}h4}dI{i2%(RtfG=(wW1W)Hm-GJO!
z2f#NmsfIpi8G$}%A^o)Q#2CMF@GjP{#=iLTf#|Ki4=gPDYk`4=#i~)92@^K8I2Qsu
zI{-ZEAB&;1D_Wd%Z}c0#gcJw0$T))DHSAK7ggeBrm838_z?24!vT90i&(hkUade2O
z8&J$}nOjLlUd4IP-zIYDppoP@1|SL`^ZXeT!MTn2qmaB&xqmR0_|SLcUEhu3{Y$qi
zRZr*cMPyXkGp9n>C?U^}Jzu&CkR*vwY+Ikw4YUNi*>eT#!NYCi$4v`lWc=iH->Yu;
zqh*npkU`K<PgsU^Y&*UkrVQUa@^<(xHiJF#2b8uA_=+_}NqoYM;-w6Vv(mD`-Pz09
z|2LDL=v^!Yug-51{*dq)V%Uj)A5HbIwc{IxOc<w4l3}p@`!ASJrIpz?rl_AdEe;Kk
z-tc_95vboxP7kl}FCYG*Bq{i7#-E_`+Y#i);Z*++my%UeWGEl&Kt9He&O&loUR#>g
ze39Yf<C|l0irxh^@vGWj(1J${uKgM?9p6ctJStS+9yYojUvb1;byrk-R<~E=0{Q9Y
zg{Wn8Vcbd_+yKWoxHzZaho*n!ZpSu!RE@pT+AGs1|6|D5|1eFjck%Dglxmq9e@AP1
zapz^-{1Kn5-N9ve`t$j-Wr@HC{L0Z=*P&Db@kTwc@C)fiv2G0Mq~BUXgLgORK;i6}
zkt^lB2u~kx4gCcxjE{ge7Uqu_fcv~p$3uG;MQLidy5rTSQ&V%ccI_EeDDi@Anmipo
z)ZoN;S&!xN-U4>h+QcV^mk|s5&y46zF4=(Uk(E|UOX5;XvTnfDm7D>($&bC9&b86V
z>L1Z_TwO|{PVmLKVAS|;N-gwXrL+VBl>)%>Y&K+(3Wz~jY)(2|>hpu|Rn{ZkU)n+7
zz-+=DZM7wyJj#iW)PJ)D+zeV*I}aS{OMXPPN4|fnBTp4JyB}@~D8tdKSVI8Rz_-v8
z{2Y+F5N;CaL#!0OUC6||C7=oopbAX9%1dQGt$VA=Y_8_p+_RNHdQmglV8w{V?T`;t
zfsr1P%%#mMy0{!;d?D$lPyXKc{@2IcTgZ7h*T!Ktv$uEQuw8q@v8w$*@<WW=*rWiP
z@rN)}G=u7|^rC_RchMVM0yRM7ViHvW*GRg6o35thyu8~WIrs`LT!X(Z154|<aFX^N
zEi1;4F}oE8Q#yd^PX!t^omRghg@Wpb6ql6bvLFrwPN7B(>;J%J?JjO<s&Y7J`#$I)
zkdCUSoB!UY?$fl3NyR!Z?6lC_45ZV}zhFKe-xl<&R;%ce7ZQ?p0F-P8C~=dH`0qw8
zK0t0JO_cZ?>wg<03K<N)j&WxvCyOh?aS05LJ@X^j{v^;cRUGJZ`-u@nKR9;o@|hrT
zDGiEfJarV3y(Y1o8}#3$imsMbcO4GK=?z!|0~nGzfy-%K!h${X_dX;`(9~3Q#4bC_
zxi(TpoJL>spGp9gm45&d@XAQIP^+ss`~EGjuh*;ie#uJ~04)mu$%mMH*UG@)LkIt?
z8JT;`B~8CS54m$s?+%X(k1_YP+ypr@3G(!oVdeYIBsCp9VQKdF;Rh!Crh`)7T?T|U
z!SF#Lcz1urd@<<co0u%7V;k-$-p~NXmr|0$^>~xtLI9K^J=Q57%z}h+RqoSUIU{W8
z^_RxsfM|wz_k+t~=#MNc2$6$9g^lE+WeI?{-eNdRNG?>gu5un&(MNv1m1>nFCfZN@
z;e&z*1Kg(mOh5u&vV`^xZAx^?o^)?Wi)n6BJnaNGSPCFFgQEDkWNxckF^%cvEOZ8a
z%{a2NF}~`^uEyV>?ClQjPo<(0!L>9td60%u{8fKbYXj2gz%`D&)gl(@s_gYCX8f&f
z6<xPwpz%58R=ypq??5NEfliG61q~$zl*$NAX<N(Xj4WNNB&IIJJEns+2cd|sW4=k&
z&;Z=BBLrjijZKM1ISFScEL?!HnX&?T=}3sty-}3;*Q6%O!8Uq1Xx{Gx3wu9G4-SpT
zM-Jwp-YjhU%_FAShe23Z2*$e-+}TLu3}&)Uv4(GDMVgp+N^=}<@L_P_KEz@)xZrUT
zz5gm0@GD!WDzOS2A#wI{M%0rVc_8mlm%E5|V;5?>K&`~N^j^BElXZsw7Bf+<PdgH%
zeS2S$jOc(=)>AW^XZvl+CsXU>B<WI}{Jb=^-UH0|G3-zxJ6zQ_pYlHe89@ONpie8U
zlDhn_>Nh4op`iwXB5-Z=+&a`~&}G-!$exs9G&~U#64;T@`vJCqa<dQU4pHLNBNM6G
z0apsSi<cuEsoO>;y=t&wkjApIA-E&Y9Bs8B&eQDMrV4u9zWf-F?pGZTEt5y8!F&*1
zX$_w$Y;_NZZlF;kHheo_WjI_CREW!Eylg1r)THp8F(Eg<#=>B<;+*qt3ZV@}tysZ)
zpO2khoVUdpR@qah{t8g0>YT6FOr$iAxujyQ;r`xX0DYvGeRdLQMw(#y1p4Mc#R|Ta
zI7UN_W@S0Mmqfwi=`W&|iqOlF-i*Uwdyr7AIyoT~i{o2$KxkW#pM$vEfwr1*XUi{>
z=0F<36jF62OruquCU}eBjs$(rbc11H$yO;#9gwpUtF5HQKYW6~kY#lxzNCfGU)H^r
zK{R^^6URS;sRUPK$_JL6-H5O7{O6;Q$~xDDJC3-H1f5_W3n9c7;GPf<a_HZYpbn>=
z_yAuapo43T3~-Hq#BZWZ$+FxdyuRX@(v0!xL7|b)O$*E|2xpH9EKQS|N8-{$Kh(P0
z3vzHiPe3)&b%vUe8nN0ttZjV5V0e^(vU%}6L<5GyyRku)AY2c|tRB0PouJO<Y(<H9
zd#H+wjZw`-;KJ!pq2)j8mubd76dD3D#_`lz_J=C7!NA(i$HZwBWuRLI1a9P^jLO^P
z`{B=n5~RU)SL*axEF-jGL}vo0Ovr7a9~wX(S(!uQDfN8J`}g{Nd7xKxz~mQQ_N51E
zBiqo{b*ZloQ$rKCoU-O8J44ozA#3_Sd3F-dhE2(e+@rvak(y;Y&e&R=`l=J4YCJ1z
zGOmvqn)H08kf^V;161jEJ1$avv_4=EH<tPY{|ygbS1>#lA`Xb;ZZ#$!y~Q^zGRD(8
zJ|yx;$HS0?>WTT8p&yFtdvc_f^+3wl=sOGqWzBCUU{VH~<1rbwz>f0b>^gQc4=E?M
zFV-Q7&1Pp1tzSr}?Prf;Q-rgiJ{F>6bu*$O)c8a?8YonnafE04-0oarrgJKgN$mN2
zxN9xp1KUY<j358vP(XhM8mPs6f#t(4l3oPI#~VatH+&7@pKUwt;@PoGkHwQ26=0AY
z0plFw4o15x$2_>!L$CB54ov2Q`$3i|UJt)LH@B7BcGz3=JN>9k-$|}KEIO7PIcUfR
za4`Md7*DqTPSyW6UvwV^2OCzDd?x*|(}Q7B=fQ||b&<#sQPbxAv*~C!k(veAqc;Y$
z-}RjHt4&ToV!h#HRgUXRpY`#b;nWa@HGjuVy+j~<ir;f<TX_yp;yH~e-PaJq8;SAV
zGYBxsVi-*cN3Hoz{z~yKk9Dx)sUGWg9=iZ^MxeF-9g8jfeQW2Ua=|(pPRB!#%fv6S
zphtyzp7>za3yyCKwC3LrwkGlcu1mjW_%&?DGAzLjeEawZRC|Ctz72}wX=BpSs>j2Q
zl7XuEbQ`Gk(5h#w6dP!njeePL2gao&Czz5p3MISu8G^j5%M34|q`_e4oL4*3zTdzz
zK1t1*v{}UitB%O!VCKx#i+%+fC7N)E0mhp<<o`KGC>cQ!(z@4%Y`{Ldjn=V?rsq8v
zV^#=>ehNa%YCaC2qG}(ZI#{m(3STAum60R6*RWAjhHUvg=$kPFP_=v8%0Cl)!j`Aw
z4nfGXndn83SAPe<7Y}d`GtxIN82>*W8Zg6?w}x8-L21pe^BZx40^NNh9Ss_<gSE}(
zuXKQQ+uO^n0ab6uF6?H5@mE~X*e(QtOH~r+`{auk%#Yq*1s~6-@?d=H3jN{qko<io
z8+WvCcuxt|ko|i4pBVfu(5?LoOd=rt<=X-3g*X)Ym$K;g?09%bk4!)vB13jCN9ZVd
z-|6fEn4^rd${BV}4KjKRWHcPi<EUW+qLkLy4xroe?#@fLpxt~6VIDyEJ?RNeU+(Oj
z^M_4N^q7I(G{KX<7GoflxQ^Y@spz1!4F336FF+P^8soc9bqqV}t%0bVBmfREz0f!A
z*<KcSEA$Pq7VBOg=_Xf)OxI^kH_cgAh>W45FM=L&%4QR~XRZ_CyCGAn|CgU9p%3~n
zPQZWAFn4o3p~acD<MvjBNt)+XJw2(9n&4XzCEvpbQX{$zsNyH#xmM`fzyC55k{a{L
zloNni4#wrw-f{V6LS*v0jDnmMumNPrTDI;>2{KFlfL8mDW1_7qG|z5bhjdlCkcBm@
zbsX2zP8~|w1nsgaS;ZDv%nHCQIdto6DCg`?-F?~W(JGeqDGmC%G4zSj*@5hg3dsg+
z!vsACDB6cSL*XABb8wf=0j1>+Z#RJLX^j9KUb|ez=bSy+O|hK4f=x{YGv#luCmyHP
zd=n6wi0`Dq+tF^4c0{%rVS?tF9YK<U3h^DR`e(8?SGO1dxZboIPsJU*rL08g!n-fF
zFTVa?(ez%_-d_*zu-`&Z(>!Dg2<=A?Wmx&<{#uPBrNwIPbp31Qtcw+t#EtmjX=O0h
z-BIGtsaa6I$^?Y2(rcLn_bLDMfUi(A%sxpMg8b@m8NN+Ow<&#DkK@dxs5mC6g#y6%
z#kL*{+wP!nO(ufgU=n4uWfqPdQr1t9fM{r$H5*_WR+i+zpEfjA`PHFB8Fguls5sCN
zAr7yh*Pu`KLSNQH6|jbs_Yj8!9FB2p6zOjcUkka=e;@rb@4oOAc+1+_KL)bB4f>-H
z)EB1Yj=PlOCwMhO%6x24vSz$}PH#D{_q>*|8Qz_T5@)bbBMn#ag_gt^bR|E|YhoE}
zPicflbRkLry~=w{kLri4Ngt&8=g|Al1XrTE9ovEhCD1uMV8q+^0(!x7AHBvASEE(x
zlUy=pwtrVfRY`7b$xniU<&25^EJz&jk6BY|y&m$6<D&A5VydEFeHQSzTxN#LpW)4+
zuP3a5Jt!zy%`VqH?U9+q4z{G9Kbcwm;m4J*;~~E3!ZTWf;xlUu2F=ct4lM^@vHd!_
z|Aj7T0Q-6^kn?%&(6<ZMnOTGC@7eP2^WW#c&;RT5rq0T%XtHou*-PlFq_D$>ybm%w
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)