Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 30 Aug 2017 19:52:39 -0700
changeset 377828 04b6be50a2526c7a26a63715f441c47e1aa1f9be
parent 377695 d9b405d82cffb07343a5f2fd941e029298c7f6c4 (current diff)
parent 377827 9182a041d8891cc61efb3e2cbe35397b3f981364 (diff)
child 377829 9ca18987dabb23b5b2a581af46e3c6969c97ac69
child 377851 a81e995c2a6a06447745f22f60fab62ca88f2eb5
push id94338
push userkwierso@gmail.com
push dateThu, 31 Aug 2017 02:58:58 +0000
treeherdermozilla-inbound@9ca18987dabb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone57.0a1
first release with
nightly linux32
04b6be50a252 / 57.0a1 / 20170831100258 / files
nightly linux64
04b6be50a252 / 57.0a1 / 20170831100258 / files
nightly mac
04b6be50a252 / 57.0a1 / 20170831100258 / files
nightly win32
04b6be50a252 / 57.0a1 / 20170831100258 / files
nightly win64
04b6be50a252 / 57.0a1 / 20170831100258 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to central, a=merge MozReview-Commit-ID: Jz9iBkuBrpV
browser/app/profile/firefox.js
browser/components/places/tests/browser/browser_410196_paste_into_tags.js
browser/components/places/tests/browser/browser_416459_cut.js
browser/components/places/tests/browser/browser_423515.js
browser/components/places/tests/browser/browser_425884.js
browser/components/places/tests/browser/browser_435851_copy_query.js
browser/components/places/tests/browser/browser_475045.js
browser/components/places/tests/browser/browser_555547.js
browser/extensions/onboarding/content/img/overlay-icon.svg
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_network_messages_click.js
dom/console/Console.cpp
dom/media/hls/HLSResource.cpp
dom/media/hls/HLSResource.h
memory/build/jemalloc_config.cpp
servo/components/style_derive/has_viewport_percentage.rs
servo/tests/unit/style/properties/viewport.rs
testing/runtimes/writeruntimes.py
--- a/accessible/base/MarkupMap.h
+++ b/accessible/base/MarkupMap.h
@@ -59,22 +59,22 @@ MARKUPMAP(figure,
           roles::FIGURE,
           Attr(xmlroles, figure))
 
 MARKUPMAP(form,
           New_HyperText,
           roles::FORM)
 
 MARKUPMAP(footer,
-          New_HyperText,
-          roles::FOOTER)
+          New_HTMLHeaderOrFooter,
+          0)
 
 MARKUPMAP(header,
-          New_HyperText,
-          roles::HEADER)
+          New_HTMLHeaderOrFooter,
+          0)
 
 MARKUPMAP(h1,
           New_HyperText,
           roles::HEADING)
 
 MARKUPMAP(h2,
           New_HyperText,
           roles::HEADING)
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -156,16 +156,19 @@ static Accessible* New_HyperText(nsICont
   { return new HyperTextAccessibleWrap(aContent, aContext->Document()); }
 
 static Accessible* New_HTMLFigcaption(nsIContent* aContent, Accessible* aContext)
   { return new HTMLFigcaptionAccessible(aContent, aContext->Document()); }
 
 static Accessible* New_HTMLFigure(nsIContent* aContent, Accessible* aContext)
   { return new HTMLFigureAccessible(aContent, aContext->Document()); }
 
+static Accessible* New_HTMLHeaderOrFooter(nsIContent* aContent, Accessible* aContext)
+  { return new HTMLHeaderOrFooterAccessible(aContent, aContext->Document()); }
+
 static Accessible* New_HTMLLegend(nsIContent* aContent, Accessible* aContext)
   { return new HTMLLegendAccessible(aContent, aContext->Document()); }
 
 static Accessible* New_HTMLOption(nsIContent* aContent, Accessible* aContext)
   { return new HTMLSelectOptionAccessible(aContent, aContext->Document()); }
 
 static Accessible* New_HTMLOptgroup(nsIContent* aContent, Accessible* aContext)
   { return new HTMLSelectOptGroupAccessible(aContent, aContext->Document()); }
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -1137,41 +1137,16 @@ HyperTextAccessible::LandmarkRole() cons
     return nullptr;
 
   // For the html landmark elements we expose them like we do ARIA landmarks to
   // make AT navigation schemes "just work".
   if (mContent->IsHTMLElement(nsGkAtoms::nav)) {
     return nsGkAtoms::navigation;
   }
 
-  if (mContent->IsAnyOfHTMLElements(nsGkAtoms::header,
-                                    nsGkAtoms::footer)) {
-    // Only map header and footer if they are not descendants of an article
-    // or section tag.
-    nsIContent* parent = mContent->GetParent();
-    while (parent) {
-      if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::section)) {
-        break;
-      }
-      parent = parent->GetParent();
-    }
-
-    // No article or section elements found.
-    if (!parent) {
-      if (mContent->IsHTMLElement(nsGkAtoms::header)) {
-        return nsGkAtoms::banner;
-      }
-
-      if (mContent->IsHTMLElement(nsGkAtoms::footer)) {
-        return nsGkAtoms::contentinfo;
-      }
-    }
-    return nullptr;
-  }
-
   if (mContent->IsHTMLElement(nsGkAtoms::aside)) {
     return nsGkAtoms::complementary;
   }
 
   if (mContent->IsHTMLElement(nsGkAtoms::main)) {
     return nsGkAtoms::main;
   }
 
--- a/accessible/html/HTMLElementAccessibles.cpp
+++ b/accessible/html/HTMLElementAccessibles.cpp
@@ -197,8 +197,64 @@ HTMLSummaryAccessible::NativeState()
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLSummaryAccessible: Widgets
 
 bool
 HTMLSummaryAccessible::IsWidget() const
 {
   return true;
 }
+
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLHeaderOrFooterAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS_INHERITED0(HTMLHeaderOrFooterAccessible, HyperTextAccessible)
+
+role
+HTMLHeaderOrFooterAccessible::NativeRole()
+{
+  // Only map header and footer if they are direct descendants of the body tag.
+  // If other sectioning or sectioning root elements, they become sections.
+  nsIContent* parent = mContent->GetParent();
+  while (parent) {
+    if (parent->IsAnyOfHTMLElements(nsGkAtoms::article, nsGkAtoms::aside,
+                             nsGkAtoms::nav, nsGkAtoms::section,
+                             nsGkAtoms::blockquote, nsGkAtoms::details,
+                             nsGkAtoms::dialog, nsGkAtoms::fieldset,
+                             nsGkAtoms::figure, nsGkAtoms::td)) {
+      break;
+    }
+    parent = parent->GetParent();
+  }
+
+  // No sectioning or sectioning root elements found.
+  if (!parent) {
+    if (mContent->IsHTMLElement(nsGkAtoms::header)) {
+      return roles::HEADER;
+    }
+
+    if (mContent->IsHTMLElement(nsGkAtoms::footer)) {
+      return roles::FOOTER;
+    }
+  }
+
+  return roles::SECTION;
+}
+
+nsIAtom*
+HTMLHeaderOrFooterAccessible::LandmarkRole() const
+{
+  if (!HasOwnContent())
+    return nullptr;
+
+  a11y::role r = const_cast<HTMLHeaderOrFooterAccessible*>(this)->Role();
+  if (r == roles::HEADER) {
+    return nsGkAtoms::banner;
+  }
+
+  if (r == roles::FOOTER) {
+    return nsGkAtoms::contentinfo;
+  }
+
+  return nullptr;
+}
--- a/accessible/html/HTMLElementAccessibles.h
+++ b/accessible/html/HTMLElementAccessibles.h
@@ -109,12 +109,32 @@ public:
   virtual uint8_t ActionCount() override;
   virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
   virtual bool DoAction(uint8_t aIndex) override;
 
   // Widgets
   virtual bool IsWidget() const override;
 };
 
+/**
+ * Used for HTML header and footer elements.
+ */
+class HTMLHeaderOrFooterAccessible : public HyperTextAccessibleWrap
+{
+public:
+
+  HTMLHeaderOrFooterAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+    HyperTextAccessibleWrap(aContent, aDoc) {}
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  // Accessible
+  virtual nsIAtom* LandmarkRole() const override;
+  virtual a11y::role NativeRole() override;
+
+protected:
+  virtual ~HTMLHeaderOrFooterAccessible() {}
+};
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -519,22 +519,30 @@
       obj = {
         role: ROLE_FOOTER,
         attributes: { "xml-roles": "contentinfo" },
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
       };
       testElm("footer", obj);
 
       obj = {
-        role: ROLE_FOOTER,
+        role: ROLE_SECTION,
         absentAttributes: { "xml-roles": "contentinfo" },
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
       };
       testElm("footer_in_article", obj);
+      testElm("footer_in_aside", obj);
+      testElm("footer_in_nav", obj);
       testElm("footer_in_section", obj);
+      testElm("footer_in_blockquote", obj);
+      testElm("footer_in_details", obj);
+      testElm("footer_in_dialog", obj);
+      testElm("footer_in_fieldset", obj);
+      testElm("footer_in_figure", obj);
+      testElm("footer_in_td", obj);
 
       // ////////////////////////////////////////////////////////////////////////
       // HTML:form
 
       obj = {
         role: ROLE_FORM
       };
       testElm("form", obj);
@@ -604,22 +612,30 @@
       obj = {
         role: ROLE_HEADER,
         attributes: { "xml-roles": "banner" },
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
       };
       testElm("header", obj);
 
       obj = {
-        role: ROLE_HEADER,
+        role: ROLE_SECTION,
         absentAttributes: { "xml-roles": "banner" },
         interfaces: [ nsIAccessibleText, nsIAccessibleHyperText ]
       };
       testElm("header_in_article", obj);
+      testElm("header_in_aside", obj);
+      testElm("header_in_nav", obj);
       testElm("header_in_section", obj);
+      testElm("header_in_blockquote", obj);
+      testElm("header_in_details", obj);
+      testElm("header_in_dialog", obj);
+      testElm("header_in_fieldset", obj);
+      testElm("header_in_figure", obj);
+      testElm("header_in_td", obj);
 
       // ////////////////////////////////////////////////////////////////////////
       // HTML:hr
 
       obj = {
         role: ROLE_SEPARATOR,
       };
       testElm("hr", obj);
@@ -1466,19 +1482,43 @@
     <img src="../moz.png" alt="An awesome picture">
     <figcaption id="figcaption">Caption for the awesome picture</figcaption>
   </figure>
 
   <footer id="footer">Some copyright info</footer>
   <article>
     <footer id="footer_in_article">Some copyright info</footer>
   </article>
+  <aside>
+    <footer id="footer_in_aside">Some copyright info</footer>
+  </aside>
+  <nav>
+    <footer id="footer_in_nav">Some copyright info</footer>
+  </nav>
   <section>
     <footer id="footer_in_section">Some copyright info</footer>
   </section>
+  <blockquote>
+    <footer id="footer_in_blockquote">Some copyright info</footer>
+  </blockquote>
+  <details open="true">
+    <footer id="footer_in_details">Some copyright info</footer>
+  </details>
+  <dialog open="true">
+    <footer id="footer_in_dialog">Some copyright info</footer>
+  </dialog>
+  <fieldset>
+    <footer id="footer_in_fieldset">Some copyright info</footer>
+  </fieldset>
+  <figure>
+    <footer id="footer_in_figure">Some copyright info</footer>
+  </figure>
+  <table><tr><td>
+    <footer id="footer_in_td">Some copyright info</footer>
+  </td></tr></table>
 
   <form id="form"></form>
 
   <iframe id="frameset_container"
           src="data:text/html,<html><frameset><frame src='data:text/html,hi'></frame></frameset></html>">
   </iframe>
 
   <h1 id="h1">heading1</h1>
@@ -1487,19 +1527,43 @@
   <h4 id="h4">heading4</h4>
   <h5 id="h5">heading5</h5>
   <h6 id="h6">heading6</h6>
 
   <header id="header">A logo</header>
   <article>
     <header id="header_in_article">Not logo</header>
   </article>
+  <aside>
+    <header id="header_in_aside">Not logo</header>
+  </aside>
+  <nav>
+    <header id="header_in_nav">Not logo</header>
+  </nav>
   <section>
     <header id="header_in_section">Not logo</header>
   </section>
+  <blockquote>
+    <header id="header_in_blockquote">Not logo</header>
+  </blockquote>
+  <details open="true">
+    <header id="header_in_details">Not logo</header>
+  </details>
+  <dialog open="true">
+    <header id="header_in_dialog">Not logo</header>
+  </dialog>
+  <fieldset>
+    <header id="header_in_fieldset">Not logo</header>
+  </fieldset>
+  <figure>
+    <header id="header_in_figure">Not logo</header>
+  </figure>
+  <table><tr><td>
+    <header id="header_in_td">Not logo</header>
+  </td></tr></table>
 
   <hr id="hr">
   <p id="i_container">normal<i>italic</i></p>
   <img id="img" src="../moz.png">
 
   <input id="input_button" type="button" value="Button">
   <input id="input_checkbox" type="checkbox">
   <input id="input_checkbox_true" type="checkbox" checked>
--- a/accessible/tests/mochitest/jsat/test_landmarks.html
+++ b/accessible/tests/mochitest/jsat/test_landmarks.html
@@ -42,44 +42,44 @@
           [{"string": "contentinfo"}, {"string": "footer"}, "a footer"],
           ["a footer", {"string": "footer"}, {"string": "contentinfo"}]],
         expectedBraille: [
           [{"string": "contentinfo"}, {"string": "footerAbbr"}, "a footer"],
           ["a footer", {"string": "footerAbbr"}, {"string": "contentinfo"}]]
       }, {
         accOrElmOrID: "article_header",
         expectedUtterance: [
-          [{"string": "header"}, "a header within an article"],
-          ["a header within an article", {"string": "header"}]],
+          ["a header within an article"],
+          ["a header within an article"]],
         expectedBraille: [
-          [{"string": "headerAbbr"}, "a header within an article"],
-          ["a header within an article", {"string": "headerAbbr"}]],
+          ["a header within an article"],
+          ["a header within an article"]],
       }, {
         accOrElmOrID: "article_footer",
         expectedUtterance: [
-          [{"string": "footer"}, "a footer within an article"],
-          ["a footer within an article", {"string": "footer"}]],
+          ["a footer within an article"],
+          ["a footer within an article"]],
         expectedBraille: [
-          [{"string": "footerAbbr"}, "a footer within an article"],
-          ["a footer within an article", {"string": "footerAbbr"}]]
+          ["a footer within an article"],
+          ["a footer within an article"]]
       }, {
         accOrElmOrID: "section_header",
-        expectedUtterance: [[{"string": "header"}, "a header within a section"],
-                            ["a header within a section", {"string": "header"}]],
+        expectedUtterance: [["a header within a section"],
+                            ["a header within a section"]],
         expectedBraille: [
-          [{"string": "headerAbbr"}, "a header within a section"],
-          ["a header within a section", {"string": "headerAbbr"}]]
+          ["a header within a section"],
+          ["a header within a section"]]
       }, {
         accOrElmOrID: "section_footer",
         expectedUtterance: [
-          [{"string": "footer"}, "a footer within a section"],
-          ["a footer within a section", {"string": "footer"}]],
+          ["a footer within a section"],
+          ["a footer within a section"]],
         expectedBraille: [
-          [{"string": "footerAbbr"}, "a footer within a section"],
-          ["a footer within a section", {"string": "footerAbbr"}]]
+          ["a footer within a section"],
+          ["a footer within a section"]]
       }, {
         accOrElmOrID: "aside",
         expectedUtterance: [
           [{"string": "complementary"}, "by the way I am an aside"],
           ["by the way I am an aside", {"string": "complementary"}]],
         expectedBraille: [
           [{"string": "complementary"}, "by the way I am an aside"],
           ["by the way I am an aside", {"string": "complementary"}]]
--- a/accessible/tests/mochitest/role/test_general.html
+++ b/accessible/tests/mochitest/role/test_general.html
@@ -27,19 +27,19 @@
       testRole("article", ROLE_ARTICLE);
       testRole("aside", ROLE_NOTE);
       testRole("section", ROLE_SECTION);
 
       // Bug 996821
       // Check that landmark elements get accessibles with styled overflow.
       testRole("section_overflow", ROLE_SECTION);
       testRole("nav_overflow", ROLE_SECTION);
-      testRole("header_overflow", ROLE_HEADER);
+      testRole("header_overflow", ROLE_SECTION);
       testRole("aside_overflow", ROLE_NOTE);
-      testRole("footer_overflow", ROLE_FOOTER);
+      testRole("footer_overflow", ROLE_SECTION);
       testRole("article_overflow", ROLE_ARTICLE);
 
       // test html:div
       testRole("sec", ROLE_SECTION);
 
       // Test html:blockquote
       testRole("quote", ROLE_SECTION);
 
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -455,16 +455,17 @@ pref("browser.tabs.insertRelatedAfterCur
 pref("browser.tabs.warnOnClose", true);
 pref("browser.tabs.warnOnCloseOtherTabs", true);
 pref("browser.tabs.warnOnOpen", true);
 pref("browser.tabs.maxOpenBeforeWarn", 15);
 pref("browser.tabs.loadInBackground", true);
 pref("browser.tabs.opentabfor.middleclick", true);
 pref("browser.tabs.loadDivertedInBackground", false);
 pref("browser.tabs.loadBookmarksInBackground", false);
+pref("browser.tabs.loadBookmarksInTabs", false);
 pref("browser.tabs.tabClipWidth", 140);
 #ifdef UNIX_BUT_NOT_MAC
 pref("browser.tabs.drawInTitlebar", false);
 #else
 pref("browser.tabs.drawInTitlebar", true);
 #endif
 
 // 0 - Disable the tabbar session restore button.
--- a/browser/base/content/aboutDialog-appUpdater.js
+++ b/browser/base/content/aboutDialog-appUpdater.js
@@ -22,27 +22,28 @@ function onUnload(aEvent) {
   if (gAppUpdater.isChecking)
     gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
   // Safe to call even when there isn't a download in progress.
   gAppUpdater.removeDownloadListener();
   gAppUpdater = null;
 }
 
 
-function appUpdater() {
+function appUpdater(options = {}) {
   XPCOMUtils.defineLazyServiceGetter(this, "aus",
                                      "@mozilla.org/updates/update-service;1",
                                      "nsIApplicationUpdateService");
   XPCOMUtils.defineLazyServiceGetter(this, "checker",
                                      "@mozilla.org/updates/update-checker;1",
                                      "nsIUpdateChecker");
   XPCOMUtils.defineLazyServiceGetter(this, "um",
                                      "@mozilla.org/updates/update-manager;1",
                                      "nsIUpdateManager");
 
+  this.options = options;
   this.updateDeck = document.getElementById("updateDeck");
 
   // Hide the update deck when the update window is already open and it's not
   // already applied, to avoid syncing issues between them. Applied updates
   // don't have any information to sync between the windows as they both just
   // show the "Restart to continue"-type button.
   if (Services.wm.getMostRecentWindow("Update:Wizard") &&
       !this.isApplied) {
@@ -181,19 +182,25 @@ appUpdater.prototype =
           let year = buildID.slice(0, 4);
           let month = buildID.slice(4, 6);
           let day = buildID.slice(6, 8);
           updateVersion += ` (${year}-${month}-${day})`;
         }
         button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
         button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
       }
+      this.updateDeck.selectedPanel = panel;
+      if (this.options.buttonAutoFocus &&
+          (!document.commandDispatcher.focusedElement || // don't steal the focus
+           document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons
+        button.focus();
+      }
+    } else {
+      this.updateDeck.selectedPanel = panel;
     }
-
-    this.updateDeck.selectedPanel = panel;
   },
 
   /**
    * Check for updates
    */
   checkForUpdates() {
     // Clear prefs that could prevent a user from discovering available updates.
     if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_CANCELATIONS_OSX)) {
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -59,23 +59,17 @@ function init(aEvent) {
     let relNotesURL = Services.urlFormatter.formatURLPref("app.releaseNotesURL");
     if (relNotesURL != "about:blank") {
       relNotesLink.href = relNotesURL;
       relNotesLink.hidden = false;
     }
   }
 
   if (AppConstants.MOZ_UPDATER) {
-    gAppUpdater = new appUpdater();
-
-    let button = gAppUpdater.updateDeck.selectedPanel.querySelector("button");
-    if (button && (!document.commandDispatcher.focusedElement || // don't steal the focus
-                   document.commandDispatcher.focusedElement.localName == "button")) { // except from the other buttons
-      button.focus();
-    }
+    gAppUpdater = new appUpdater({ buttonAutoFocus: true });
 
     let channelLabel = document.getElementById("currentChannel");
     let currentChannelText = document.getElementById("currentChannelText");
     channelLabel.value = UpdateUtils.UpdateChannel;
     if (/^release($|\-)/.test(channelLabel.value))
         currentChannelText.hidden = true;
   }
 
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -662,16 +662,17 @@ html|input.urlbar-input[textoverflow]:no
 }
 
 #DateTimePickerPanel[active="true"] {
   -moz-binding: url("chrome://global/content/bindings/datetimepopup.xml#datetime-popup");
 }
 
 #urlbar[pageproxystate=invalid] > #page-action-buttons > .urlbar-page-action,
 #identity-box.chromeUI ~ #page-action-buttons > .urlbar-page-action,
+#urlbar[usertyping] > .urlbar-textbox-container > .urlbar-history-dropmarker,
 .urlbar-go-button[pageproxystate="valid"],
 .urlbar-go-button:not([parentfocused="true"]),
 #urlbar[pageproxystate="invalid"] > #identity-box > #blocked-permissions-container,
 #urlbar[pageproxystate="invalid"] > #identity-box > #notification-popup-box,
 #urlbar[pageproxystate="invalid"] > #identity-box > #identity-icon-labels {
   visibility: collapse;
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1374,20 +1374,16 @@ var gBrowserInit = {
         document.documentElement.setAttribute("darkwindowframe", "true");
       }
     }
 
     ToolbarIconColor.init();
 
     gRemoteControl.updateVisualCue(Marionette.running);
 
-    this._uriToLoadPromise.then(uriToLoad => {
-      gIdentityHandler.initIdentityBlock(uriToLoad);
-    });
-
     // Wait until chrome is painted before executing code not critical to making the window visible
     this._boundDelayedStartup = this._delayedStartup.bind(this);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
     this._loadHandled = true;
   },
 
   _cancelDelayedStartup() {
@@ -2770,16 +2766,17 @@ function URLBarSetURI(aURI) {
     }
 
     valid = !isBlankPageURL(uri.spec);
   }
 
   let isDifferentValidValue = valid && value != gURLBar.value;
   gURLBar.value = value;
   gURLBar.valueIsTyped = !valid;
+  gURLBar.removeAttribute("usertyping");
   if (isDifferentValidValue) {
     gURLBar.selectionStart = gURLBar.selectionEnd = 0;
   }
 
   SetPageProxyState(valid ? "valid" : "invalid");
 }
 
 function losslessDecodeURI(aURI) {
@@ -7077,17 +7074,17 @@ var gIdentityHandler = {
    * to be able to focus it on the popupshown event.
    */
   _popupTriggeredByKeyboard: false,
 
   /**
    * RegExp used to decide if an about url should be shown as being part of
    * the browser UI.
    */
-  _secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|home|license|newaddon|permissions|preferences|privatebrowsing|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i,
+  _secureInternalUIWhitelist: /^(?:accounts|addons|cache|config|crashes|customizing|downloads|healthreport|license|newaddon|permissions|preferences|rights|searchreset|sessionrestore|support|welcomeback)(?:[?#]|$)/i,
 
   get _isBroken() {
     return this._state & Ci.nsIWebProgressListener.STATE_IS_BROKEN;
   },
 
   get _isSecure() {
     // If a <browser> is included within a chrome document, then this._state
     // will refer to the security state for the <browser> and not the top level
@@ -7707,24 +7704,16 @@ var gIdentityHandler = {
     this._identityPopupContentSupp.textContent = supplemental;
     this._identityPopupContentVerif.textContent = verifier;
 
     // Update per-site permissions section.
     this.updateSitePermissions();
   },
 
   setURI(uri) {
-    // Ignore about:blank loads until the window's initial URL has loaded,
-    // to avoid hiding the UI that initIdentityBlock could have prepared.
-    if (this._ignoreAboutBlankUntilFirstLoad) {
-      if (uri.spec == "about:blank")
-        return;
-      this._ignoreAboutBlankUntilFirstLoad = false;
-    }
-
     this._uri = uri;
 
     try {
       this._uri.host;
       this._uriHasHost = true;
     } catch (ex) {
       this._uriHasHost = false;
     }
@@ -7749,40 +7738,16 @@ var gIdentityHandler = {
       // Check the URI again after resolving.
       this._isURILoadedFromFile = resolvedURI.schemeIs("file");
     } catch (ex) {
       // NetUtil's methods will throw for malformed URIs and the like
     }
   },
 
   /**
-   * Used to initialize the identity block before first paint to avoid
-   * flickering when opening a new window showing a secure internal page
-   * (eg. about:home)
-   */
-  initIdentityBlock(initialURI) {
-    if (this._uri) {
-      // Apparently we already loaded something, so there's nothing to do here.
-      return;
-    }
-
-    if ((typeof initialURI != "string") || !initialURI.startsWith("about:"))
-      return;
-
-    let uri = Services.io.newURI(initialURI);
-    if (this._secureInternalUIWhitelist.test(uri.pathQueryRef)) {
-      this._isSecureInternalUI = true;
-      this._ignoreAboutBlankUntilFirstLoad = true;
-      this.refreshIdentityBlock();
-      // The identity label won't be visible without setting this.
-      gURLBar.setAttribute("pageproxystate", "valid");
-    }
-  },
-
-  /**
    * Click handler for the identity-box element in primary chrome.
    */
   handleIdentityButtonEvent(event) {
     event.stopPropagation();
 
     if ((event.type == "click" && event.button != 0) ||
         (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
          event.keyCode != KeyEvent.DOM_VK_RETURN)) {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -724,16 +724,17 @@
                          aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
 
                 if (this.mTab.hasAttribute("busy")) {
                   this.mTab.removeAttribute("busy");
 
                   // Only animate the "burst" indicating the page has loaded if
                   // the top-level page is the one that finished loading.
                   if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument &&
+                      Components.isSuccessCode(aStatus) &&
                       !this.mTabBrowser.tabAnimationsInProgress &&
                       Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
                     this.mTab.setAttribute("bursting", "true");
                   }
 
                   this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
                   if (!this.mTab.selected)
                     this.mTab.setAttribute("unread", "true");
--- a/browser/base/content/test/about/browser_aboutHome.js
+++ b/browser/base/content/test/about/browser_aboutHome.js
@@ -523,28 +523,27 @@ add_task(async function() {
 
   // Skip this test on Mac, because Space doesn't activate the button there.
   if (AppConstants.platform == "macosx") {
     return;
   }
 
   await BrowserTestUtils.withNewTab({ gBrowser, url: "about:home" }, async function(browser) {
     info("Waiting for about:addons tab to open...");
-    let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
+    let promiseTabLoaded = BrowserTestUtils.browserLoaded(browser, false, "about:addons");
 
     await ContentTask.spawn(browser, null, async function() {
       let addOnsButton = content.document.getElementById("addons");
       addOnsButton.focus();
     });
     await BrowserTestUtils.synthesizeKey(" ", {}, browser);
 
-    let tab = await promiseTabOpened;
-    is(tab.linkedBrowser.currentURI.spec, "about:addons",
+    await promiseTabLoaded;
+    is(browser.currentURI.spec, "about:addons",
       "Should have seen the about:addons tab");
-    await BrowserTestUtils.removeTab(tab);
   });
 });
 
 /**
  * Cleans up snippets and ensures that by default we don't try to check for
  * remote snippets since that may cause network bustage or slowness.
  *
  * @param aSetupFn
--- a/browser/base/content/test/general/browser_bug882977.js
+++ b/browser/base/content/test/general/browser_bug882977.js
@@ -1,16 +1,16 @@
 "use strict";
 
 /**
  * Tests that the identity-box shows the chromeUI styling
- * when viewing about:home in a new window.
+ * when viewing such a page in a new window.
  */
 add_task(async function() {
-  let homepage = "about:home";
+  let homepage = "about:preferences";
   await SpecialPowers.pushPrefEnv({
     "set": [
       ["browser.startup.homepage", homepage],
       ["browser.startup.page", 1],
     ]
   });
 
   let win = OpenBrowserWindow();
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -124,18 +124,16 @@ var whitelist = [
   // These are used in content processes. They are actually referenced.
   {file: "resource://shield-recipe-client-content/shield-content-frame.js"},
   {file: "resource://shield-recipe-client-content/shield-content-process.js"},
 
   // New L10n API that is not yet used in production
   {file: "resource://gre/modules/Localization.jsm"},
 
   // Starting from here, files in the whitelist are bugs that need fixing.
-  // Bug 1339420
-  {file: "chrome://branding/content/icon128.png"},
   // Bug 1339424 (wontfix?)
   {file: "chrome://browser/locale/taskbar.properties",
    platforms: ["linux", "macosx"]},
   // Bug 1316187
   {file: "chrome://global/content/customizeToolbar.xul"},
   // Bug 1343837
   {file: "chrome://global/content/findUtils.js"},
   // Bug 1343843
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1295,16 +1295,21 @@ file, You can obtain one at http://mozil
 
       <method name="onInput">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (!this.mIgnoreInput && this.mController.input == this) {
             this._value = this.inputField.value;
             gBrowser.userTypedValue = this.value;
             this.valueIsTyped = true;
+            if (this.inputField.value) {
+              this.setAttribute("usertyping", "true");
+            } else {
+              this.removeAttribute("usertyping");
+            }
             // Only wait for a result when we are sure to get one.  In some
             // cases, like when pasting the same exact text, we may not fire
             // a new search and we won't get a result.
             if (this.mController.handleText()) {
               this.gotResultForCurrentQuery = false;
               this._searchStartDate = Date.now();
               this._deferredKeyEventQueue = [];
               if (this._deferredKeyEventTimeout) {
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -38,17 +38,19 @@ Object.defineProperty(this, "BROWSER_NEW
 var TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
 
 var gBidiUI = false;
 
 /**
  * Determines whether the given url is considered a special URL for new tabs.
  */
 function isBlankPageURL(aURL) {
-  return aURL == "about:blank" || aURL == BROWSER_NEW_TAB_URL;
+  return aURL == "about:blank" ||
+         aURL == "about:home" ||
+         aURL == BROWSER_NEW_TAB_URL;
 }
 
 function getBrowserURL() {
   return "chrome://browser/content/browser.xul";
 }
 
 function getTopWin(skipPopups) {
   // If this is called in a browser window, use that window regardless of
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -35,49 +35,63 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
 const FAVICON_REQUEST_TIMEOUT = 60 * 1000;
 // Map from windows to arrays of data about pending favicon loads.
 let gFaviconLoadDataMap = new Map();
 
 // copied from utilityOverlay.js
 const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab";
+const PREF_LOAD_BOOKMARKS_IN_BACKGROUND = "browser.tabs.loadBookmarksInBackground";
+const PREF_LOAD_BOOKMARKS_IN_TABS = "browser.tabs.loadBookmarksInTabs";
 
 // This function isn't public both because it's synchronous and because it is
 // going to be removed in bug 1072833.
 function IsLivemark(aItemId) {
   // Since this check may be done on each dragover event, it's worth maintaining
   // a cache.
   let self = IsLivemark;
   if (!("ids" in self)) {
     const LIVEMARK_ANNO = PlacesUtils.LMANNO_FEEDURI;
 
     let idsVec = PlacesUtils.annotations.getItemsWithAnnotation(LIVEMARK_ANNO);
     self.ids = new Set(idsVec);
 
     let obs = Object.freeze({
-      QueryInterface: XPCOMUtils.generateQI(Ci.nsIAnnotationObserver),
+      QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarksObserver]),
+
+      // Ci.nsINavBookmarkObserver items.
 
-      onItemAnnotationSet(itemId, annoName) {
-        if (annoName == LIVEMARK_ANNO)
+      onItemChanged(itemId, property, isAnnoProperty, newValue, lastModified,
+                    itemType, parentId, guid) {
+        if (isAnnoProperty && property == LIVEMARK_ANNO) {
           self.ids.add(itemId);
+        }
       },
 
-      onItemAnnotationRemoved(itemId, annoName) {
-        // If annoName is set to an empty string, the item is gone.
-        if (annoName == LIVEMARK_ANNO || annoName == "")
-          self.ids.delete(itemId);
+      onItemRemoved(itemId) {
+        // Since the bookmark is removed, we know we can remove any references
+        // to it from the cache.
+        self.ids.delete(itemId);
       },
 
+      onItemAdded() {},
+      onBeginUpdateBatch() {},
+      onEndUpdateBatch() {},
+      onItemVisited() {},
+      onItemMoved() {},
       onPageAnnotationSet() { },
       onPageAnnotationRemoved() { },
+      skipDescendantsOnItemRemoval: false,
+      skipTags: false,
     });
-    PlacesUtils.annotations.addObserver(obs);
+
+    PlacesUtils.bookmarks.addObserver(obs);
     PlacesUtils.registerShutdownFunction(() => {
-      PlacesUtils.annotations.removeObserver(obs);
+      PlacesUtils.bookmarks.removeObserver(obs);
     });
   }
   return self.ids.has(aItemId);
 }
 
 let InternalFaviconLoader = {
   /**
    * This gets called for every inner window that is destroyed.
@@ -1013,30 +1027,37 @@ this.PlacesUIUtils = {
    *          An uri result node.
    * @param   aEvent
    *          The DOM mouse/key event with modifier keys set that track the
    *          user's preferred destination window or tab.
    */
   openNodeWithEvent:
   function PUIU_openNodeWithEvent(aNode, aEvent) {
     let window = aEvent.target.ownerGlobal;
-    this._openNodeIn(aNode, window.whereToOpenLink(aEvent, false, true), window);
+
+    let where = window.whereToOpenLink(aEvent, false, true);
+    if (where == "current" && this.loadBookmarksInTabs &&
+        PlacesUtils.nodeIsBookmark(aNode) && !aNode.uri.startsWith("javascript:")) {
+      where = "tab";
+    }
+
+    this._openNodeIn(aNode, where, window);
   },
 
   /**
    * Loads the node's URL in the appropriate tab or window or as a
    * web panel.
    * see also openUILinkIn
    */
   openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aView, aPrivate) {
     let window = aView.ownerWindow;
     this._openNodeIn(aNode, aWhere, window, aPrivate);
   },
 
-  _openNodeIn: function PUIU_openNodeIn(aNode, aWhere, aWindow, aPrivate = false) {
+  _openNodeIn: function PUIU__openNodeIn(aNode, aWhere, aWindow, aPrivate = false) {
     if (aNode && PlacesUtils.nodeIsURI(aNode) &&
         this.checkURLSecurity(aNode, aWindow)) {
       let isBookmark = PlacesUtils.nodeIsBookmark(aNode);
 
       if (!PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
         if (isBookmark)
           this.markPageAsFollowedBookmark(aNode.uri);
         else
@@ -1053,17 +1074,17 @@ this.PlacesUIUtils = {
             browserWin.openWebPanel(aNode.title, aNode.uri);
             return;
           }
         }
       }
 
       aWindow.openUILinkIn(aNode.uri, aWhere, {
         allowPopups: aNode.uri.startsWith("javascript:"),
-        inBackground: Services.prefs.getBoolPref("browser.tabs.loadBookmarksInBackground"),
+        inBackground: this.loadBookmarksInBackground,
         private: aPrivate,
       });
     }
   },
 
   /**
    * Helper for guessing scheme from an url string.
    * Used to avoid nsIURI overhead in frequently called UI functions.
@@ -1520,16 +1541,21 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtil
 
 XPCOMUtils.defineLazyGetter(PlacesUIUtils, "useAsyncTransactions", function() {
   try {
     return Services.prefs.getBoolPref("browser.places.useAsyncTransactions");
   } catch (ex) { }
   return false;
 });
 
+XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInBackground",
+                                      PREF_LOAD_BOOKMARKS_IN_BACKGROUND, false);
+XPCOMUtils.defineLazyPreferenceGetter(PlacesUIUtils, "loadBookmarksInTabs",
+                                      PREF_LOAD_BOOKMARKS_IN_TABS, false);
+
 XPCOMUtils.defineLazyServiceGetter(this, "URIFixup",
                                    "@mozilla.org/docshell/urifixup;1",
                                    "nsIURIFixup");
 
 XPCOMUtils.defineLazyGetter(this, "bundle", function() {
   const PLACES_STRING_BUNDLE_URI =
     "chrome://browser/locale/places/places.properties";
   return Cc["@mozilla.org/intl/stringbundle;1"].
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -7,37 +7,35 @@ support-files =
   head.js
   framedPage.html
   frameLeft.html
   frameRight.html
   sidebarpanels_click_test_page.html
   keyword_form.html
 
 [browser_0_library_left_pane_migration.js]
-[browser_410196_paste_into_tags.js]
-subsuite = clipboard
-[browser_416459_cut.js]
-subsuite = clipboard
-[browser_423515.js]
-[browser_425884.js]
-[browser_435851_copy_query.js]
-subsuite = clipboard
-[browser_475045.js]
-[browser_555547.js]
 [browser_addBookmarkForFrame.js]
+[browser_bookmark_folder_moveability.js]
 [browser_bookmarklet_windowOpen.js]
 support-files =
   pageopeningwindow.html
 [browser_bookmarkProperties_addFolderDefaultButton.js]
 [browser_bookmarkProperties_addKeywordForThisSearch.js]
 [browser_bookmarkProperties_addLivemark.js]
 [browser_bookmarkProperties_bookmarkAllTabs.js]
 [browser_bookmarkProperties_editTagContainer.js]
 [browser_bookmarkProperties_readOnlyRoot.js]
 [browser_bookmarksProperties.js]
+[browser_check_correct_controllers.js]
+[browser_click_bookmarks_on_toolbar.js]
+[browser_cutting_bookmarks.js]
+subsuite = clipboard
+[browser_copy_folder_tree.js]
+[browser_copy_query_without_tree.js]
+subsuite = clipboard
 [browser_drag_bookmarks_on_toolbar.js]
 [browser_forgetthissite_single.js]
 [browser_history_sidebar_search.js]
 [browser_library_batch_delete.js]
 [browser_library_commands.js]
 [browser_library_delete_tags.js]
 [browser_library_downloads.js]
 [browser_library_infoBox.js]
@@ -46,19 +44,22 @@ support-files =
 [browser_library_left_pane_select_hierarchy.js]
 [browser_library_middleclick.js]
 [browser_library_open_leak.js]
 [browser_library_openFlatContainer.js]
 [browser_library_panel_leak.js]
 [browser_library_search.js]
 [browser_library_views_liveupdate.js]
 [browser_markPageAsFollowedLink.js]
+[browser_paste_into_tags.js]
+subsuite = clipboard
 [browser_sidebarpanels_click.js]
 skip-if = true # temporarily disabled for breaking the treeview - bug 658744
 [browser_sort_in_library.js]
+[browser_toolbar_drop_text.js]
 [browser_toolbarbutton_menu_context.js]
 [browser_views_iconsupdate.js]
 support-files =
   favicon-normal16.png
 [browser_views_liveupdate.js]
 [browser_bookmark_all_tabs.js]
 support-files =
   bookmark_dummy_1.html
rename from browser/components/places/tests/browser/browser_423515.js
rename to browser/components/places/tests/browser/browser_bookmark_folder_moveability.js
rename from browser/components/places/tests/browser/browser_555547.js
rename to browser/components/places/tests/browser/browser_check_correct_controllers.js
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_click_bookmarks_on_toolbar.js
@@ -0,0 +1,130 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const PREF_LOAD_BOOKMARKS_IN_TABS = "browser.tabs.loadBookmarksInTabs";
+const TEST_PAGES = ["about:mozilla", "about:robots"];
+
+var gBookmarkElements = [];
+
+function getToolbarNodeForItemGuid(aItemGuid) {
+  var children = document.getElementById("PlacesToolbarItems").childNodes;
+  for (let child of children) {
+    if (aItemGuid == child._placesNode.bookmarkGuid) {
+      return child;
+    }
+  }
+  return null;
+}
+
+function waitForLoad(browser, url) {
+  return BrowserTestUtils.browserLoaded(browser, false, url).then(() => {
+    return BrowserTestUtils.loadURI(browser, "about:blank");
+  });
+}
+
+function waitForNewTab(url, inBackground) {
+  return BrowserTestUtils.waitForNewTab(gBrowser, url).then(tab => {
+    if (inBackground) {
+      Assert.notEqual(tab,
+        gBrowser.selectedTab, `The new tab is in the background`);
+    } else {
+      Assert.equal(tab,
+        gBrowser.selectedTab, `The new tab is in the foreground`);
+    }
+
+    return BrowserTestUtils.removeTab(tab);
+  })
+}
+
+add_task(async function setup() {
+  let bookmarks = await Promise.all(TEST_PAGES.map((url, index) => {
+    return PlacesUtils.bookmarks.insert({
+      parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+      title: `Title ${index}`,
+      url
+    });
+  }));
+
+  let toolbar = document.getElementById("PersonalToolbar");
+  let wasCollapsed = toolbar.collapsed;
+  if (wasCollapsed) {
+    await promiseSetToolbarVisibility(toolbar, true);
+  }
+
+  for (let bookmark of bookmarks) {
+    let element = getToolbarNodeForItemGuid(bookmark.guid);
+    Assert.notEqual(element, null, "Found node on toolbar");
+
+    gBookmarkElements.push(element);
+  }
+
+  registerCleanupFunction(async () => {
+    gBookmarkElements = [];
+
+    if (wasCollapsed) {
+      await promiseSetToolbarVisibility(toolbar, false);
+    }
+
+    await Promise.all(bookmarks.map(bookmark => {
+      return PlacesUtils.bookmarks.remove(bookmark);
+    }));
+  });
+});
+
+add_task(async function click() {
+  let promise = waitForLoad(gBrowser.selectedBrowser, TEST_PAGES[0]);
+  EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], {
+    button: 0
+  });
+  await promise;
+
+  promise = waitForNewTab(TEST_PAGES[1], false);
+  EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], {
+    button: 0, accelKey: true
+  });
+  await promise;
+});
+
+add_task(async function middleclick() {
+  let promise = waitForNewTab(TEST_PAGES[0], true);
+  EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], {
+    button: 1, shiftKey: true
+  });
+  await promise;
+
+  promise = waitForNewTab(TEST_PAGES[1], false);
+  EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], {
+    button: 1
+  });
+  await promise;
+});
+
+add_task(async function clickWithPrefSet() {
+  await SpecialPowers.pushPrefEnv({set: [
+    [PREF_LOAD_BOOKMARKS_IN_TABS, true]
+  ]})
+
+  let promise = waitForNewTab(TEST_PAGES[0], false);
+  EventUtils.synthesizeMouseAtCenter(gBookmarkElements[0], {
+    button: 0
+  });
+  await promise;
+
+  let placesContext = document.getElementById("placesContext");
+  promise = BrowserTestUtils.waitForEvent(placesContext, "popupshown");
+  EventUtils.synthesizeMouseAtCenter(gBookmarkElements[1], {
+    button: 2,
+    type: "contextmenu"
+  });
+  await promise;
+
+  promise = waitForLoad(gBrowser.selectedBrowser, TEST_PAGES[1]);
+  let open = document.getElementById("placesContext_open");
+  EventUtils.synthesizeMouseAtCenter(open, {
+    button: 0
+  });
+  await promise;
+
+  await SpecialPowers.popPrefEnv();
+});
rename from browser/components/places/tests/browser/browser_425884.js
rename to browser/components/places/tests/browser/browser_copy_folder_tree.js
rename from browser/components/places/tests/browser/browser_435851_copy_query.js
rename to browser/components/places/tests/browser/browser_copy_query_without_tree.js
rename from browser/components/places/tests/browser/browser_416459_cut.js
rename to browser/components/places/tests/browser/browser_cutting_bookmarks.js
--- a/browser/components/places/tests/browser/browser_library_views_liveupdate.js
+++ b/browser/components/places/tests/browser/browser_library_views_liveupdate.js
@@ -60,20 +60,20 @@ function startTest() {
                          PlacesUtils._uri("http://bmf1.mozilla.org/"),
                          bs.DEFAULT_INDEX,
                          "bmf1");
   addedBookmarks.push(id);
   bs.moveItem(id, bs.bookmarksMenuFolder, 0);
 
   // TOOLBAR
   ok(true, "*** Acting on toolbar bookmarks");
-  bs.insertBookmark(bs.toolbarFolder,
-                    PlacesUtils._uri("http://tb1.mozilla.org/"),
-                    bs.DEFAULT_INDEX,
-                    "tb1");
+  id = bs.insertBookmark(bs.toolbarFolder,
+                         PlacesUtils._uri("http://tb1.mozilla.org/"),
+                         bs.DEFAULT_INDEX,
+                         "tb1");
   bs.setItemTitle(id, "tb1_edited");
   addedBookmarks.push(id);
   id = bs.insertBookmark(bs.toolbarFolder,
                          PlacesUtils._uri("place:"),
                          bs.DEFAULT_INDEX,
                          "tb2");
   bs.setItemTitle(id, "tb2_edited");
   addedBookmarks.push(id);
rename from browser/components/places/tests/browser/browser_410196_paste_into_tags.js
rename to browser/components/places/tests/browser/browser_paste_into_tags.js
rename from browser/components/places/tests/browser/browser_475045.js
rename to browser/components/places/tests/browser/browser_toolbar_drop_text.js
--- a/browser/components/places/tests/browser/browser_475045.js
+++ b/browser/components/places/tests/browser/browser_toolbar_drop_text.js
@@ -19,32 +19,36 @@ add_task(async function test() {
     });
   }
 
   // Setup the node we will use to be dropped. The actual node used does not
   // matter because we will set its data, effect, and mimeType manually.
   let placesItems = document.getElementById("PlacesToolbarItems");
   ok(placesItems, "PlacesToolbarItems should not be null");
   ok(placesItems.localName == "scrollbox", "PlacesToolbarItems should not be null");
-  ok(placesItems.childNodes[0], "PlacesToolbarItems must have at least one child");
 
   /**
    * Simulates a drop of a URI onto the bookmarks bar.
    *
    * @param aEffect
    *        The effect to use for the drop operation: move, copy, or link.
    * @param aMimeType
    *        The mime type to use for the drop operation.
    */
   let simulateDragDrop = async function(aEffect, aMimeType) {
     const url = "http://www.mozilla.org/D1995729-A152-4e30-8329-469B01F30AA7";
     let promiseItemAddedNotification = promiseBookmarksNotification(
       "onItemAdded", (itemId, parentId, index, type, uri, guid) => uri.spec == url);
 
-    EventUtils.synthesizeDrop(placesItems.childNodes[0],
+    // We use the toolbar as the drag source, as we just need almost any node
+    // to simulate the drag. The actual data for the drop is passed via the
+    // drag data. Note: The toolbar is used rather than another bookmark node,
+    // as we need something that is immovable from a places perspective, as this
+    // forces the move into a copy.
+    EventUtils.synthesizeDrop(toolbar,
                               placesItems,
                               [[{type: aMimeType,
                                 data: url}]],
                               aEffect, window);
 
     await promiseItemAddedNotification;
 
     // Verify that the drop produces exactly one bookmark.
@@ -75,17 +79,18 @@ add_task(async function test() {
     if (aMimeType == "text/x-moz-url")
       data = urls.map(spec => spec + "\n" + spec).join("\n");
     else
       data = urls.join("\n");
 
     let promiseItemAddedNotification = promiseBookmarksNotification(
       "onItemAdded", (itemId, parentId, index, type, uri, guid) => uri.spec == urls[2]);
 
-    EventUtils.synthesizeDrop(placesItems.childNodes[0],
+    // See notes for EventUtils.synthesizeDrop in simulateDragDrop().
+    EventUtils.synthesizeDrop(toolbar,
                               placesItems,
                               [[{type: aMimeType,
                                  data}]],
                               aEffect, window);
 
     await promiseItemAddedNotification;
 
     // Verify that the drop produces exactly one bookmark per each URL.
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/unit/test_PUIU_livemarksCache.js
@@ -0,0 +1,40 @@
+"use strict";
+
+let {IsLivemark} = Cu.import("resource:///modules/PlacesUIUtils.jsm", {});
+
+add_task(function test_livemark_cache_builtin_folder() {
+  // This test checks a basic livemark, and also initializes the observer for
+  // updates to the bookmarks.
+  Assert.ok(!IsLivemark(PlacesUtils.unfiledBookmarksFolderId),
+    "unfiled bookmarks should not be seen as a livemark");
+});
+
+add_task(async function test_livemark_add_and_remove_items() {
+  let bookmark = await PlacesUtils.bookmarks.insert({
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+    title: "Grandsire",
+    url: "http://example.com",
+  });
+
+  let bookmarkId = await PlacesUtils.promiseItemId(bookmark.guid);
+
+  Assert.ok(!IsLivemark(bookmarkId),
+    "a simple bookmark should not be seen as a livemark");
+
+  let livemark = await PlacesUtils.livemarks.addLivemark({
+    title: "Stedman",
+    feedURI: Services.io.newURI("http://livemark.com/"),
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+  });
+
+  let livemarkId = await PlacesUtils.promiseItemId(livemark.guid);
+
+  Assert.ok(IsLivemark(livemarkId),
+    "a livemark should be reported as a livemark");
+
+  // Now remove the livemark.
+  await PlacesUtils.livemarks.removeLivemark(livemark);
+
+  Assert.ok(!IsLivemark(livemarkId),
+    "the livemark should have been removed from the cache");
+});
--- a/browser/components/places/tests/unit/xpcshell.ini
+++ b/browser/components/places/tests/unit/xpcshell.ini
@@ -16,9 +16,10 @@ support-files =
 [test_browserGlue_distribution.js]
 [test_browserGlue_migrate.js]
 [test_browserGlue_prefs.js]
 [test_browserGlue_restore.js]
 [test_browserGlue_smartBookmarks.js]
 [test_browserGlue_urlbar_defaultbehavior_migration.js]
 [test_clearHistory_shutdown.js]
 [test_leftpane_corruption_handling.js]
+[test_PUIU_livemarksCache.js]
 [test_PUIU_makeTransaction.js]
--- a/browser/components/preferences/in-content-new/main.js
+++ b/browser/components/preferences/in-content-new/main.js
@@ -762,26 +762,26 @@ var gMainPane = {
 
   /**
    * Hide/show the "Show my windows and tabs from last time" option based
    * on the value of the browser.privatebrowsing.autostart pref.
    */
   updateBrowserStartupLastSession() {
     let pbAutoStartPref = document.getElementById("browser.privatebrowsing.autostart");
     let startupPref = document.getElementById("browser.startup.page");
-    let menu = document.getElementById("browserStartupPage");
+    let group = document.getElementById("browserStartupPage");
     let option = document.getElementById("browserStartupLastSession");
     if (pbAutoStartPref.value) {
       option.setAttribute("disabled", "true");
       if (option.selected) {
-        menu.selectedItem = document.getElementById("browserStartupHomePage");
+        group.selectedItem = document.getElementById("browserStartupHomePage");
       }
     } else {
       option.removeAttribute("disabled");
-      startupPref.updateElements(); // select the correct index in the startup menulist
+      startupPref.updateElements(); // select the correct radio in the startup group
     }
   },
 
   // TABS
 
   /*
    * Preferences:
    *
--- a/browser/components/preferences/in-content-new/main.xul
+++ b/browser/components/preferences/in-content-new/main.xul
@@ -316,84 +316,77 @@
                 label="&setAsMyDefaultBrowser3.label;" accesskey="&setAsMyDefaultBrowser3.accesskey;"
                 preference="pref.general.disable_button.default_browser"/>
       </hbox>
       <hbox align="center" class="indent">
         <image class="face-smile"/>
         <label id="isDefaultLabel" flex="1">&isDefault.label;</label>
       </hbox>
     </deck>
-    <separator class="thin"/>
   </vbox>
 #endif
 
-  <html:table id="startupTable">
-    <html:tr>
-      <html:td class="label-cell">
-        <label accesskey="&startupPage2.accesskey;"
-               control="browserStartupPage">&startupPage2.label;</label>
-      </html:td>
-      <html:td class="content-cell">
-        <menulist id="browserStartupPage"
-                  class="content-cell-item"
-                  preference="browser.startup.page">
-          <menupopup>
-          <menuitem label="&startupUserHomePage.label;"
-                    value="1"
-                    id="browserStartupHomePage"/>
-          <menuitem label="&startupBlankPage.label;"
-                    value="0"
-                    id="browserStartupBlank"/>
-          <menuitem label="&startupPrevSession.label;"
-                    value="3"
-                    id="browserStartupLastSession"/>
-          </menupopup>
-        </menulist>
-      </html:td>
-    </html:tr>
-    <html:tr class="tableGroup">
-      <html:td class="label-cell">
-        <label accesskey="&homepage2.accesskey;"
-               control="browserHomePage">&homepage2.label;</label>
-      </html:td>
-      <html:td class="content-cell">
-        <textbox id="browserHomePage"
-                 class="padded uri-element content-cell-item"
-                 type="autocomplete"
-                 autocompletesearch="unifiedcomplete"
-                 onsyncfrompreference="return gMainPane.syncFromHomePref();"
-                 onsynctopreference="return gMainPane.syncToHomePref(this.value);"
-                 placeholder="&abouthome.pageTitle;"
-                 preference="browser.startup.homepage"/>
-      </html:td>
-    </html:tr>
-    <html:tr class="tableSubGroup">
-      <html:td class="label-cell" />
-      <html:td class="content-cell homepage-buttons">
-        <button id="useCurrent"
-                class="content-cell-item"
-                label=""
-                accesskey="&useCurrentPage.accesskey;"
-                label1="&useCurrentPage.label;"
-                label2="&useMultiple.label;"
-                preference="pref.browser.homepage.disable_button.current_page"/>
-        <button id="useBookmark"
-                class="content-cell-item"
-                label="&chooseBookmark.label;"
-                accesskey="&chooseBookmark.accesskey;"
-                preference="pref.browser.homepage.disable_button.bookmark_page"
-                searchkeywords="&selectBookmark.title; &selectBookmark.label;"/>
-        <button id="restoreDefaultHomePage"
-                class="content-cell-item"
-                label="&restoreDefault.label;"
-                accesskey="&restoreDefault.accesskey;"
-                preference="pref.browser.homepage.disable_button.restore_default"/>
-      </html:td>
-    </html:tr>
-  </html:table>
+  <vbox id="startupPageBox">
+    <label accesskey="&startupPage2.accesskey;"
+           control="browserStartupPage">&startupPage2.label;</label>
+    <radiogroup id="browserStartupPage"
+                preference="browser.startup.page">
+      <radio label="&startupUserHomePage.label;"
+             value="1"
+             id="browserStartupHomePage"/>
+      <radio label="&startupBlankPage.label;"
+             value="0"
+             id="browserStartupBlank"/>
+      <radio label="&startupPrevSession.label;"
+             value="3"
+             id="browserStartupLastSession"/>
+    </radiogroup>
+  </vbox>
+</groupbox>
+
+<!-- Home Page -->
+<groupbox id="homepageGroup"
+          data-category="paneGeneral"
+          hidden="true">
+  <caption><label>&homepage2.label;</label></caption>
+
+  <vbox>
+    <textbox id="browserHomePage"
+             class="uri-element"
+             type="autocomplete"
+             autocompletesearch="unifiedcomplete"
+             onsyncfrompreference="return gMainPane.syncFromHomePref();"
+             onsynctopreference="return gMainPane.syncToHomePref(this.value);"
+             placeholder="&abouthome.pageTitle;"
+             preference="browser.startup.homepage"/>
+  </vbox>
+
+  <hbox class="homepage-buttons">
+    <button id="useCurrent"
+            flex="1"
+            class="homepage-button"
+            label=""
+            accesskey="&useCurrentPage.accesskey;"
+            label1="&useCurrentPage.label;"
+            label2="&useMultiple.label;"
+            preference="pref.browser.homepage.disable_button.current_page"/>
+    <button id="useBookmark"
+            flex="1"
+            class="homepage-button"
+            label="&chooseBookmark.label;"
+            accesskey="&chooseBookmark.accesskey;"
+            preference="pref.browser.homepage.disable_button.bookmark_page"
+            searchkeywords="&selectBookmark.title; &selectBookmark.label;"/>
+    <button id="restoreDefaultHomePage"
+            flex="1"
+            class="homepage-button"
+            label="&restoreDefault.label;"
+            accesskey="&restoreDefault.accesskey;"
+            preference="pref.browser.homepage.disable_button.restore_default"/>
+  </hbox>
 </groupbox>
 
 <!-- Tab preferences -->
 <groupbox data-category="paneGeneral"
           hidden="true">
     <caption><label>&tabsGroup.label;</label></caption>
 
     <checkbox id="ctrlTabRecentlyUsedOrder" label="&ctrlTabRecentlyUsedOrder.label;"
--- a/browser/components/preferences/in-content-new/privacy.js
+++ b/browser/components/preferences/in-content-new/privacy.js
@@ -1097,20 +1097,27 @@ var gPrivacyPane = {
     });
 
     blockUncommonUnwanted.addEventListener("command", function() {
       blockUnwantedPref.value = blockUncommonUnwanted.checked;
       blockUncommonPref.value = blockUncommonUnwanted.checked;
 
       let malware = malwareTable.value
         .split(",")
-        .filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple");
+        .filter(x => x !== "goog-unwanted-proto" &&
+                     x !== "goog-unwanted-shavar" &&
+                     x !== "test-unwanted-simple");
 
       if (blockUncommonUnwanted.checked) {
-        malware.push("goog-unwanted-shavar");
+        if (malware.indexOf("goog-malware-shavar") != -1) {
+          malware.push("goog-unwanted-shavar");
+        } else {
+          malware.push("goog-unwanted-proto");
+        }
+
         malware.push("test-unwanted-simple");
       }
 
       // sort alphabetically to keep the pref consistent
       malware.sort();
 
       malwareTable.value = malware.join(",");
     });
--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js
+++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js
@@ -7,17 +7,17 @@ add_task(async function() {
   await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]});
 });
 
 /**
  * Test for searching for the "Set Home Page" subdialog.
  */
 add_task(async function() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
-  await evaluateSearchResults("Set Home Page", "startupGroup");
+  await evaluateSearchResults("Set Home Page", "homepageGroup");
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 /**
  * Test for searching for the "Languages" subdialog.
  */
 add_task(async function() {
   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
--- a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js
+++ b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js
@@ -125,16 +125,17 @@ add_task(async function() {
   }
   await searchCompletedPromise;
 
   // Checks if back to generalPane
   for (let i = 0; i < mainPrefTag.childElementCount; i++) {
     let child = mainPrefTag.children[i]
     if (child.id == "paneGeneral"
     || child.id == "startupGroup"
+    || child.id == "homepageGroup"
     || child.id == "languagesGroup"
     || child.id == "fontsGroup"
     || child.id == "downloadsGroup"
     || child.id == "applicationsGroup"
     || child.id == "drmGroup"
     || child.id == "updateApp"
     || child.id == "browsingGroup"
     || child.id == "performanceGroup"
--- a/browser/components/preferences/in-content-new/tests/browser_security-2.js
+++ b/browser/components/preferences/in-content-new/tests/browser_security-2.js
@@ -54,21 +54,29 @@ add_task(async function() {
     is(blockUncommon.hasAttribute("disabled"), val, "block uncommon checkbox is set correctly");
 
   }
 
   await checkPrefSwitch(true);
   await checkPrefSwitch(false);
 });
 
+requestLongerTimeout(2);
 // test the unwanted/uncommon software warning preference
 add_task(async function() {
-  async function checkPrefSwitch(val1, val2) {
+  async function checkPrefSwitch(val1, val2, isV2) {
     Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", val1);
     Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_uncommon", val2);
+    let testMalwareTable = "goog-malware-" + (isV2 ? "shavar" : "proto");
+    testMalwareTable += ",test-malware-simple";
+    if (val1 && val2) {
+      testMalwareTable += ",goog-unwanted-" + (isV2 ? "shavar" : "proto");
+      testMalwareTable += ",test-unwanted-simple";
+    }
+    Services.prefs.setCharPref("urlclassifier.malwareTable", testMalwareTable);
 
     gBrowser.reload();
     await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
     let doc = gBrowser.selectedBrowser.contentDocument;
     let checkbox = doc.getElementById("blockUncommonUnwanted");
     let checked = checkbox.checked;
     is(checked, val1 && val2, "unwanted/uncommon preference is initialized correctly");
@@ -80,23 +88,33 @@ add_task(async function() {
     // check that both settings are now turned on or off
     is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted"), !checked,
        "block_potentially_unwanted is set correctly");
     is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_uncommon"), !checked,
        "block_uncommon is set correctly");
 
     // when the preference is on, the malware table should include these ids
     let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(",");
-    is(malwareTable.includes("goog-unwanted-shavar"), !checked,
-       "malware table doesn't include goog-unwanted-shavar");
+    if (isV2) {
+      is(malwareTable.includes("goog-unwanted-shavar"), !checked,
+         "malware table doesn't include goog-unwanted-shavar");
+    } else {
+      is(malwareTable.includes("goog-unwanted-proto"), !checked,
+         "malware table doesn't include goog-unwanted-proto");
+    }
     is(malwareTable.includes("test-unwanted-simple"), !checked,
        "malware table doesn't include test-unwanted-simple");
     let sortedMalware = malwareTable.slice(0);
     sortedMalware.sort();
     Assert.deepEqual(malwareTable, sortedMalware, "malware table has been sorted");
 
   }
 
-  await checkPrefSwitch(true, true);
-  await checkPrefSwitch(false, true);
-  await checkPrefSwitch(true, false);
-  await checkPrefSwitch(false, false);
+  await checkPrefSwitch(true, true, false);
+  await checkPrefSwitch(false, true, false);
+  await checkPrefSwitch(true, false, false);
+  await checkPrefSwitch(false, false, false);
+  await checkPrefSwitch(true, true, true);
+  await checkPrefSwitch(false, true, true);
+  await checkPrefSwitch(true, false, true);
+  await checkPrefSwitch(false, false, true);
+
 });
--- a/browser/components/preferences/in-content/security.js
+++ b/browser/components/preferences/in-content/security.js
@@ -194,20 +194,27 @@ var gSecurityPane = {
     });
 
     blockUncommonUnwanted.addEventListener("command", function() {
       blockUnwantedPref.value = blockUncommonUnwanted.checked;
       blockUncommonPref.value = blockUncommonUnwanted.checked;
 
       let malware = malwareTable.value
         .split(",")
-        .filter(x => x !== "goog-unwanted-shavar" && x !== "test-unwanted-simple");
+        .filter(x => x !== "goog-unwanted-proto" &&
+                     x !== "goog-unwanted-shavar" &&
+                     x !== "test-unwanted-simple");
 
       if (blockUncommonUnwanted.checked) {
-        malware.push("goog-unwanted-shavar");
+        if (malware.indexOf("goog-malware-shavar") != -1) {
+          malware.push("goog-unwanted-shavar");
+        } else {
+          malware.push("goog-unwanted-proto");
+        }
+
         malware.push("test-unwanted-simple");
       }
 
       // sort alphabetically to keep the pref consistent
       malware.sort();
 
       malwareTable.value = malware.join(",");
     });
--- a/browser/components/preferences/in-content/tests/browser_security.js
+++ b/browser/components/preferences/in-content/tests/browser_security.js
@@ -92,21 +92,29 @@ add_task(async function() {
     // check if the uncommon warning checkbox has updated
     is(blockUncommon.hasAttribute("disabled"), val, "block uncommon checkbox is set correctly");
   }
 
   await checkPrefSwitch(true);
   await checkPrefSwitch(false);
 });
 
+requestLongerTimeout(2);
 // test the unwanted/uncommon software warning preference
 add_task(async function() {
-  async function checkPrefSwitch(val1, val2) {
+  async function checkPrefSwitch(val1, val2, isV2) {
     Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted", val1);
     Services.prefs.setBoolPref("browser.safebrowsing.downloads.remote.block_uncommon", val2);
+    let testMalwareTable = "goog-malware-" + (isV2 ? "shavar" : "proto");
+    testMalwareTable += ",test-malware-simple";
+    if (val1 && val2) {
+      testMalwareTable += ",goog-unwanted-" + (isV2 ? "shavar" : "proto");
+      testMalwareTable += ",test-unwanted-simple";
+    }
+    Services.prefs.setCharPref("urlclassifier.malwareTable", testMalwareTable);
 
     gBrowser.reload();
     await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
 
     let doc = gBrowser.selectedBrowser.contentDocument;
     let checkbox = doc.getElementById("blockUncommonUnwanted");
     let checked = checkbox.checked;
     is(checked, val1 && val2, "unwanted/uncommon preference is initialized correctly");
@@ -117,22 +125,32 @@ add_task(async function() {
     // check that both settings are now turned on or off
     is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_potentially_unwanted"), !checked,
        "block_potentially_unwanted is set correctly");
     is(Services.prefs.getBoolPref("browser.safebrowsing.downloads.remote.block_uncommon"), !checked,
        "block_uncommon is set correctly");
 
     // when the preference is on, the malware table should include these ids
     let malwareTable = Services.prefs.getCharPref("urlclassifier.malwareTable").split(",");
-    is(malwareTable.includes("goog-unwanted-shavar"), !checked,
-       "malware table doesn't include goog-unwanted-shavar");
+    if (isV2) {
+      is(malwareTable.includes("goog-unwanted-shavar"), !checked,
+         "malware table doesn't include goog-unwanted-shavar");
+    } else {
+      is(malwareTable.includes("goog-unwanted-proto"), !checked,
+         "malware table doesn't include goog-unwanted-proto");
+    }
     is(malwareTable.includes("test-unwanted-simple"), !checked,
        "malware table doesn't include test-unwanted-simple");
     let sortedMalware = malwareTable.slice(0);
     sortedMalware.sort();
     Assert.deepEqual(malwareTable, sortedMalware, "malware table has been sorted");
   }
 
-  await checkPrefSwitch(true, true);
-  await checkPrefSwitch(false, true);
-  await checkPrefSwitch(true, false);
-  await checkPrefSwitch(false, false);
+  await checkPrefSwitch(true, true, false);
+  await checkPrefSwitch(false, true, false);
+  await checkPrefSwitch(true, false, false);
+  await checkPrefSwitch(false, false, false);
+  await checkPrefSwitch(true, true, true);
+  await checkPrefSwitch(false, true, true);
+  await checkPrefSwitch(true, false, true);
+  await checkPrefSwitch(false, false, true);
+
 });
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -121,19 +121,26 @@ AutofillProfileAutoCompleteSearch.protot
         onSearchResult: (search, result) => {
           listener.onSearchResult(this, result);
           ProfileAutocomplete.setProfileAutoCompleteResult(result);
         },
       });
       return;
     }
 
-    let collectionName = isAddressField ? ADDRESSES_COLLECTION_NAME : CREDITCARDS_COLLECTION_NAME;
+    let infoWithoutElement = Object.assign({}, info);
+    delete infoWithoutElement.elementWeakRef;
 
-    this._getRecords({collectionName, info, searchString}).then((records) => {
+    let data = {
+      collectionName: isAddressField ? ADDRESSES_COLLECTION_NAME : CREDITCARDS_COLLECTION_NAME,
+      info: infoWithoutElement,
+      searchString,
+    };
+
+    this._getRecords(data).then((records) => {
       if (this.forceStop) {
         return;
       }
       // Sort addresses by timeLastUsed for showing the lastest used address at top.
       records.sort((a, b) => b.timeLastUsed - a.timeLastUsed);
 
       let adaptedRecords = handler.getAdaptedProfiles(records);
       let result = null;
--- a/browser/extensions/formautofill/FormAutofillUtils.jsm
+++ b/browser/extensions/formautofill/FormAutofillUtils.jsm
@@ -53,16 +53,19 @@ this.FormAutofillUtils = {
     "tel-national": "tel",
     "tel-area-code": "tel",
     "tel-local": "tel",
     "tel-local-prefix": "tel",
     "tel-local-suffix": "tel",
     "tel-extension": "tel",
     "email": "email",
     "cc-name": "creditCard",
+    "cc-given-name": "creditCard",
+    "cc-additional-name": "creditCard",
+    "cc-family-name": "creditCard",
     "cc-number": "creditCard",
     "cc-exp-month": "creditCard",
     "cc-exp-year": "creditCard",
     "cc-exp": "creditCard",
   },
   _addressDataLoaded: false,
   _collators: {},
   _reAlternativeCountryNames: {},
--- a/browser/extensions/formautofill/ProfileStorage.jsm
+++ b/browser/extensions/formautofill/ProfileStorage.jsm
@@ -64,16 +64,17 @@
  *       cc-exp-year,          // 2-digit year will be converted to 4 digits
  *                             // upon saving
  *
  *       // computed fields (These fields are computed based on the above fields
  *       // and are not allowed to be modified directly.)
  *       cc-given-name,
  *       cc-additional-name,
  *       cc-family-name,
+ *       cc-exp,
  *
  *       // metadata
  *       timeCreated,          // in ms
  *       timeLastUsed,         // in ms
  *       timeLastModified,     // in ms
  *       timesUsed
  *       _sync: { ... optional sync metadata },
  *     }
@@ -185,16 +186,17 @@ const VALID_CREDIT_CARD_FIELDS = [
   "cc-exp-month",
   "cc-exp-year",
 ];
 
 const VALID_CREDIT_CARD_COMPUTED_FIELDS = [
   "cc-given-name",
   "cc-additional-name",
   "cc-family-name",
+  "cc-exp",
 ];
 
 const INTERNAL_FIELDS = [
   "guid",
   "version",
   "timeCreated",
   "timeLastUsed",
   "timeLastModified",
--- a/browser/extensions/formautofill/skin/shared/editCreditCard.css
+++ b/browser/extensions/formautofill/skin/shared/editCreditCard.css
@@ -1,16 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 form {
   justify-content: center;
-  /* Add extra space to ensure invalid input box is displayed properly */
-  padding: 2px;
 }
 
 form > label,
 form > div {
   flex: 1 0 100%;
   align-self: center;
   font-size: 1.3em;
   margin: 0 0 0.5em !important;
--- a/browser/extensions/formautofill/skin/shared/editDialog.css
+++ b/browser/extensions/formautofill/skin/shared/editDialog.css
@@ -10,16 +10,18 @@ form,
 label,
 div,
 p {
   display: flex;
 }
 
 form {
   flex-wrap: wrap;
+  /* Add extra space to ensure invalid input box is displayed properly */
+  padding: 2px;
 }
 
 form > label,
 form > p {
   margin: 0 0 0.5em !important;
 }
 
 label > span,
--- a/browser/extensions/formautofill/test/unit/test_creditCardRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_creditCardRecords.js
@@ -123,21 +123,23 @@ add_task(async function test_getAll() {
 
   do_check_eq(creditCards.length, 2);
   do_check_credit_card_matches(creditCards[0], TEST_CREDIT_CARD_1);
   do_check_credit_card_matches(creditCards[1], TEST_CREDIT_CARD_2);
 
   // Check computed fields.
   do_check_eq(creditCards[0]["cc-given-name"], "John");
   do_check_eq(creditCards[0]["cc-family-name"], "Doe");
+  do_check_eq(creditCards[0]["cc-exp"], "2017-04");
 
   // Test with rawData set.
   creditCards = profileStorage.creditCards.getAll({rawData: true});
   do_check_eq(creditCards[0]["cc-given-name"], undefined);
   do_check_eq(creditCards[0]["cc-family-name"], undefined);
+  do_check_eq(creditCards[0]["cc-exp"], undefined);
 
   // Modifying output shouldn't affect the storage.
   creditCards[0]["cc-name"] = "test";
   do_check_credit_card_matches(profileStorage.creditCards.getAll()[0], TEST_CREDIT_CARD_1);
 });
 
 add_task(async function test_get() {
   let path = getTempFile(TEST_STORE_FILE_NAME).path;
deleted file mode 100644
--- a/browser/extensions/onboarding/content/img/overlay-icon.svg
+++ /dev/null
@@ -1,2 +0,0 @@
-
-<svg width="36" height="29" viewBox="0 0 36 29" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>overlayfox</title><defs><path id="a" d="M.002.058h35.953V27.94H.002z"/><path id="c" d="M0 17.39V.42h35.957v16.97H0z"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 .55)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M35.953 16.593c.006-.19-.036-.386-.133-.562-1.02-1.884-2.052-3.65-3.17-5.243.773-.62 1.448-1.394 1.975-2.312 1.497-2.61 1.413-5.72.042-8.175-.063-.114-.176-.2-.294-.242.002.01-.006.016-.006.024-.008-.012-.018-.024-.03-.024-2.825-.03-5.558 1.46-7.112 4.09-.16.27-.3.572-.418.896-2.394-1.464-5.24-2.31-8.822-2.31-3.57 0-6.416.832-8.806 2.283v.007l-.02.014c-.12-.322-.257-.623-.416-.89C7.19 1.52 4.457.03 1.632.06c-.014 0-.028.014-.035.028 0-.007.007-.007.007-.014-.14.036-.267.12-.337.256-1.37 2.455-1.462 5.557.042 8.175.526.926 1.208 1.7 1.988 2.327-1.118 1.586-2.15 3.344-3.163 5.23-.092.166-.13.352-.13.533H.002c0 .007.002.013 0 .02v.016h.002c-.006.17.032.344.117.504 3.685 6.71 7.6 9.377 15 9.88.807.58 1.79.928 2.857.928 1.065 0 2.05-.347 2.857-.93 7.4-.5 11.323-3.168 15-9.878.09-.162.13-.35.12-.534v-.003-.004" fill="#F70" mask="url(#b)"/></g><g transform="translate(0 10.268)"><mask id="d" fill="#fff"><use xlink:href="#c"/></mask><path d="M17.978 17.39c9.31 0 13.732-2.447 17.857-9.975.09-.163.133-.356.12-.54h-4.238V5.23h-.014c.007-.113.014-.234.014-.348 0-2.462-1.975-4.46-4.407-4.46-2.43 0-4.406 1.998-4.406 4.46 0 .12.008.235.014.35h-9.88c.007-.1.014-.208.014-.314 0-2.462-1.974-4.462-4.406-4.462-2.43 0-4.406 2-4.406 4.462 0 .106.007.206.014.313h-.007v1.644H.002c-.013.185.03.37.12.54 4.132 7.53 8.545 9.976 17.856 9.976" fill="#FFC899" mask="url(#d)"/></g><path d="M35.954 17.15c.007-.192-.035-.39-.134-.57-1.018-1.885-2.05-3.65-3.17-5.243.774-.62 1.45-1.395 1.976-2.312 1.497-2.61 1.413-5.72.042-8.175-.063-.114-.175-.2-.295-.242.007.02.007.043-.014.057-.746.37-3.943 2.15-5.756 6.19-2.74-2.242-6.1-3.572-10.62-3.572-3.568 0-6.414.832-8.804 2.284v.007c-.632.384-1.23.81-1.8 1.28-1.474-3.28-3.85-5.065-5.1-5.82-.008 0-.008-.006-.015-.006C2.175.97 2.09.92 2.013.878c-.008 0-.015-.007-.015-.007C1.963.85 1.935.836 1.9.82c-.007 0-.007-.006-.014-.006-.035-.02-.064-.035-.098-.05-.008 0-.008-.006-.015-.006-.02-.015-.05-.03-.07-.036-.007 0-.014-.008-.02-.008C1.66.7 1.632.694 1.612.68 1.604.68 1.604.67 1.597.67L1.59.665V.65c0-.007 0-.007.008-.014 0-.007.007-.007.007-.014-.14.036-.267.12-.338.256-1.37 2.455-1.46 5.557.042 8.175.527.925 1.208 1.7 1.988 2.327-1.117 1.587-2.15 3.344-3.162 5.23-.098.177-.14.377-.133.57H4.24v-1.645h.007c-.007-.1-.014-.207-.014-.313 0-2.462 1.975-4.46 4.406-4.46 2.43 0 4.405 1.998 4.405 4.46 0 .106-.007.206-.014.313h.015v7.96c0 2.755 2.207 4.996 4.933 4.996 2.72 0 4.933-2.233 4.933-4.994v-7.99h.01c0-.042-.01-.085-.01-.128v-.47c.128-2.347 2.047-4.21 4.4-4.21 2.432 0 4.407 1.998 4.407 4.46 0 .12-.007.235-.015.348h.015v1.644h4.237z" fill="#F70"/><path d="M16.453 19.832s.05 0 .134.008c.084.006.204.014.345.014.14.007.31.007.49.014.177 0 .374.007.563.007.19 0 .38 0 .563-.007.175 0 .344-.007.492-.014.14-.008.26-.014.344-.014.084-.008.133-.008.133-.008.598-.057 1.132.39 1.18.996.03.3-.07.584-.245.804l-.942 1.146-.035.035c-.02.022-.056.058-.098.093-.043.036-.092.078-.148.114-.057.043-.127.078-.197.12-.07.036-.148.072-.233.107-.084.03-.168.057-.26.08-.175.042-.372.056-.56.048-.1-.006-.19-.014-.29-.028-.05-.007-.09-.014-.14-.02-.05-.008-.092-.023-.134-.03-.042-.007-.09-.028-.133-.035-.042-.015-.084-.03-.127-.043-.042-.015-.084-.03-.12-.05-.034-.015-.077-.036-.112-.05-.035-.022-.07-.036-.105-.057-.036-.022-.064-.036-.092-.058-.057-.035-.106-.07-.148-.106-.042-.03-.077-.065-.098-.08l-.036-.035-.906-.946c-.45-.47-.436-1.21.02-1.666.254-.25.577-.356.893-.342" fill="#994C00"/><path d="M8.407 19.398c-.618 0-1.243-.135-1.82-.412-.28-.136-.4-.477-.267-.762.134-.284.47-.405.752-.27.87.42 1.884.406 2.77-.043.28-.14.617-.027.75.258.14.284.03.625-.252.76-.612.314-1.272.47-1.933.47M26.938 19.398c-.618 0-1.244-.135-1.82-.412-.28-.136-.4-.477-.267-.762.135-.284.472-.405.753-.27.87.42 1.883.406 2.77-.043.28-.14.617-.027.75.258.134.284.03.625-.252.76-.61.314-1.27.47-1.932.47" fill="#F70"/><path d="M10.91 15.926c-.008-.064-.03-.12-.057-.178-.45-1.024-1.363-1.657-2.39-1.657-1.025 0-1.94.634-2.39 1.658-.027.057-.04.12-.055.178-.14.3-.077.67.183.904.31.277.788.25 1.07-.064.3-.342.736-.54 1.194-.54.456 0 .9.198 1.194.54.148.17.358.256.57.256.175 0 .358-.064.498-.192.26-.235.323-.605.183-.904M29.44 15.926c-.007-.064-.028-.12-.057-.178-.45-1.024-1.363-1.657-2.39-1.657-1.024 0-1.938.634-2.388 1.658-.028.057-.042.12-.056.178-.142.3-.078.67.182.904.14.128.323.192.5.192.21 0 .413-.086.568-.256.302-.342.737-.54 1.194-.54.457 0 .9.198 1.195.54.273.313.75.348 1.067.064.26-.235.323-.605.183-.904" fill="#363959"/><path d="M17.978 27.438c-1.405 0-2.122-.718-2.473-1.323-.373-.648-.415-1.288-.415-1.31-.007-.092.042-.184.12-.234.077-.05.175-.056.26-.014.007.008.934.456 2.48.456 1.553 0 2.53-.456 2.544-.456.085-.042.183-.028.26.022.078.05.12.142.112.235 0 .028-.042.67-.414 1.31-.352.597-1.068 1.315-2.474 1.315" fill="#994C00"/><path d="M28.597 6.855c1.468-3.28 3.843-5.066 5.094-5.82.008 0 .008-.007.016-.007.09-.057.175-.107.252-.15.007 0 .015-.007.015-.007.034-.02.063-.034.098-.05.008 0 .008-.006.015-.006.035-.02.063-.035.098-.05.007 0 .007-.007.015-.007.02-.014.05-.028.07-.035.006 0 .014-.008.02-.008.022-.014.05-.02.07-.035.008 0 .008-.008.015-.008l.007-.008V.65c0-.007 0-.007-.007-.013-.007-.015-.02-.03-.035-.03C31.513.58 28.78 2.068 27.226 4.7c-.16.27-.302.575-.42.902.617.356 1.215.783 1.79 1.253M7.374 6.855C5.906 3.575 3.53 1.79 2.28 1.035c-.008 0-.008-.007-.015-.007C2.175.97 2.09.92 2.013.878c-.008 0-.015-.007-.015-.007C1.963.85 1.935.837 1.9.82c-.007 0-.007-.006-.014-.006-.035-.02-.064-.035-.098-.05-.008 0-.008-.007-.015-.007-.02-.014-.05-.028-.07-.035-.007 0-.014-.008-.02-.008C1.66.7 1.632.694 1.612.68 1.604.68 1.604.67 1.597.67L1.59.664V.65c0-.007 0-.007.008-.013.007-.015.02-.03.035-.03C4.458.58 7.19 2.068 8.745 4.7c.16.27.302.575.42.902-.624.356-1.22.783-1.79 1.253" fill="#FF9F4D"/></g></svg>
--- a/browser/extensions/onboarding/content/onboarding.css
+++ b/browser/extensions/onboarding/content/onboarding.css
@@ -27,48 +27,51 @@
   padding: 0;
   position: absolute;
   cursor: pointer;
   top: 34px;
   offset-inline-start: 34px;
   border: none;
   /* Set to none so no grey contrast background in the high-contrast mode */
   background: none;
+  /* make sure the icon stay above the activity-stream searchbar */
+  z-index: 10;
 }
 
 /* Keyboard focus styling */
 #onboarding-overlay-button:-moz-focusring {
   outline: solid 2px rgba(0, 0, 0, 0.1);
   -moz-outline-radius: 5px;
   outline-offset: 5px;
   transition: outline-offset 150ms;
 }
 
 #onboarding-overlay-button-icon {
-  width: 36px;
+  width: 32px;
   vertical-align: top;
 }
 
 #onboarding-overlay-button::after {
-  background: #5ce6e6;
-  font-size: 12px;
-  border: 1px solid #fff;
+  background: #0060df;
+  font-size: 13px;
   text-align: center;
-  color: #10404a;
+  color: #fff;
   box-sizing: content-box;
+  font-weight: 400;
   content: attr(aria-label);
   display: inline-block;
-  offset-inline-start: 39px;
-  border-radius: 22px;
-  padding: 5px 12px;
+  border: 1px solid transparent;
+  border-radius: 2px;
+  padding: 10px 16px;
   min-width: 100px;
   max-width: 140px;
   white-space: pre-line;
-  margin-inline-start: 3px;
-  margin-top: -5px;
+  margin-inline-start: 4px;
+  margin-top: -10px;
+  box-shadow: -2px 0 5px 0 rgba(74, 74, 79, 0.25);
 }
 
 #onboarding-overlay-dialog,
 .onboarding-hidden,
 #onboarding-tour-sync-page[data-login-state=logged-in] .show-on-logged-out,
 #onboarding-tour-sync-page[data-login-state=logged-out] .show-on-logged-in {
   display: none;
 }
@@ -81,17 +84,17 @@
   width: 16px;
   height: 16px;
   border: none;
   background: none;
   padding: 0;
  }
 
 .onboarding-close-btn::before {
-  content: url(chrome://global/skin/icons/close.svg);
+  content: url("chrome://global/skin/icons/close.svg");
   -moz-context-properties: fill, fill-opacity;
   fill-opacity: 0;
 }
 
 .onboarding-close-btn:-moz-any(:hover, :active, :focus, :-moz-focusring)::before {
   fill-opacity: 0.1;
 }
 
@@ -302,24 +305,24 @@
 
 .onboarding-tour-page.onboarding-no-button > .onboarding-tour-button-container {
   display: none;
   grid-row: tour-page-end;
   grid-column: tour-page-end;
 }
 
 .onboarding-tour-action-button {
-  padding: 10px 20px;
-  font-size: 15px;
-  font-weight: 600;
-  line-height: 21px;
-  background: #0a84ff;
+  background: #0060df;
   /* With 1px transparent border, could see a border in the high-constrast mode */
   border: 1px solid transparent;
-  border-radius: 0;
+  border-radius: 2px;
+  padding: 10px 20px;
+  font-size: 14px;
+  font-weight: 600;
+  line-height: 16px;
   color: #fff;
   float: inline-end;
   margin-inline-end: 26px;
   margin-top: -32px;
 }
 
 /* Remove default dotted outline around buttons' text */
 .onboarding-tour-action-button::-moz-focus-inner,
@@ -332,22 +335,22 @@
 #onboarding-notification-action-btn:-moz-focusring,
 #onboarding-tour-list .onboarding-tour-item:focus {
   outline: 2px solid rgba(0,149,221,0.5);
   outline-offset: 1px;
   -moz-outline-radius: 2px;
 }
 
 .onboarding-tour-action-button:hover:not([disabled])  {
-  background: #0060df;
+  background: #003eaa;
   cursor: pointer;
 }
 
 .onboarding-tour-action-button:active:not([disabled]) {
-  background: #003EAA;
+  background: #002275;
 }
 
 .onboarding-tour-action-button:disabled {
   opacity: 0.5;
 }
 
 /* Tour Icons */
 #onboarding-tour-singlesearch,
@@ -527,8 +530,14 @@
 #onboarding-notification-action-btn:hover {
   background-color: #ebebeb;
   cursor: pointer;
 }
 
 #onboarding-notification-action-btn:active {
   background-color: #dadada;
 }
+
+@media (min-resolution: 2dppx) {
+  #onboarding-notification-tour-icon {
+    background-image: url("chrome://branding/content/icon128.png");
+  }
+}
--- a/browser/extensions/onboarding/content/onboarding.js
+++ b/browser/extensions/onboarding/content/onboarding.js
@@ -452,31 +452,33 @@ class Onboarding {
     // Only containers receive pointer events in onboarding tour tab list,
     // actual semantic tab is their first child.
     if (classList.contains("onboarding-tour-item-container")) {
       ({ id, classList } = target.firstChild);
     }
 
     switch (id) {
       case "onboarding-overlay-button":
+        this.showOverlay();
+        this.gotoPage(this.selectedTour.id);
+        break;
       case "onboarding-overlay-close-btn":
       // If the clicking target is directly on the outer-most overlay,
       // that means clicking outside the tour content area.
       // Let's toggle the overlay.
       case "onboarding-overlay":
-        this.toggleOverlay();
-        this.gotoPage(this.selectedTour.id);
+        this.hideOverlay();
         break;
       case "onboarding-notification-close-btn":
         this.hideNotification();
         this._removeTourFromNotificationQueue(this._notificationBar.dataset.targetTourId);
         break;
       case "onboarding-notification-action-btn":
         let tourId = this._notificationBar.dataset.targetTourId;
-        this.toggleOverlay();
+        this.showOverlay();
         this.gotoPage(tourId);
         this._removeTourFromNotificationQueue(tourId);
         break;
     }
     if (classList.contains("onboarding-tour-item")) {
       this.gotoPage(id);
       // Keep focus (not visible) on current item for potential keyboard
       // navigation.
@@ -559,17 +561,17 @@ class Onboarding {
         if (targetIndex > -1 && targetIndex < this._tourItems.length - 1) {
           let next = this._tourItems[targetIndex + 1];
           this.handleClick(next);
           next.focus();
         }
         event.preventDefault();
         break;
       case "Escape":
-        this.toggleOverlay();
+        this.hideOverlay();
         break;
       case "Tab":
         let next = this.wrapMoveFocus(target, shiftKey);
         // If focus was wrapped, prevent Tab key default action.
         if (next) {
           event.preventDefault();
         }
         break;
@@ -610,30 +612,32 @@ class Onboarding {
     this._overlay.remove();
     if (this._notificationBar) {
       this._notificationBar.remove();
     }
     this._tourItems = this._tourPages =
     this._overlayIcon = this._overlay = this._notificationBar = null;
   }
 
-  toggleOverlay() {
+  showOverlay() {
     if (this._tourItems.length == 0) {
       // Lazy loading until first toggle.
       this._loadTours(this._tours);
     }
 
     this.hideNotification();
-    this._overlay.classList.toggle("onboarding-opened");
-    this.toggleModal(this._overlay.classList.contains("onboarding-opened"));
+    this.toggleModal(this._overlay.classList.toggle("onboarding-opened"));
+  }
 
+  hideOverlay() {
     let hiddenCheckbox = this._window.document.getElementById("onboarding-tour-hidden-checkbox");
     if (hiddenCheckbox.checked) {
       this.hide();
     }
+    this.toggleModal(this._overlay.classList.toggle("onboarding-opened"));
   }
 
   /**
    * Set modal dialog state and properties for accessibility purposes.
    * @param  {Boolean} opened  whether the dialog is opened or closed.
    */
   toggleModal(opened) {
     let { document: doc } = this._window;
@@ -986,17 +990,17 @@ class Onboarding {
     let tooltip = this._bundle.formatStringFromName(tooltipStringId, [BRAND_SHORT_NAME], 1);
     button.setAttribute("aria-label", tooltip);
     button.id = "onboarding-overlay-button";
     button.setAttribute("aria-haspopup", true);
     button.setAttribute("aria-controls", `${ONBOARDING_DIALOG_ID}`);
     let img = this._window.document.createElement("img");
     img.id = "onboarding-overlay-button-icon";
     img.setAttribute("role", "presentation");
-    img.src = "resource://onboarding/img/overlay-icon.svg";
+    img.src = "chrome://branding/content/icon64.png";
     button.appendChild(img);
     return button;
   }
 
   _loadTours(tours) {
     let itemsFrag = this._window.document.createDocumentFragment();
     let pagesFrag = this._window.document.createDocumentFragment();
     for (let tour of tours) {
--- a/browser/extensions/onboarding/test/browser/browser.ini
+++ b/browser/extensions/onboarding/test/browser/browser.ini
@@ -5,11 +5,12 @@ support-files =
 [browser_onboarding_accessibility.js]
 [browser_onboarding_hide_all.js]
 [browser_onboarding_keyboard.js]
 skip-if = debug || os == "mac" # Full keyboard navigation on OSX only works if Full Keyboard Access setting is set to All Control in System Keyboard Preferences
 [browser_onboarding_notification.js]
 [browser_onboarding_notification_2.js]
 [browser_onboarding_notification_3.js]
 [browser_onboarding_notification_4.js]
+[browser_onboarding_notification_click_auto_complete_tour.js]
 [browser_onboarding_select_default_tour.js]
 [browser_onboarding_tours.js]
 [browser_onboarding_tourset.js]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/onboarding/test/browser/browser_onboarding_notification_click_auto_complete_tour.js
@@ -0,0 +1,33 @@
+add_task(async function test_show_click_auto_complete_tour_in_notification() {
+  resetOnboardingDefaultState();
+  skipMuteNotificationOnFirstSession();
+  // the second tour is an click-auto-complete tour
+  await SpecialPowers.pushPrefEnv({set: [
+    ["browser.onboarding.newtour", "customize,library"],
+  ]});
+
+  let tab = await openTab(ABOUT_NEWTAB_URL);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-button", {}, tab.linkedBrowser);
+  await promiseOnboardingOverlayOpened(tab.linkedBrowser);
+
+  // Trigger CTA button to mark the tour as complete
+  let expectedPrefUpdates = [
+    promisePrefUpdated(`browser.onboarding.tour.onboarding-tour-customize.completed`, true),
+  ]
+  BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize", {}, tab.linkedBrowser);
+  BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-tour-customize-button", {}, tab.linkedBrowser);
+  await Promise.all(expectedPrefUpdates);
+
+  await BrowserTestUtils.synthesizeMouseAtCenter("#onboarding-overlay-close-btn", {}, gBrowser.selectedBrowser);
+  let { activeNavItemId } = await getCurrentActiveTour(tab.linkedBrowser);
+  is("onboarding-tour-customize", activeNavItemId, "the active tour should be the previous shown tour");
+
+  await reloadTab(tab);
+  await promiseOnboardingOverlayLoaded(tab.linkedBrowser);
+  await promiseTourNotificationOpened(tab.linkedBrowser);
+  let targetTourId = await getCurrentNotificationTargetTourId(tab.linkedBrowser);
+  is("onboarding-tour-library", targetTourId, "correctly show the click-autocomplete-tour in notification");
+
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
+++ b/browser/extensions/webcompat-reporter/content/WebCompatReporter.jsm
@@ -23,17 +23,17 @@ let WebCompatReporter = {
   get endpoint() {
     return Services.urlFormatter.formatURLPref(
       "extensions.webcompat-reporter.newIssueEndpoint");
   },
 
   init() {
     PageActions.addAction(new PageActions.Action({
       id: "webcompat-reporter-button",
-      title: wcStrings.GetStringFromName("wc-reporter.label"),
+      title: wcStrings.GetStringFromName("wc-reporter.label2"),
       iconURL: "chrome://webcompat-reporter/skin/lightbulb.svg",
       onCommand: (e) => this.reportIssue(e.target.ownerGlobal),
       onShowingInPanel: (buttonNode) => this.onShowingInPanel(buttonNode)
     }));
   },
 
   uninit() {
     let action = PageActions.actionForID("webcompat-reporter-button");
--- a/browser/extensions/webcompat-reporter/locales/en-US/webcompat.properties
+++ b/browser/extensions/webcompat-reporter/locales/en-US/webcompat.properties
@@ -1,11 +1,10 @@
 # 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/.
 
-# LOCALIZATION NOTE(wc-reporter.label): This string will be used in the
-# Firefox menu panel below its button. Localized length should be considered.
-# \u00ad is included at the beginning of the string to disable auto-hyphens.
-wc-reporter.label=\u00adReport Site Issue
+# LOCALIZATION NOTE(wc-reporter.label2): This string will be used in the
+# Firefox page actions menu. Localized length should be considered.
+wc-reporter.label2=Report Site Issue…
 # LOCALIZATION NOTE(wc-reporter.tooltip): A site compatibility issue is
 # a website bug that exists in one browser (Firefox), but not another.
 wc-reporter.tooltip=Report a site compatibility issue
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -464,27 +464,16 @@ notification[value="translation"] menuli
 
 %include ../shared/autocomplete.inc.css
 %include ../shared/urlbar-autocomplete.inc.css
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] {
   border-top: 1px solid ThreeDShadow;
 }
 
-.autocomplete-richlistitem:hover,
-treechildren.searchbar-treebody::-moz-tree-row(hover) {
-  background-color: var(--arrowpanel-dimmed);
-  border-color: var(--panel-separator-color);
-}
-
-.autocomplete-richlistitem[selected],
-treechildren.searchbar-treebody::-moz-tree-row(selected) {
-  background-color: Highlight;
-}
-
 .ac-title {
   font-size: 1.05em;
 }
 
 .ac-separator,
 .ac-url,
 .ac-action,
 .ac-tags {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -420,28 +420,16 @@
 
 %include ../shared/autocomplete.inc.css
 %include ../shared/urlbar-autocomplete.inc.css
 
 #PopupAutoComplete > richlistbox > richlistitem[originaltype~="datalist-first"] {
   border-top: 1px solid #C7C7C7;
 }
 
-.autocomplete-richlistitem:hover,
-treechildren.searchbar-treebody::-moz-tree-row(hover) {
-  background-color: var(--arrowpanel-dimmed);
-  border-color: var(--panel-separator-color);
-}
-
-.autocomplete-richlistitem[selected],
-treechildren.searchbar-treebody::-moz-tree-row(selected) {
-  background-color: Highlight;
-  color: HighlightText;
-}
-
 .ac-title {
   font-size: 14px;
 }
 
 .ac-separator,
 .ac-url,
 .ac-action,
 .ac-tags {
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -149,70 +149,31 @@ separator.thin:not([orient="vertical"]) 
   justify-content: space-between;
 }
 
 .header[hidden=true] {
   display: none;
 }
 
 /* General Pane */
-
-#startupGroup {
-  margin-top: 0px !important;
-}
-
-#startupTable {
-  margin-top: 32px;
-  margin-inline-end: -4px;
-  border-collapse: collapse;
-}
-
-#startupTable > tr > td {
-  padding: 0; /* remove the padding from html.css */
-}
-
-#startupTable > .tableGroup > td {
+#startupPageBox {
   padding-top: 32px;
 }
 
-#startupTable > .tableSubGroup > td {
-  padding-top: 8px;
-}
-
-#startupTable > tr > .label-cell {
-  text-align: end;
-  width: 0; /* make the column as small as possible */
-}
-
-#startupTable > tr > .content-cell:not(:first-child) {
-  padding-inline-start: 8px;
-}
-
-#startupTable > tr > .label-cell > label {
-  white-space: nowrap;
+#browserHomePage {
+  margin-inline-start: 0;
+  margin-inline-end: 0;
 }
 
-#startupTable > tr > .content-cell > menulist,
-#startupTable > tr > .content-cell > textbox {
-  width: calc(100% - 8px);
-  margin-left: 4px;
-  margin-right: 4px;
+.homepage-button:first-of-type {
+  margin-inline-start: 0;
 }
 
-#startupTable > tr > .homepage-buttons {
-  display: flex;
-  flex-wrap: wrap;
-}
-
-#startupTable > tr > .homepage-buttons > .content-cell-item {
-  flex-grow: 1;
-}
-
-.content-cell-item {
-  margin: 2px 4px;
+.homepage-button:last-of-type {
+  margin-inline-end: 0;
 }
 
 #getStarted {
   font-size: 90%;
 }
 
 #isNotDefaultLabel,
 #signedOutAccountBoxTitle {
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -4,21 +4,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 
 #treecolAutoCompleteImage {
   max-width: 36px;
 }
 
 .autocomplete-richlistbox {
-  padding: 4px;
+  padding: 4px 3px;
 }
 
 .autocomplete-richlistitem {
   min-height: 30px;
   font: message-box;
   border-radius: 2px;
-  border: 1px solid transparent;
 }
 
 :root[uidensity=touch] .autocomplete-richlistitem {
   min-height: 40px;
 }
+
+.autocomplete-richlistitem:hover,
+treechildren.searchbar-treebody::-moz-tree-row(hover) {
+  background-color: var(--arrowpanel-dimmed);
+}
+
+.autocomplete-richlistitem[selected],
+treechildren.searchbar-treebody::-moz-tree-row(selected) {
+  background-color: Highlight;
+  color: HighlightText;
+}
+
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -673,28 +673,16 @@ html|span.ac-emphasize-text-url {
   }
 
   .ac-tags-text[selected] > html|span.ac-tag {
     background-color: HighlightText;
     color: Highlight;
   }
 }
 
-.autocomplete-richlistitem:hover,
-treechildren.searchbar-treebody::-moz-tree-row(hover) {
-  background-color: var(--arrowpanel-dimmed);
-  border-color: var(--panel-separator-color);
-}
-
-.autocomplete-richlistitem[selected],
-treechildren.searchbar-treebody::-moz-tree-row(selected) {
-  background-color: Highlight;
-  color: HighlightText;
-}
-
 .ac-type-icon[type=bookmark] {
   list-style-image: url("chrome://browser/skin/bookmark.svg");
   -moz-context-properties: fill;
   fill: #b2b2b2;
 }
 
 .ac-type-icon[type=bookmark][selected][current] {
   fill: white;
--- a/devtools/client/responsive.html/browser/tunnel.js
+++ b/devtools/client/responsive.html/browser/tunnel.js
@@ -236,28 +236,24 @@ function tunnelToInnerBrowser(outer, inn
       //   * Specific target names (everything treated as _blank)
       //   * Window features
       //   * window.opener
       // These things are deferred for now, since content which does depend on them seems
       // outside the main focus of RDM.
       let { detail } = event;
       event.preventDefault();
       let uri = Services.io.newURI(detail.url);
-      let sourceNode = event.dataTransfer.mozSourceNode;
-      let triggeringPrincipal = sourceNode
-        ? sourceNode.nodePrincipal
-        : Services.scriptSecurityManager.getSystemPrincipal();
       // This API is used mainly because it's near the path used for <a target/> with
       // regular browser tabs (which calls `openURIInFrame`).  The more elaborate APIs
       // that support openers, window features, etc. didn't seem callable from JS and / or
       // this event doesn't give enough info to use them.
       browserWindow.browserDOMWindow
         .openURI(uri, null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB,
                  Ci.nsIBrowserDOMWindow.OPEN_NEW,
-                 triggeringPrincipal);
+                 outer.contentPrincipal);
     },
 
     stop() {
       let tab = gBrowser.getTabForBrowser(outer);
       let filteredProgressListener = gBrowser._tabFilters.get(tab);
 
       // The browser's state has changed over time while the tunnel was active.  Push the
       // the current state down to the inner browser, so that it follows the content in
--- a/devtools/client/responsive.html/test/browser/browser.ini
+++ b/devtools/client/responsive.html/test/browser/browser.ini
@@ -35,15 +35,16 @@ support-files =
 [browser_network_throttling.js]
 [browser_page_state.js]
 [browser_permission_doorhanger.js]
 tags = geolocation
 [browser_resize_cmd.js]
 [browser_screenshot_button.js]
 [browser_tab_close.js]
 [browser_tab_remoteness_change.js]
+[browser_target_blank.js]
 [browser_toolbox_computed_view.js]
 [browser_toolbox_rule_view.js]
 [browser_toolbox_swap_browsers.js]
 [browser_touch_device.js]
 [browser_touch_simulation.js]
 [browser_viewport_basics.js]
 [browser_window_close.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/responsive.html/test/browser/browser_target_blank.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Ensure target="_blank" link opens a new tab
+
+const TAB_URL = "http://example.com/";
+const TEST_URL =
+  `data:text/html,<a href="${TAB_URL}" target="_blank">Click me</a>`
+  .replace(/ /g, "%20");
+
+addRDMTask(TEST_URL, function* ({ ui }) {
+  let store = ui.toolWindow.store;
+
+  // Wait until the viewport has been added
+  yield waitUntilState(store, state => state.viewports.length == 1);
+
+  // Click the target="_blank" link and wait for a new tab
+  yield waitForFrameLoad(ui, TEST_URL);
+  let newTab = BrowserTestUtils.waitForNewTab(gBrowser, TAB_URL);
+  spawnViewportTask(ui, {}, function* () {
+    content.document.querySelector("a").click(); // eslint-disable-line
+  });
+  ok(yield newTab, "New tab opened from link");
+});
--- a/dom/animation/test/mozilla/test_distance_of_transform.html
+++ b/dom/animation/test/mozilla/test_distance_of_transform.html
@@ -65,16 +65,22 @@ function rotate3dToMatrix(x, y, z, radia
     0,
     0,
     1
   ];
 }
 
 test(function(t) {
   var target = addDiv(t);
+  var dist = getDistance(target, 'transform', 'none', 'none');
+  assert_equals(dist, 0, 'distance of translate');
+}, 'Test distance of none and none');
+
+test(function(t) {
+  var target = addDiv(t);
   var dist = getDistance(target, 'transform', 'translate(100px)', 'none');
   assert_equals(dist, 100, 'distance of translate');
 }, 'Test distance of translate function and none');
 
 test(function(t) {
   var target = addDiv(t);
   var dist =
     getDistance(target, 'transform', 'translate(100px)', 'translate(200px)');
@@ -299,10 +305,44 @@ test(function(t) {
   var dist =
     getDistance(target, 'transform',
                 'rotate(180deg) translate(1000px)',
                 'rotate(360deg) translate(0px)');
   assert_equals(dist, Math.sqrt(1000 * 1000 + Math.PI * Math.PI),
                 'distance of transform lists');
 }, 'Test distance of transform lists');
 
+test(function(t) {
+  var target = addDiv(t);
+  var dist = getDistance(target, 'transform',
+                         'translate(100px) rotate(180deg)',
+                         'translate(50px) rotate(90deg) scale(5) skew(1rad)');
+  assert_approx_equals(dist,
+                       Math.sqrt(50 * 50 +
+                                 Math.PI / 2 * Math.PI / 2 +
+                                 4 * 4 * 2 +
+                                 1 * 1),
+                       epsilon,
+                       'distance of transform lists');
+}, 'Test distance of transform lists where one has extra items');
+
+test(function(t) {
+  var target = addDiv(t);
+  var dist = getDistance(target, 'transform',
+                         'translate(1000px) rotate3d(1, 0, 0, 180deg)',
+                         'translate(1000px) scale3d(2.5, 0.5, 1)');
+  assert_equals(dist, Math.sqrt(Math.PI * Math.PI + 1.5 * 1.5 + 0.5 * 0.5),
+                'distance of transform lists');
+}, 'Test distance of mismatched transform lists');
+
+test(function(t) {
+  var target = addDiv(t);
+  var dist = getDistance(target, 'transform',
+                         'translate(100px) skew(1rad)',
+                         'translate(1000px) rotate3d(0, 1, 0, -2rad)');
+  assert_approx_equals(dist,
+                       Math.sqrt(900 * 900 + 1 * 1 + 2 * 2),
+                       epsilon,
+                       'distance of transform lists');
+}, 'Test distance of mismatched transform lists with skew function');
+
 </script>
 </html>
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -444,47 +444,54 @@ private:
 };
 
 /**
  * There is a reference cycle involving this class: MediaLoadListener
  * holds a reference to the HTMLMediaElement, which holds a reference
  * to an nsIChannel, which holds a reference to this listener.
  * We break the reference cycle in OnStartRequest by clearing mElement.
  */
-class HTMLMediaElement::MediaLoadListener final : public nsIStreamListener,
-                                                  public nsIChannelEventSink,
-                                                  public nsIInterfaceRequestor,
-                                                  public nsIObserver
+class HTMLMediaElement::MediaLoadListener final
+  : public nsIStreamListener
+  , public nsIChannelEventSink
+  , public nsIInterfaceRequestor
+  , public nsIObserver
+  , public nsIThreadRetargetableStreamListener
 {
   ~MediaLoadListener() {}
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 
 public:
   explicit MediaLoadListener(HTMLMediaElement* aElement)
     : mElement(aElement),
       mLoadID(aElement->GetCurrentLoadID())
   {
     MOZ_ASSERT(mElement, "Must pass an element to call back");
   }
 
 private:
   RefPtr<HTMLMediaElement> mElement;
   nsCOMPtr<nsIStreamListener> mNextListener;
   const uint32_t mLoadID;
 };
 
-NS_IMPL_ISUPPORTS(HTMLMediaElement::MediaLoadListener, nsIRequestObserver,
-                  nsIStreamListener, nsIChannelEventSink,
-                  nsIInterfaceRequestor, nsIObserver)
+NS_IMPL_ISUPPORTS(HTMLMediaElement::MediaLoadListener,
+                  nsIRequestObserver,
+                  nsIStreamListener,
+                  nsIChannelEventSink,
+                  nsIInterfaceRequestor,
+                  nsIObserver,
+                  nsIThreadRetargetableStreamListener)
 
 NS_IMETHODIMP
 HTMLMediaElement::MediaLoadListener::Observe(nsISupports* aSubject,
                                              const char* aTopic,
                                              const char16_t* aData)
 {
   nsContentUtils::UnregisterShutdownObserver(this);
 
@@ -617,16 +624,28 @@ HTMLMediaElement::MediaLoadListener::Asy
   if (sink) {
     return sink->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, cb);
   }
   cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+HTMLMediaElement::MediaLoadListener::CheckListenerChain()
+{
+  MOZ_ASSERT(mNextListener);
+  nsCOMPtr<nsIThreadRetargetableStreamListener> retargetable =
+    do_QueryInterface(mNextListener);
+  if (retargetable) {
+    return retargetable->CheckListenerChain();
+  }
+  return NS_ERROR_NO_INTERFACE;
+}
+
+NS_IMETHODIMP
 HTMLMediaElement::MediaLoadListener::GetInterface(const nsIID& aIID,
                                                   void** aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
 void HTMLMediaElement::ReportLoadError(const char* aMsg,
                                        const char16_t** aParams,
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -20,16 +20,17 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/TextTrackManager.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/MediaKeys.h"
 #include "mozilla/StateWatching.h"
 #include "nsGkAtoms.h"
 #include "PrincipalChangeObserver.h"
 #include "nsStubMutationObserver.h"
+#include "MediaSegment.h" // for PrincipalHandle
 
 // X.h on Linux #defines CurrentTime as 0L, so we have to #undef it here.
 #ifdef CurrentTime
 #undef CurrentTime
 #endif
 
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 
--- a/dom/media/AudioNotificationReceiver.cpp
+++ b/dom/media/AudioNotificationReceiver.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AudioNotificationReceiver.h"
-#include "AudioStream.h"          // for AudioStream
 #include "mozilla/Logging.h"      // for LazyLogModule
 #include "mozilla/StaticMutex.h"  // for StaticMutex
 #include "mozilla/StaticPtr.h"    // for StaticAutoPtr
 #include "nsAppRunner.h"          // for XRE_IsContentProcess
 #include "nsTArray.h"             // for nsTArray
 
 static mozilla::LazyLogModule sLogger("AudioNotificationReceiver");
 
@@ -20,65 +19,68 @@ static mozilla::LazyLogModule sLogger("A
 #define ANR_LOGW(...) MOZ_LOG(sLogger, mozilla::LogLevel::Warning, (__VA_ARGS__))
 
 namespace mozilla {
 namespace audio {
 
 /*
  * A list containing all clients subscribering the device-changed notifications.
  */
-static StaticAutoPtr<nsTArray<AudioStream*>> sSubscribers;
+static StaticAutoPtr<nsTArray<DeviceChangeListener*>> sSubscribers;
 static StaticMutex sMutex;
 
 /*
  * AudioNotificationReceiver Implementation
  */
 /* static */ void
-AudioNotificationReceiver::Register(AudioStream* aAudioStream)
+AudioNotificationReceiver::Register(DeviceChangeListener* aDeviceChangeListener)
 {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   StaticMutexAutoLock lock(sMutex);
   if (!sSubscribers) {
-    sSubscribers = new nsTArray<AudioStream*>();
+    sSubscribers = new nsTArray<DeviceChangeListener*>();
   }
-  sSubscribers->AppendElement(aAudioStream);
+  sSubscribers->AppendElement(aDeviceChangeListener);
 
-  ANR_LOG("The AudioStream: %p is registered successfully.", aAudioStream);
+  ANR_LOG("The DeviceChangeListener: %p is registered successfully.",
+          aDeviceChangeListener);
 }
 
 /* static */ void
-AudioNotificationReceiver::Unregister(AudioStream* aAudioStream)
+AudioNotificationReceiver::Unregister(DeviceChangeListener* aDeviceChangeListener)
 {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   StaticMutexAutoLock lock(sMutex);
   MOZ_ASSERT(!sSubscribers->IsEmpty(), "No subscriber.");
 
-  sSubscribers->RemoveElement(aAudioStream);
+  sSubscribers->RemoveElement(aDeviceChangeListener);
   if (sSubscribers->IsEmpty()) {
     // Clear the static pointer here to prevent memory leak.
     sSubscribers = nullptr;
   }
 
-  ANR_LOG("The AudioStream: %p is unregistered successfully.", aAudioStream);
+  ANR_LOG("The DeviceChangeListener: %p is unregistered successfully.",
+          aDeviceChangeListener);
 }
 
 /* static */ void
 AudioNotificationReceiver::NotifyDefaultDeviceChanged()
 {
   MOZ_ASSERT(XRE_IsContentProcess());
 
   StaticMutexAutoLock lock(sMutex);
 
-  // Do nothing when there is no AudioStream.
+  // Do nothing when there is no DeviceChangeListener.
   if (!sSubscribers) {
     return;
   }
 
-  for (AudioStream* stream : *sSubscribers) {
-    ANR_LOG("Notify the AudioStream: %p that the default device has been changed.", stream);
+  for (DeviceChangeListener* stream : *sSubscribers) {
+    ANR_LOG("Notify the DeviceChangeListener: %p "
+            "that the default device has been changed.", stream);
     stream->ResetDefaultDevice();
   }
 }
 
 } // namespace audio
 } // namespace mozilla
--- a/dom/media/AudioNotificationReceiver.h
+++ b/dom/media/AudioNotificationReceiver.h
@@ -8,78 +8,88 @@
 #define MOZILLA_AUDIONOTIFICATIONRECEIVER_H_
 
 /*
  * Architecture to send/receive default device-changed notification:
  *
  *  Chrome Process                       ContentProcess 1
  *  ------------------                   ------------------
  *
- *       AudioNotification                       AudioStream 1    AudioStream N
+ *       AudioNotification               DeviceChangeListener 1   DeviceChangeListener N
  *             |      ^                             |      ^         ^
  *          (4)|      |(2)                          |(3)   |(8)      .
  *             v      |                             v      |         v
  *   AudioNotificationSender                  AudioNotificationReceiver
  *     ^       |      ^                                ^
  *     .    (5)|      |(1)                             |(7)
  *     .       v      |                                |
  *     .  (P)ContentParent 1                   (P)ContentChild 1
  *     .          |                                    ^
  *     .       (6)|                                    |
  *     .          |                                    |
  *     .          |                                    |
  *     .          +------------------------------------+
- *     .
+ *     .                      PContent IPC
  *     .
  *     .                                 Content Process M
  *     .                                 ------------------
  *     .                                          .
  *     v                                          .
  *   (P)ContentParent M  < . . . . . . . . .  > (P)ContentChild M
  *                            PContent IPC
  *
  * Steps
  * --------
  *  1) Initailize the AudioNotificationSender when ContentParent is created.
  *  2) Create an AudioNotification to get the device-changed signal
  *     from the system.
- *  3) Register the AudioStream to AudioNotificationReceiver when it's created.
+ *  3) Register the DeviceChangeListener to AudioNotificationReceiver when it's created.
  *  4) When the default device is changed, AudioNotification get the signal and
  *  5) Pass this message by AudioNotificationSender.
  *  6) The AudioNotificationSender sends the device-changed notification via
  *     the PContent.
  *  7) The ContentChild will call AudioNotificationReceiver to
  *  8) Notify all the registered audio streams to reconfigure the output devices.
  *
  * Notes
  * --------
  * a) There is only one AudioNotificationSender and AudioNotification
  *    in a chrome process.
  * b) There is only one AudioNotificationReceiver and might be many
- *    AudioStreams in a content process.
+ *    DeviceChangeListeners in a content process.
  * c) There might be many ContentParent in a chrome process.
  * d) There is only one ContentChild in a content process.
- * e) All the Audiostreams are registered in the AudioNotificationReceiver.
+ * e) All the DeviceChangeListeners are registered in the AudioNotificationReceiver.
  * f) All the ContentParents are registered in the AudioNotificationSender.
  */
 
 namespace mozilla {
+namespace audio {
 
-class AudioStream;
-
-namespace audio {
+// The base class that provides a ResetDefaultDevice interface that
+// will be called in AudioNotificationReceiver::NotifyDefaultDeviceChanged
+// when it receives device-changed notification from the chrome process.
+class DeviceChangeListener
+{
+protected:
+  virtual ~DeviceChangeListener() {};
+public:
+  // The subclass shoule provide its own implementation switching the
+  // audio stream to the new default output device.
+  virtual void ResetDefaultDevice() = 0;
+};
 
 class AudioNotificationReceiver final
 {
 public:
-  // Add the AudioStream into the subscribers list.
-  static void Register(AudioStream* aAudioStream);
+  // Add the DeviceChangeListener into the subscribers list.
+  static void Register(DeviceChangeListener* aDeviceChangeListener);
 
-  // Remove the AudioStream from the subscribers list.
-  static void Unregister(AudioStream* aAudioStream);
+  // Remove the DeviceChangeListener from the subscribers list.
+  static void Unregister(DeviceChangeListener* aDeviceChangeListener);
 
   // Notify all the streams that the default device has been changed.
   static void NotifyDefaultDeviceChanged();
 }; // AudioNotificationReceiver
 
 } // namespace audio
 } // namespace mozilla
 
--- a/dom/media/AudioSegment.h
+++ b/dom/media/AudioSegment.h
@@ -228,16 +228,28 @@ struct AudioChunk {
   template<typename T>
   const nsTArray<const T*>& ChannelData() const
   {
     MOZ_ASSERT(AudioSampleTypeToFormat<T>::Format == mBufferFormat);
     return *reinterpret_cast<const AutoTArray<const T*,GUESS_AUDIO_CHANNELS>*>
       (&mChannelData);
   }
 
+  /**
+   * ChannelFloatsForWrite() should be used only when mBuffer is owned solely
+   * by the calling thread.
+   */
+  template<typename T>
+  T* ChannelDataForWrite(size_t aChannel)
+  {
+    MOZ_ASSERT(AudioSampleTypeToFormat<T>::Format == mBufferFormat);
+    MOZ_ASSERT(!mBuffer->IsShared());
+    return static_cast<T*>(const_cast<void*>(mChannelData[aChannel]));
+  }
+
   PrincipalHandle GetPrincipalHandle() const { return mPrincipalHandle; }
 
   StreamTime mDuration; // in frames within the buffer
   RefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes
   // one pointer per channel; empty if and only if mBuffer is null
   AutoTArray<const void*,GUESS_AUDIO_CHANNELS> mChannelData;
   float mVolume; // volume multiplier to apply (1.0f if mBuffer is nonnull)
   SampleFormat mBufferFormat; // format of frames in mBuffer (only meaningful if mBuffer is nonnull)
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -14,19 +14,16 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/Sprintf.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 #include "CubebUtils.h"
 #include "nsPrintfCString.h"
 #include "gfxPrefs.h"
 #include "AudioConverter.h"
-#if defined(XP_WIN)
-#include "mozilla/audio/AudioNotificationReceiver.h"
-#endif
 
 namespace mozilla {
 
 #undef LOG
 #undef LOGW
 
 LazyLogModule gAudioStreamLog("AudioStream");
 // For simple logs
@@ -470,30 +467,32 @@ AudioStream::Shutdown()
     // Must not try to shut down cubeb from within the lock!  wasapi may still
     // call our callback after Pause()/stop()!?! Bug 996162
     mCubebStream.reset();
   }
 
   mState = SHUTDOWN;
 }
 
+#if defined(XP_WIN)
 void
 AudioStream::ResetDefaultDevice()
 {
   MonitorAutoLock mon(mMonitor);
   if (mState != STARTED && mState != STOPPED) {
     return;
   }
 
   MOZ_ASSERT(mCubebStream);
   auto r = InvokeCubeb(cubeb_stream_reset_default_device);
   if (!(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED)) {
     mState = ERRORED;
   }
 }
+#endif
 
 int64_t
 AudioStream::GetPosition()
 {
   MonitorAutoLock mon(mMonitor);
   int64_t frames = GetPositionInFramesUnlocked();
   return frames >= 0 ? mAudioClock.GetPosition(frames) : -1;
 }
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -12,16 +12,20 @@
 #include "nsThreadUtils.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 #include "CubebUtils.h"
 #include "soundtouch/SoundTouchFactory.h"
 
+#if defined(XP_WIN)
+#include "mozilla/audio/AudioNotificationReceiver.h"
+#endif
+
 namespace mozilla {
 
 struct CubebDestroyPolicy
 {
   void operator()(cubeb_stream* aStream) const {
     cubeb_stream_destroy(aStream);
   }
 };
@@ -145,16 +149,19 @@ public:
   using AudioBufferCursor::Available;
 };
 
 // Access to a single instance of this class must be synchronized by
 // callers, or made from a single thread.  One exception is that access to
 // GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels},
 // SetMicrophoneActive is thread-safe without external synchronization.
 class AudioStream final
+#if defined(XP_WIN)
+  : public audio::DeviceChangeListener
+#endif
 {
   virtual ~AudioStream();
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioStream)
 
   class Chunk {
   public:
@@ -205,18 +212,20 @@ public:
   void Start();
 
   // Pause audio playback.
   void Pause();
 
   // Resume audio playback.
   void Resume();
 
-  // Reset stream to default device.
-  void ResetDefaultDevice();
+#if defined(XP_WIN)
+  // Reset stream to the default device.
+  void ResetDefaultDevice() override;
+#endif
 
   // Return the position in microseconds of the audio frame being played by
   // the audio hardware, compensated for playback rate change. Thread-safe.
   int64_t GetPosition();
 
   // Return the position, measured in audio frames played since the stream
   // was opened, of the audio hardware.  Thread-safe.
   int64_t GetPositionInFrames();
--- a/dom/media/Benchmark.cpp
+++ b/dom/media/Benchmark.cpp
@@ -11,16 +11,17 @@
 #include "MediaPrefs.h"
 #include "PDMFactory.h"
 #include "VideoUtils.h"
 #include "WebMDemuxer.h"
 #include "gfxPrefs.h"
 #include "mozilla/AbstractThread.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/SharedThreadPool.h"
+#include "mozilla/SystemGroup.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/gfx/gfxVars.h"
 
 #ifndef MOZ_WIDGET_ANDROID
 #include "WebMSample.h"
 #endif
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -107,16 +107,19 @@ enum class CubebState {
 } sCubebState = CubebState::Uninitialized;
 cubeb* sCubebContext;
 double sVolumeScale;
 uint32_t sCubebPlaybackLatencyInMilliseconds;
 uint32_t sCubebMSGLatencyInFrames;
 bool sCubebPlaybackLatencyPrefSet;
 bool sCubebMSGLatencyPrefSet;
 bool sAudioStreamInitEverSucceeded = false;
+#ifdef MOZ_CUBEB_REMOTING
+bool sCubebSandbox;
+#endif
 StaticAutoPtr<char> sBrandName;
 StaticAutoPtr<char> sCubebBackendName;
 
 const char kBrandBundleURL[]      = "chrome://branding/locale/brand.properties";
 
 const char* AUDIOSTREAM_BACKEND_ID_STR[] = {
   "jack",
   "pulse",
@@ -244,21 +247,21 @@ void PrefChanged(const char* aPref, void
     } else {
       sCubebBackendName = new char[value.Length() + 1];
       PodCopy(sCubebBackendName.get(), value.get(), value.Length());
       sCubebBackendName[value.Length()] = 0;
     }
   }
 #ifdef MOZ_CUBEB_REMOTING
   else if (strcmp(aPref, PREF_CUBEB_SANDBOX) == 0) {
-    bool cubebSandbox = false;
-    Preferences::GetBool(aPref, cubebSandbox);
-    MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s: %s", PREF_CUBEB_SANDBOX, cubebSandbox ? "true" : "false"));
+    StaticMutexAutoLock lock(sMutex);
+    sCubebSandbox = Preferences::GetBool(aPref);
+    MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false"));
 
-    if (cubebSandbox && !sServerHandle && XRE_IsParentProcess()) {
+    if (sCubebSandbox && !sServerHandle && XRE_IsParentProcess()) {
       MOZ_LOG(gCubebLog, LogLevel::Debug, ("Starting cubeb server..."));
       StartSoundServer();
     }
   }
 #endif
 }
 
 bool GetFirstStream()
@@ -385,21 +388,19 @@ cubeb* GetCubebContextUnlocked()
   if (!sBrandName && NS_IsMainThread()) {
     InitBrandName();
   } else {
     NS_WARNING_ASSERTION(
       sBrandName, "Did not initialize sbrandName, and not on the main thread?");
   }
 
 #ifdef MOZ_CUBEB_REMOTING
-  bool cubebSandbox = false;
-  Preferences::GetBool(PREF_CUBEB_SANDBOX, cubebSandbox);
-  MOZ_LOG(gCubebLog, LogLevel::Info, ("%s: %s", PREF_CUBEB_SANDBOX, cubebSandbox ? "true" : "false"));
+  MOZ_LOG(gCubebLog, LogLevel::Info, ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false"));
 
-  int rv = cubebSandbox
+  int rv = sCubebSandbox
     ? audioipc_client_init(&sCubebContext, sBrandName)
     : cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
 #else // !MOZ_CUBEB_REMOTING
   int rv = cubeb_init(&sCubebContext, sBrandName, sCubebBackendName.get());
 #endif // MOZ_CUBEB_REMOTING
   NS_WARNING_ASSERTION(rv == CUBEB_OK, "Could not get a cubeb context.");
   sCubebState = (rv == CUBEB_OK) ? CubebState::Initialized : CubebState::Uninitialized;
 
--- a/dom/media/FileBlockCache.cpp
+++ b/dom/media/FileBlockCache.cpp
@@ -8,16 +8,17 @@
 #include "MediaCache.h"
 #include "MediaPrefs.h"
 #include "mozilla/SharedThreadPool.h"
 #include "VideoUtils.h"
 #include "prio.h"
 #include <algorithm>
 #include "nsAnonymousTemporaryFile.h"
 #include "mozilla/dom/ContentChild.h"
+#include "mozilla/SystemGroup.h"
 #include "nsXULAppAPI.h"
 
 namespace mozilla {
 
 #undef LOG
 LazyLogModule gFileBlockCacheLog("FileBlockCache");
 #define LOG(x, ...) MOZ_LOG(gFileBlockCacheLog, LogLevel::Debug, \
   ("%p " x, this, ##__VA_ARGS__))
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -569,21 +569,31 @@ AudioCallbackDriver::AudioCallbackDriver
   , mStarted(false)
   , mAudioInput(nullptr)
   , mAddedMixer(false)
   , mInCallback(false)
   , mMicrophoneActive(false)
   , mFromFallback(false)
 {
   LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
+#if defined(XP_WIN)
+  if (XRE_IsContentProcess()) {
+    audio::AudioNotificationReceiver::Register(this);
+  }
+#endif
 }
 
 AudioCallbackDriver::~AudioCallbackDriver()
 {
   MOZ_ASSERT(mPromisesForOperation.IsEmpty());
+#if defined(XP_WIN)
+  if (XRE_IsContentProcess()) {
+    audio::AudioNotificationReceiver::Unregister(this);
+  }
+#endif
 }
 
 bool IsMacbookOrMacbookAir()
 {
 #ifdef XP_MACOSX
   size_t len = 0;
   sysctlbyname("hw.model", NULL, &len, NULL, 0);
   if (len) {
@@ -852,16 +862,26 @@ AudioCallbackDriver::WaitForNextIteratio
 
 void
 AudioCallbackDriver::WakeUp()
 {
   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
   mGraphImpl->GetMonitor().Notify();
 }
 
+#if defined(XP_WIN)
+void
+AudioCallbackDriver::ResetDefaultDevice()
+{
+  if (cubeb_stream_reset_default_device(mAudioStream) != CUBEB_OK) {
+    NS_WARNING("Could not reset cubeb stream to default output device.");
+  }
+}
+#endif
+
 /* static */ long
 AudioCallbackDriver::DataCallback_s(cubeb_stream* aStream,
                                     void* aUser,
                                     const void* aInputBuffer,
                                     void* aOutputBuffer,
                                     long aFrames)
 {
   AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -10,16 +10,20 @@
 #include "AudioBufferUtils.h"
 #include "AudioMixer.h"
 #include "AudioSegment.h"
 #include "SelfRef.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/StaticPtr.h"
 
+#if defined(XP_WIN)
+#include "mozilla/audio/AudioNotificationReceiver.h"
+#endif
+
 struct cubeb_stream;
 
 template <>
 class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
 {
 public:
   static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
 };
@@ -371,29 +375,35 @@ enum AsyncCubebOperation {
  *   sometimes hardware components are involved and need to be warmed up)
  * - We have no control on how much audio we generate, we have to return exactly
  *   the number of frames asked for by the callback. Since for the Web Audio
  *   API, we have to do block processing at 128 frames per block, we need to
  *   keep a little spill buffer to store the extra frames.
  */
 class AudioCallbackDriver : public GraphDriver,
                             public MixerCallbackReceiver
+#if defined(XP_WIN)
+                            , public audio::DeviceChangeListener
+#endif
 {
 public:
   explicit AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl);
   virtual ~AudioCallbackDriver();
 
   void Destroy() override;
   void Start() override;
   void Stop() override;
   void Resume() override;
   void Revive() override;
   void RemoveCallback() override;
   void WaitForNextIteration() override;
   void WakeUp() override;
+#if defined(XP_WIN)
+  void ResetDefaultDevice() override;
+#endif
 
   /* Static wrapper function cubeb calls back. */
   static long DataCallback_s(cubeb_stream * aStream,
                              void * aUser,
                              const void * aInputBuffer,
                              void * aOutputBuffer,
                              long aFrames);
   static void StateCallback_s(cubeb_stream* aStream, void * aUser,
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -8,17 +8,17 @@
 
 #include "mozilla/UniquePtr.h"
 #include "mozilla/RefPtr.h"
 #include "nsDataHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "ImageTypes.h"
 #include "MediaData.h"
-#include "StreamTracks.h" // for TrackID
+#include "TrackID.h" // for TrackID
 #include "TimeUnits.h"
 #include "mozilla/gfx/Point.h" // for gfx::IntSize
 #include "mozilla/gfx/Rect.h"  // for gfx::IntRect
 
 namespace mozilla {
 
 class AudioInfo;
 class VideoInfo;
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -117,18 +117,21 @@ ChannelMediaResource::~ChannelMediaResou
 
 // ChannelMediaResource::Listener just observes the channel and
 // forwards notifications to the ChannelMediaResource. We use multiple
 // listener objects so that when we open a new stream for a seek we can
 // disconnect the old listener from the ChannelMediaResource and hook up
 // a new listener, so notifications from the old channel are discarded
 // and don't confuse us.
 NS_IMPL_ISUPPORTS(ChannelMediaResource::Listener,
-                  nsIRequestObserver, nsIStreamListener, nsIChannelEventSink,
-                  nsIInterfaceRequestor)
+                  nsIRequestObserver,
+                  nsIStreamListener,
+                  nsIChannelEventSink,
+                  nsIInterfaceRequestor,
+                  nsIThreadRetargetableStreamListener)
 
 nsresult
 ChannelMediaResource::Listener::OnStartRequest(nsIRequest* aRequest,
                                                nsISupports* aContext)
 {
   if (!mResource)
     return NS_OK;
   return mResource->OnStartRequest(aRequest);
@@ -169,17 +172,23 @@ ChannelMediaResource::Listener::AsyncOnC
   if (NS_FAILED(rv))
     return rv;
 
   cb->OnRedirectVerifyCallback(NS_OK);
   return NS_OK;
 }
 
 nsresult
-ChannelMediaResource::Listener::GetInterface(const nsIID & aIID, void **aResult)
+ChannelMediaResource::Listener::CheckListenerChain()
+{
+  return NS_OK;
+}
+
+nsresult
+ChannelMediaResource::Listener::GetInterface(const nsIID& aIID, void** aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
 static bool
 IsPayloadCompressed(nsIHttpChannel* aChannel)
 {
   nsAutoCString encoding;
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -8,16 +8,17 @@
 
 #include "mozilla/Mutex.h"
 #include "nsIChannel.h"
 #include "nsIURI.h"
 #include "nsISeekableStream.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
+#include "nsIThreadRetargetableStreamListener.h"
 #include "Intervals.h"
 #include "MediaCache.h"
 #include "MediaContainerType.h"
 #include "MediaData.h"
 #include "MediaPrefs.h"
 #include "MediaResourceCallback.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/Attributes.h"
@@ -498,29 +499,32 @@ public:
 
     return size;
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
-  class Listener final : public nsIStreamListener,
-                         public nsIInterfaceRequestor,
-                         public nsIChannelEventSink
+  class Listener final
+    : public nsIStreamListener
+    , public nsIInterfaceRequestor
+    , public nsIChannelEventSink
+    , public nsIThreadRetargetableStreamListener
   {
     ~Listener() {}
   public:
     explicit Listener(ChannelMediaResource* aResource) : mResource(aResource) {}
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSICHANNELEVENTSINK
     NS_DECL_NSIINTERFACEREQUESTOR
+    NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
 
     void Revoke() { mResource = nullptr; }
 
   private:
     RefPtr<ChannelMediaResource> mResource;
   };
   friend class Listener;
 
--- a/dom/media/SharedBuffer.h
+++ b/dom/media/SharedBuffer.h
@@ -9,28 +9,33 @@
 #include "mozilla/CheckedInt.h"
 #include "mozilla/mozalloc.h"
 #include "mozilla/MemoryReporting.h"
 #include "nsISupportsImpl.h"
 
 namespace mozilla {
 
 class AudioBlockBuffer;
+class ThreadSharedFloatArrayBufferList;
 
 /**
  * Base class for objects with a thread-safe refcount and a virtual
  * destructor.
  */
 class ThreadSharedObject {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadSharedObject)
 
   bool IsShared() { return mRefCnt.get() > 1; }
 
   virtual AudioBlockBuffer* AsAudioBlockBuffer() { return nullptr; };
+  virtual ThreadSharedFloatArrayBufferList* AsThreadSharedFloatArrayBufferList()
+  {
+    return nullptr;
+  };
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   {
     return 0;
   }
 
   virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   {
@@ -48,34 +53,52 @@ protected:
  * This only guarantees 4-byte alignment of the data. For alignment we simply
  * assume that the memory from malloc is at least 4-byte aligned and the
  * refcount's size is large enough that SharedBuffer's size is divisible by 4.
  */
 class SharedBuffer : public ThreadSharedObject {
 public:
   void* Data() { return this + 1; }
 
+  static already_AddRefed<SharedBuffer> Create(size_t aSize, const fallible_t&)
+  {
+    void* m = operator new(AllocSize(aSize), fallible);
+    if (!m) {
+      return nullptr;
+    }
+    RefPtr<SharedBuffer> p = new (m) SharedBuffer();
+    return p.forget();
+  }
+
   static already_AddRefed<SharedBuffer> Create(size_t aSize)
   {
-    CheckedInt<size_t> size = sizeof(SharedBuffer);
-    size += aSize;
-    if (!size.isValid()) {
-      MOZ_CRASH();
-    }
-    void* m = moz_xmalloc(size.value());
+    void* m = operator new(AllocSize(aSize));
     RefPtr<SharedBuffer> p = new (m) SharedBuffer();
-    NS_ASSERTION((reinterpret_cast<char*>(p.get() + 1) - reinterpret_cast<char*>(p.get())) % 4 == 0,
-                 "SharedBuffers should be at least 4-byte aligned");
     return p.forget();
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
-  SharedBuffer() {}
+  static size_t
+  AllocSize(size_t aDataSize)
+  {
+    CheckedInt<size_t> size = sizeof(SharedBuffer);
+    size += aDataSize;
+    if (!size.isValid()) {
+      MOZ_CRASH();
+    }
+    return size.value();
+  }
+
+  SharedBuffer()
+  {
+    NS_ASSERTION((reinterpret_cast<char*>(this + 1) - reinterpret_cast<char*>(this)) % 4 == 0,
+                 "SharedBuffers should be at least 4-byte aligned");
+  }
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_SHAREDBUFFER_H_ */
--- a/dom/media/StreamTracks.h
+++ b/dom/media/StreamTracks.h
@@ -3,33 +3,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/. */
 
 #ifndef MOZILLA_STREAMTRACKS_H_
 #define MOZILLA_STREAMTRACKS_H_
 
 #include "MediaSegment.h"
 #include "nsAutoPtr.h"
+#include "TrackID.h"
 
 namespace mozilla {
 
-/**
- * Unique ID for track within a StreamTracks. Tracks from different
- * StreamTrackss may have the same ID; this matters when appending StreamTrackss,
- * since tracks with the same ID are matched. Only IDs greater than 0 are allowed.
- */
-typedef int32_t TrackID;
-const TrackID TRACK_NONE = 0;
-const TrackID TRACK_INVALID = -1;
-const TrackID TRACK_ANY = -2;
-
-inline bool IsTrackIDExplicit(const TrackID& aId) {
-  return aId > TRACK_NONE;
-}
-
 inline TrackTicks RateConvertTicksRoundDown(TrackRate aOutRate,
                                             TrackRate aInRate,
                                             TrackTicks aTicks)
 {
   NS_ASSERTION(0 < aOutRate && aOutRate <= TRACK_RATE_MAX, "Bad out rate");
   NS_ASSERTION(0 < aInRate && aInRate <= TRACK_RATE_MAX, "Bad in rate");
   NS_ASSERTION(0 <= aTicks && aTicks <= TRACK_TICKS_MAX, "Bad ticks");
   return (aTicks * aOutRate) / aInRate;
new file mode 100644
--- /dev/null
+++ b/dom/media/TrackID.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ #ifndef MOZILLA_TRACK_ID_H_
+ #define MOZILLA_TRACK_ID_H_
+
+ namespace mozilla {
+
+ /**
+  * Unique ID for track within a StreamTracks. Tracks from different
+  * StreamTrackss may have the same ID; this matters when appending StreamTrackss,
+  * since tracks with the same ID are matched. Only IDs greater than 0 are allowed.
+  */
+ typedef int32_t TrackID;
+ const TrackID TRACK_NONE = 0;
+ const TrackID TRACK_INVALID = -1;
+ const TrackID TRACK_ANY = -2;
+
+ inline bool IsTrackIDExplicit(const TrackID& aId) {
+   return aId > TRACK_NONE;
+ }
+
+} // namespace mozilla
+
+#endif // MOZILLA_TRACK_ID_H_
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -35,16 +35,17 @@
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
 #include "GMPCrashHelper.h"
 
 #include "mozilla/dom/PluginCrashedEvent.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/SystemGroup.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 LogModule*
--- a/dom/media/gmp/GMPServiceChild.cpp
+++ b/dom/media/gmp/GMPServiceChild.cpp
@@ -14,16 +14,17 @@
 #include "GMPContentParent.h"
 #include "nsXPCOMPrivate.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/StaticMutex.h"
 #include "runnable_utils.h"
 #include "base/task.h"
 #include "nsIObserverService.h"
 #include "nsComponentManagerUtils.h"
+#include "mozilla/SystemGroup.h"
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
 
 #define LOGD(msg) MOZ_LOG(GetGMPLog(), mozilla::LogLevel::Debug, msg)
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -35,16 +35,17 @@
 #include "nsDirectoryServiceDefs.h"
 #include "nsHashKeys.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIXULRuntime.h"
 #include "GMPDecoderModule.h"
 #include <limits>
 #include "MediaPrefs.h"
+#include "mozilla/SystemGroup.h"
 
 using mozilla::ipc::Transport;
 
 namespace mozilla {
 
 #ifdef LOG
 #undef LOG
 #endif
--- a/dom/media/hls/HLSDecoder.cpp
+++ b/dom/media/hls/HLSDecoder.cpp
@@ -1,54 +1,105 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "HLSDecoder.h"
 #include "AndroidBridge.h"
 #include "DecoderTraits.h"
+#include "GeneratedJNINatives.h"
+#include "GeneratedJNIWrappers.h"
 #include "HLSDemuxer.h"
-#include "HLSResource.h"
 #include "HLSUtils.h"
 #include "MediaContainerType.h"
 #include "MediaDecoderStateMachine.h"
 #include "MediaFormatReader.h"
 #include "MediaPrefs.h"
 #include "MediaShutdownManager.h"
+#include "nsContentUtils.h"
 #include "nsNetUtil.h"
 
+using namespace mozilla::java;
+
 namespace mozilla {
 
+class HLSResourceCallbacksSupport
+  : public GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport>
+{
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport)
+public:
+  typedef GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> NativeCallbacks;
+  using NativeCallbacks::DisposeNative;
+  using NativeCallbacks::AttachNative;
+
+  HLSResourceCallbacksSupport(HLSDecoder* aResource);
+  void Detach();
+  void OnDataArrived();
+  void OnError(int aErrorCode);
+
+private:
+  ~HLSResourceCallbacksSupport() {}
+  HLSDecoder* mDecoder;
+};
+
+HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSDecoder* aDecoder)
+{
+  MOZ_ASSERT(aDecoder);
+  mDecoder = aDecoder;
+}
+
 void
-HLSDecoder::Shutdown()
+HLSResourceCallbacksSupport::Detach()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mResource) {
-    mResource->Detach();
+  mDecoder = nullptr;
+}
+
+void
+HLSResourceCallbacksSupport::OnDataArrived()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mDecoder) {
+    HLS_DEBUG("HLSResourceCallbacksSupport", "OnDataArrived");
+    mDecoder->NotifyDataArrived();
   }
-  MediaDecoder::Shutdown();
+}
+
+void
+HLSResourceCallbacksSupport::OnError(int aErrorCode)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (mDecoder) {
+    HLS_DEBUG("HLSResourceCallbacksSupport", "onError(%d)", aErrorCode);
+    // Since HLS source should be from the Internet, we treat all resource errors
+    // from GeckoHlsPlayer as network errors.
+    mDecoder->NetworkError();
+  }
+}
+
+HLSDecoder::HLSDecoder(MediaDecoderInit& aInit)
+  : MediaDecoder(aInit)
+{
 }
 
 MediaDecoderStateMachine*
 HLSDecoder::CreateStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  MOZ_ASSERT(mResource);
-  auto resourceWrapper = mResource->GetResourceWrapper();
-  MOZ_ASSERT(resourceWrapper);
   MediaFormatReaderInit init;
   init.mVideoFrameContainer = GetVideoFrameContainer();
   init.mKnowsCompositor = GetCompositor();
   init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
   init.mFrameStats = mFrameStats;
   mReader =
-    new MediaFormatReader(init, new HLSDemuxer(resourceWrapper->GetPlayerId()));
+    new MediaFormatReader(init, new HLSDemuxer(mHLSResourceWrapper->GetPlayerId()));
 
   return new MediaDecoderStateMachine(this, mReader);
 }
 
 bool
 HLSDecoder::IsEnabled()
 {
   return MediaPrefs::HLSEnabled() && (jni::GetAPIVersion() >= 16);
@@ -60,84 +111,110 @@ HLSDecoder::IsSupportedType(const MediaC
   return IsEnabled() &&
          DecoderTraits::IsHttpLiveStreamingType(aContainerType);
 }
 
 nsresult
 HLSDecoder::Load(nsIChannel* aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!mResource);
 
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mURI));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  mResource = MakeUnique<HLSResource>(this, aChannel, uri);
+  mChannel = aChannel;
+  nsCString spec;
+  Unused << mURI->GetSpec(spec);;
+  HLSResourceCallbacksSupport::Init();
+  mJavaCallbacks = GeckoHLSResourceWrapper::Callbacks::New();
+  mCallbackSupport = new HLSResourceCallbacksSupport(this);
+  HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);
+  mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(NS_ConvertUTF8toUTF16(spec),
+                                                              mJavaCallbacks);
+  MOZ_ASSERT(mHLSResourceWrapper);
 
   rv = MediaShutdownManager::Instance().Register(this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   SetStateMachine(CreateStateMachine());
   NS_ENSURE_TRUE(GetStateMachine(), NS_ERROR_FAILURE);
 
   return InitializeStateMachine();
 }
 
 void
 HLSDecoder::AddSizeOfResources(ResourceSizes* aSizes)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mResource) {
-    aSizes->mByteSize += mResource->SizeOfIncludingThis(aSizes->mMallocSizeOf);
-  }
+  // TODO: track JAVA wrappers.
 }
 
 already_AddRefed<nsIPrincipal>
 HLSDecoder::GetCurrentPrincipal()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  return mResource ? mResource->GetCurrentPrincipal() : nullptr;
+  nsCOMPtr<nsIPrincipal> principal;
+  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
+  if (!secMan || !mChannel) {
+    return nullptr;
+  }
+  secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
+  return principal.forget();
 }
 
 nsresult
 HLSDecoder::Play()
 {
   MOZ_ASSERT(NS_IsMainThread());
   HLS_DEBUG("HLSDecoder", "MediaElement called Play");
-  auto resourceWrapper = mResource->GetResourceWrapper();
-  resourceWrapper->Play();
+  mHLSResourceWrapper->Play();
   return MediaDecoder::Play();
 }
 
 void
 HLSDecoder::Pause()
 {
   MOZ_ASSERT(NS_IsMainThread());
   HLS_DEBUG("HLSDecoder", "MediaElement called Pause");
-  auto resourceWrapper = mResource->GetResourceWrapper();
-  resourceWrapper->Pause();
+  mHLSResourceWrapper->Pause();
   return MediaDecoder::Pause();
 }
 
 void
 HLSDecoder::Suspend()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mResource) {
-    mResource->Suspend();
-  }
+  HLS_DEBUG("HLSDecoder", "Should suspend the resource fetching.");
+  mHLSResourceWrapper->Suspend();
 }
 
 void
 HLSDecoder::Resume()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mResource) {
-    mResource->Resume();
+  HLS_DEBUG("HLSDecoder", "Should resume the resource fetching.");
+  mHLSResourceWrapper->Resume();
+}
+
+void
+HLSDecoder::Shutdown()
+{
+  HLS_DEBUG("HLSDecoder", "Shutdown");
+  if (mCallbackSupport) {
+    mCallbackSupport->Detach();
+    mCallbackSupport = nullptr;
   }
+  if (mHLSResourceWrapper) {
+    mHLSResourceWrapper->Destroy();
+    mHLSResourceWrapper = nullptr;
+  }
+  if (mJavaCallbacks) {
+    HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks);
+    mJavaCallbacks = nullptr;
+  }
+  MediaDecoder::Shutdown();
 }
 
 } // namespace mozilla
--- a/dom/media/hls/HLSDecoder.h
+++ b/dom/media/hls/HLSDecoder.h
@@ -2,31 +2,27 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef HLSDecoder_h_
 #define HLSDecoder_h_
 
-#include "HLSResource.h"
 #include "MediaDecoder.h"
 
 namespace mozilla {
 
+class HLSResourceCallbacksSupport;
+
 class HLSDecoder final : public MediaDecoder
 {
 public:
   // MediaDecoder interface.
-  explicit HLSDecoder(MediaDecoderInit& aInit)
-    : MediaDecoder(aInit)
-  {
-  }
-
-  void Shutdown() override;
+  explicit HLSDecoder(MediaDecoderInit& aInit);
 
   // Returns true if the HLS backend is pref'ed on.
   static bool IsEnabled();
 
   // Returns true if aContainerType is an HLS type that we think we can render
   // with the a platform decoder backend.
   // If provided, codecs are checked for support.
   static bool IsSupportedType(const MediaContainerType& aContainerType);
@@ -37,30 +33,37 @@ public:
 
   void Pause() override;
 
   void AddSizeOfResources(ResourceSizes* aSizes) override;
   already_AddRefed<nsIPrincipal> GetCurrentPrincipal() override;
   bool IsTransportSeekable() override { return true; }
   void Suspend() override;
   void Resume() override;
+  void Shutdown() override;
 
 private:
+  friend class HLSResourceCallbacksSupport;
+
   void PinForSeek() override {}
   void UnpinForSeek() override {}
 
   MediaDecoderStateMachine* CreateStateMachine();
 
   bool CanPlayThroughImpl() override final
   {
     // TODO: We don't know how to estimate 'canplaythrough' for this decoder.
     // For now we just return true for 'autoplay' can work.
     return true;
   }
 
   bool IsLiveStream() override final { return false; }
 
-  UniquePtr<HLSResource> mResource;
+  nsCOMPtr<nsIChannel> mChannel;
+  nsCOMPtr<nsIURI> mURI;
+  java::GeckoHLSResourceWrapper::GlobalRef mHLSResourceWrapper;
+  java::GeckoHLSResourceWrapper::Callbacks::GlobalRef mJavaCallbacks;
+  RefPtr<HLSResourceCallbacksSupport> mCallbackSupport;
 };
 
 } // namespace mozilla
 
 #endif /* HLSDecoder_h_ */
deleted file mode 100644
--- a/dom/media/hls/HLSResource.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 "HLSDecoder.h"
-#include "HLSResource.h"
-#include "HLSUtils.h"
-
-using namespace mozilla::java;
-
-namespace mozilla {
-
-HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSResource* aResource)
-{
-  MOZ_ASSERT(aResource);
-  mResource = aResource;
-}
-
-void
-HLSResourceCallbacksSupport::Detach()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  mResource = nullptr;
-}
-
-void
-HLSResourceCallbacksSupport::OnDataArrived()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (mResource) {
-    mResource->onDataAvailable();
-  }
-}
-
-void
-HLSResourceCallbacksSupport::OnError(int aErrorCode)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (mResource) {
-    mResource->onError(aErrorCode);
-  }
-}
-
-HLSResource::HLSResource(HLSDecoder* aDecoder,
-                         nsIChannel* aChannel,
-                         nsIURI* aURI)
-  : mDecoder(aDecoder)
-  , mChannel(aChannel)
-  , mURI(aURI)
-{
-  nsCString spec;
-  nsresult rv = aURI->GetSpec(spec);
-  (void)rv;
-  HLSResourceCallbacksSupport::Init();
-  mJavaCallbacks = GeckoHLSResourceWrapper::Callbacks::New();
-  mCallbackSupport = new HLSResourceCallbacksSupport(this);
-  HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);
-  mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(NS_ConvertUTF8toUTF16(spec),
-                                                              mJavaCallbacks);
-  MOZ_ASSERT(mHLSResourceWrapper);
-}
-
-void
-HLSResource::onDataAvailable()
-{
-  HLS_DEBUG("HLSResource", "onDataAvailable");
-  if (mDecoder) {
-    mDecoder->NotifyDataArrived();
-  }
-}
-
-void
-HLSResource::onError(int aErrorCode)
-{
-  HLS_DEBUG("HLSResource", "onError(%d)", aErrorCode);
-  // Since HLS source should be from the Internet, we treat all resource errors
-  // from GeckoHlsPlayer as network errors.
-  if (mDecoder) {
-    mDecoder->NetworkError();
-  }
-}
-
-void
-HLSResource::Suspend()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Don't call on non-main thread");
-  HLS_DEBUG("HLSResource", "Should suspend the resource fetching.");
-  mHLSResourceWrapper->Suspend();
-}
-
-void HLSResource::Resume()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Don't call on non-main thread");
-  HLS_DEBUG("HLSResource", "Should resume the resource fetching.");
-  mHLSResourceWrapper->Resume();
-}
-
-HLSResource::~HLSResource()
-{
-  HLS_DEBUG("HLSResource", "~HLSResource()");
-  if (mCallbackSupport) {
-    mCallbackSupport->Detach();
-    mCallbackSupport = nullptr;
-  }
-  if (mHLSResourceWrapper) {
-    mHLSResourceWrapper->Destroy();
-    mHLSResourceWrapper = nullptr;
-  }
-  if (mJavaCallbacks) {
-      HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks);
-      mJavaCallbacks = nullptr;
-  }
-}
-
-} // namespace mozilla
deleted file mode 100644
--- a/dom/media/hls/HLSResource.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim:set ts=2 sw=2 sts=2 et cindent: */
-/* 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 HLSResource_h_
-#define HLSResource_h_
-
-#include "GeneratedJNINatives.h"
-#include "GeneratedJNIWrappers.h"
-#include "HLSUtils.h"
-#include "nsContentUtils.h"
-
-using namespace mozilla::java;
-
-namespace mozilla {
-
-class HLSDecoder;
-class HLSResource;
-
-class HLSResourceCallbacksSupport
-  : public GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport>
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport)
-public:
-  typedef GeckoHLSResourceWrapper::Callbacks::Natives<HLSResourceCallbacksSupport> NativeCallbacks;
-  using NativeCallbacks::DisposeNative;
-  using NativeCallbacks::AttachNative;
-
-  HLSResourceCallbacksSupport(HLSResource* aResource);
-  void Detach();
-  void OnDataArrived();
-  void OnError(int aErrorCode);
-
-private:
-  ~HLSResourceCallbacksSupport() {}
-  HLSResource* mResource;
-};
-
-class HLSResource final
-{
-public:
-  HLSResource(HLSDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI);
-  ~HLSResource();
-  void Suspend();
-  void Resume();
-
-  already_AddRefed<nsIPrincipal> GetCurrentPrincipal()
-  {
-    NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
-
-    nsCOMPtr<nsIPrincipal> principal;
-    nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-    if (!secMan || !mChannel)
-      return nullptr;
-    secMan->GetChannelResultPrincipal(mChannel, getter_AddRefs(principal));
-    return principal.forget();
-  }
-
-  java::GeckoHLSResourceWrapper::GlobalRef GetResourceWrapper() {
-    return mHLSResourceWrapper;
-  }
-
-  void Detach() { mDecoder = nullptr; }
-
-  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    // TODO: track JAVA wrappers.
-    return 0;
-  }
-
-  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
-  }
-
-private:
-  friend class HLSResourceCallbacksSupport;
-
-  void onDataAvailable();
-  void onError(int aErrorCode);
-
-  HLSDecoder* mDecoder;
-  nsCOMPtr<nsIChannel> mChannel;
-  nsCOMPtr<nsIURI> mURI;
-  java::GeckoHLSResourceWrapper::GlobalRef mHLSResourceWrapper;
-  java::GeckoHLSResourceWrapper::Callbacks::GlobalRef mJavaCallbacks;
-  RefPtr<HLSResourceCallbacksSupport> mCallbackSupport;
-};
-
-} // namespace mozilla
-#endif /* HLSResource_h_ */
--- a/dom/media/hls/moz.build
+++ b/dom/media/hls/moz.build
@@ -2,24 +2,22 @@
 # 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/.
 
 
 EXPORTS += [
     'HLSDecoder.h',
     'HLSDemuxer.h',
-    'HLSResource.h',
     'HLSUtils.h',
 ]
 
 UNIFIED_SOURCES += [
     'HLSDecoder.cpp',
     'HLSDemuxer.cpp',
-    'HLSResource.cpp',
     'HLSUtils.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
--- a/dom/media/ipc/PVideoDecoderManager.ipdl
+++ b/dom/media/ipc/PVideoDecoderManager.ipdl
@@ -12,17 +12,25 @@ using struct mozilla::layers::TextureFac
 
 namespace mozilla {
 namespace dom {
 
 sync protocol PVideoDecoderManager
 {
   manages PVideoDecoder;
 parent:
-  sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier) returns (bool success);
+  // aBlacklistedD3D11Driver and aBlacklistedD3D9Driver are used to read back the blacklisted driver information
+  // from GPU process to content process.
+  // We should have added a new sync method to read back this information but, in that way, we also introduce one
+  // more sync IPC call.
+  // Considering that this information is only used for telemetry usage in bug 1393392 and should be removed once
+  // we have collected enough data, we add these two return values here for convenience.
+  sync PVideoDecoder(VideoInfo info, TextureFactoryIdentifier identifier) returns (bool success,
+                                                                                   nsCString aBlacklistedD3D11Driver,
+                                                                                   nsCString aBlacklistedD3D9Driver);
 
   sync Readback(SurfaceDescriptorGPUVideo sd) returns (SurfaceDescriptor aResult);
 
   async DeallocateSurfaceDescriptorGPUVideo(SurfaceDescriptorGPUVideo sd);
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/ipc/VideoDecoderChild.cpp
+++ b/dom/media/ipc/VideoDecoderChild.cpp
@@ -1,29 +1,48 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=99: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "VideoDecoderChild.h"
 #include "VideoDecoderManagerChild.h"
 #include "mozilla/layers/TextureClient.h"
+#include "mozilla/Telemetry.h"
 #include "base/thread.h"
 #include "MediaInfo.h"
 #include "ImageContainer.h"
 #include "GPUVideoImage.h"
 
 namespace mozilla {
 namespace dom {
 
 using base::Thread;
 using namespace ipc;
 using namespace layers;
 using namespace gfx;
 
+#ifdef XP_WIN
+static void
+ReportUnblacklistingTelemetry(bool isGPUProcessCrashed,
+                              const nsCString& aD3D11BlacklistedDriver,
+                              const nsCString& aD3D9BlacklistedDriver)
+{
+  const nsCString& blacklistedDLL = !aD3D11BlacklistedDriver.IsEmpty()
+                                    ? aD3D11BlacklistedDriver
+                                    : aD3D9BlacklistedDriver;
+
+  if (!blacklistedDLL.IsEmpty()) {
+    Telemetry::Accumulate(Telemetry::VIDEO_UNBLACKINGLISTING_DXVA_DRIVER_RUNTIME_STATUS,
+                          blacklistedDLL,
+                          isGPUProcessCrashed ? 1 : 0);
+  }
+}
+#endif // XP_WIN
+
 VideoDecoderChild::VideoDecoderChild()
   : mThread(VideoDecoderManagerChild::GetManagerThread())
   , mCanSend(false)
   , mInitialized(false)
   , mIsHardwareAccelerated(false)
   , mConversion(MediaDataDecoder::ConversionRequired::kNeedNone)
   , mNeedNewDecoder(false)
 {
@@ -139,16 +158,22 @@ VideoDecoderChild::ActorDestroy(ActorDes
           mNeedNewDecoder = true;
         } else {
           ref->mInitPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_NEED_NEW_DECODER,
                                            __func__);
         }
       }));
   }
   mCanSend = false;
+
+#ifdef XP_WIN
+  ReportUnblacklistingTelemetry(aWhy == AbnormalShutdown,
+                                mBlacklistedD3D11Driver,
+                                mBlacklistedD3D9Driver);
+#endif // XP_WIN
 }
 
 bool
 VideoDecoderChild::InitIPDL(const VideoInfo& aVideoInfo,
                             const layers::TextureFactoryIdentifier& aIdentifier)
 {
   RefPtr<VideoDecoderManagerChild> manager =
     VideoDecoderManagerChild::GetSingleton();
@@ -168,17 +193,19 @@ VideoDecoderChild::InitIPDL(const VideoI
   // available. If not, then the cycle repeats until we're ready.
   if (!manager->CanSend()) {
     return true;
   }
 
   mIPDLSelfRef = this;
   bool success = false;
   if (manager->SendPVideoDecoderConstructor(this, aVideoInfo, aIdentifier,
-                                            &success)) {
+                                            &success,
+                                            &mBlacklistedD3D11Driver,
+                                            &mBlacklistedD3D9Driver)) {
     mCanSend = true;
   }
   return success;
 }
 
 void
 VideoDecoderChild::DestroyIPDL()
 {
--- a/dom/media/ipc/VideoDecoderChild.h
+++ b/dom/media/ipc/VideoDecoderChild.h
@@ -74,14 +74,17 @@ private:
   bool mInitialized;
   Atomic<bool> mIsHardwareAccelerated;
   Atomic<MediaDataDecoder::ConversionRequired> mConversion;
 
   // Set to true if the actor got destroyed and we haven't yet notified the
   // caller.
   bool mNeedNewDecoder;
   MediaDataDecoder::DecodedData mDecodedData;
+
+  nsCString mBlacklistedD3D11Driver;
+  nsCString mBlacklistedD3D9Driver;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // include_dom_ipc_VideoDecoderChild_h
--- a/dom/media/ipc/VideoDecoderManagerChild.cpp
+++ b/dom/media/ipc/VideoDecoderManagerChild.cpp
@@ -112,17 +112,19 @@ VideoDecoderManagerChild::GetManagerThre
 VideoDecoderManagerChild::GetManagerAbstractThread()
 {
   return sVideoDecoderChildAbstractThread;
 }
 
 PVideoDecoderChild*
 VideoDecoderManagerChild::AllocPVideoDecoderChild(const VideoInfo& aVideoInfo,
                                                   const layers::TextureFactoryIdentifier& aIdentifier,
-                                                  bool* aSuccess)
+                                                  bool* aSuccess,
+                                                  nsCString* /* not used */,
+                                                  nsCString* /* not used */)
 {
   return new VideoDecoderChild();
 }
 
 bool
 VideoDecoderManagerChild::DeallocPVideoDecoderChild(PVideoDecoderChild* actor)
 {
   VideoDecoderChild* child = static_cast<VideoDecoderChild*>(actor);
--- a/dom/media/ipc/VideoDecoderManagerChild.h
+++ b/dom/media/ipc/VideoDecoderManagerChild.h
@@ -67,17 +67,19 @@ protected:
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void DeallocPVideoDecoderManagerChild() override;
 
   void HandleFatalError(const char* aName, const char* aMsg) const override;
 
   PVideoDecoderChild* AllocPVideoDecoderChild(const VideoInfo& aVideoInfo,
                                               const layers::TextureFactoryIdentifier& aIdentifier,
-                                              bool* aSuccess) override;
+                                              bool* aSuccess,
+                                              nsCString* aBlacklistedD3D11Driver,
+                                              nsCString* aBlacklistedD3D9Driver) override;
   bool DeallocPVideoDecoderChild(PVideoDecoderChild* actor) override;
 
 private:
   // Main thread only
   static void InitializeThread();
 
   VideoDecoderManagerChild()
     : mCanSend(false)
--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
+++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
@@ -19,16 +19,22 @@
 #include "mozilla/layers/ImageDataSerializer.h"
 #include "mozilla/SyncRunnable.h"
 
 #if XP_WIN
 #include <objbase.h>
 #endif
 
 namespace mozilla {
+
+#ifdef XP_WIN
+extern const nsCString GetFoundD3D11BlacklistedDLL();
+extern const nsCString GetFoundD3D9BlacklistedDLL();
+#endif // XP_WIN
+
 namespace dom {
 
 using namespace ipc;
 using namespace layers;
 using namespace gfx;
 
 SurfaceDescriptorGPUVideo
 VideoDecoderManagerParent::StoreImage(Image* aImage, TextureClient* aTexture)
@@ -188,25 +194,34 @@ void
 VideoDecoderManagerParent::ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason)
 {
   mThreadHolder = nullptr;
 }
 
 PVideoDecoderParent*
 VideoDecoderManagerParent::AllocPVideoDecoderParent(const VideoInfo& aVideoInfo,
                                                     const layers::TextureFactoryIdentifier& aIdentifier,
-                                                    bool* aSuccess)
+                                                    bool* aSuccess,
+                                                    nsCString* aBlacklistedD3D11Driver,
+                                                    nsCString* aBlacklistedD3D9Driver)
 {
   RefPtr<TaskQueue> decodeTaskQueue = new TaskQueue(
     SharedThreadPool::Get(NS_LITERAL_CSTRING("VideoDecoderParent"), 4),
     "VideoDecoderParent::mDecodeTaskQueue");
 
-  return new VideoDecoderParent(
+  auto* parent = new VideoDecoderParent(
     this, aVideoInfo, aIdentifier,
     sManagerTaskQueue, decodeTaskQueue, aSuccess);
+
+#ifdef XP_WIN
+  *aBlacklistedD3D11Driver = GetFoundD3D11BlacklistedDLL();
+  *aBlacklistedD3D9Driver = GetFoundD3D9BlacklistedDLL();
+#endif // XP_WIN
+
+  return parent;
 }
 
 bool
 VideoDecoderManagerParent::DeallocPVideoDecoderParent(PVideoDecoderParent* actor)
 {
   VideoDecoderParent* parent = static_cast<VideoDecoderParent*>(actor);
   parent->Destroy();
   return true;
--- a/dom/media/ipc/VideoDecoderManagerParent.h
+++ b/dom/media/ipc/VideoDecoderManagerParent.h
@@ -26,17 +26,21 @@ public:
   static void StartupThreads();
   static void ShutdownThreads();
 
   static void ShutdownVideoBridge();
 
   bool OnManagerThread();
 
 protected:
-  PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo, const layers::TextureFactoryIdentifier& aIdentifier, bool* aSuccess) override;
+  PVideoDecoderParent* AllocPVideoDecoderParent(const VideoInfo& aVideoInfo,
+                                                const layers::TextureFactoryIdentifier& aIdentifier,
+                                                bool* aSuccess,
+                                                nsCString* aBlacklistedD3D11Driver,
+                                                nsCString* aBlacklistedD3D9Driver) override;
   bool DeallocPVideoDecoderParent(PVideoDecoderParent* actor) override;
 
   mozilla::ipc::IPCResult RecvReadback(const SurfaceDescriptorGPUVideo& aSD, SurfaceDescriptor* aResult) override;
   mozilla::ipc::IPCResult RecvDeallocateSurfaceDescriptorGPUVideo(const SurfaceDescriptorGPUVideo& aSD) override;
 
   void ActorDestroy(mozilla::ipc::IProtocol::ActorDestroyReason) override;
 
   void DeallocPVideoDecoderManagerParent() override;
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -142,16 +142,17 @@ EXPORTS += [
     'QueueObject.h',
     'SeekJob.h',
     'SeekTarget.h',
     'SelfRef.h',
     'SharedBuffer.h',
     'StreamTracks.h',
     'ThreadPoolCOMListener.h',
     'TimeUnits.h',
+    'TrackID.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoLimits.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
     'XiphExtradata.h',
 ]
--- a/dom/media/platforms/apple/AppleVTLinker.cpp
+++ b/dom/media/platforms/apple/AppleVTLinker.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <dlfcn.h>
 
 #include "AppleVTLinker.h"
 #include "mozilla/ArrayUtils.h"
 #include "nsDebug.h"
+#include "PlatformDecoderModule.h" // for sPDMLog
 
 #define LOG(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 namespace mozilla {
 
 AppleVTLinker::LinkStatus
 AppleVTLinker::sLinkStatus = LinkStatus_INIT;
 
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -405,53 +405,75 @@ FindD3D11BlacklistedDLL()
 static const nsCString&
 FindD3D9BlacklistedDLL()
 {
   return FindDXVABlacklistedDLL(sD3D9BlacklistingCache,
                                 gfx::gfxVars::PDMWMFDisableD3D9Dlls(),
                                 "media.wmf.disable-d3d9-for-dlls");
 }
 
+const nsCString
+GetFoundD3D11BlacklistedDLL()
+{
+  if (sD3D11BlacklistingCache) {
+    return sD3D11BlacklistingCache->mBlacklistedDLL;
+  }
+
+  return nsCString();
+}
+
+const nsCString
+GetFoundD3D9BlacklistedDLL()
+{
+  if (sD3D9BlacklistingCache) {
+    return sD3D9BlacklistingCache->mBlacklistedDLL;
+  }
+
+  return nsCString();
+}
+
 class CreateDXVAManagerEvent : public Runnable
 {
 public:
   CreateDXVAManagerEvent(layers::KnowsCompositor* aKnowsCompositor,
                          nsCString& aFailureReason)
     : Runnable("CreateDXVAManagerEvent")
     , mBackend(LayersBackend::LAYERS_D3D11)
     , mKnowsCompositor(aKnowsCompositor)
     , mFailureReason(aFailureReason)
   {
   }
 
   NS_IMETHOD Run() override {
     NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
+    const bool deblacklistingForTelemetry =
+      XRE_IsGPUProcess() && gfxPrefs::PDMWMFDeblacklistingForTelemetryInGPUProcess();
     nsACString* failureReason = &mFailureReason;
     nsCString secondFailureReason;
     if (mBackend == LayersBackend::LAYERS_D3D11 &&
       gfxPrefs::PDMWMFAllowD3D11() && IsWin8OrLater()) {
       const nsCString& blacklistedDLL = FindD3D11BlacklistedDLL();
-      if (!blacklistedDLL.IsEmpty()) {
+      if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) {
         failureReason->AppendPrintf("D3D11 blacklisted with DLL %s",
                                     blacklistedDLL.get());
       } else {
         mDXVA2Manager =
           DXVA2Manager::CreateD3D11DXVA(mKnowsCompositor, *failureReason);
         if (mDXVA2Manager) {
           return NS_OK;
         }
       }
       // Try again with d3d9, but record the failure reason
       // into a new var to avoid overwriting the d3d11 failure.
       failureReason = &secondFailureReason;
       mFailureReason.Append(NS_LITERAL_CSTRING("; "));
     }
 
     const nsCString& blacklistedDLL = FindD3D9BlacklistedDLL();
-    if (!blacklistedDLL.IsEmpty()) {
+    if (!deblacklistingForTelemetry && !blacklistedDLL.IsEmpty()) {
       mFailureReason.AppendPrintf("D3D9 blacklisted with DLL %s",
                                   blacklistedDLL.get());
     } else {
       mDXVA2Manager =
         DXVA2Manager::CreateD3D9DXVA(mKnowsCompositor, *failureReason);
       // Make sure we include the messages from both attempts (if applicable).
       mFailureReason.Append(secondFailureReason);
     }
--- a/dom/media/webaudio/AudioBuffer.cpp
+++ b/dom/media/webaudio/AudioBuffer.cpp
@@ -155,25 +155,32 @@ AudioBufferMemoryTracker::CollectReports
 
   return NS_OK;
 }
 
 AudioBuffer::AudioBuffer(nsPIDOMWindowInner* aWindow,
                          uint32_t aNumberOfChannels,
                          uint32_t aLength,
                          float aSampleRate,
-                         already_AddRefed<ThreadSharedFloatArrayBufferList>
-                           aInitialContents)
+                         ErrorResult& aRv)
   : mOwnerWindow(do_GetWeakReference(aWindow)),
-    mSharedChannels(aInitialContents),
-    mLength(aLength),
     mSampleRate(aSampleRate)
 {
-  MOZ_ASSERT(!mSharedChannels ||
-             mSharedChannels->GetChannels() == aNumberOfChannels);
+  // Note that a buffer with zero channels is permitted here for the sake of
+  // AudioProcessingEvent, where channel counts must match parameters passed
+  // to createScriptProcessor(), one of which may be zero.
+  if (aSampleRate < WebAudioUtils::MinSampleRate ||
+      aSampleRate > WebAudioUtils::MaxSampleRate ||
+      aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
+      !aLength || aLength > INT32_MAX) {
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
+  }
+
+  mSharedChannels.mDuration = aLength;
   mJSChannels.SetLength(aNumberOfChannels);
   mozilla::HoldJSObjects(this);
   AudioBufferMemoryTracker::RegisterAudioBuffer(this);
 }
 
 AudioBuffer::~AudioBuffer()
 {
   AudioBufferMemoryTracker::UnregisterAudioBuffer(this);
@@ -199,146 +206,194 @@ AudioBuffer::Constructor(const GlobalObj
 }
 
 void
 AudioBuffer::ClearJSChannels()
 {
   mJSChannels.Clear();
 }
 
+void
+AudioBuffer::SetSharedChannels(
+  already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
+{
+  RefPtr<ThreadSharedFloatArrayBufferList> buffer = aBuffer;
+  uint32_t channelCount = buffer->GetChannels();
+  mSharedChannels.mChannelData.SetLength(channelCount);
+  for (uint32_t i = 0; i < channelCount; ++i) {
+    mSharedChannels.mChannelData[i] = buffer->GetData(i);
+  }
+  mSharedChannels.mBuffer = buffer.forget();
+  mSharedChannels.mVolume = 1.0f;
+  mSharedChannels.mBufferFormat = AUDIO_FORMAT_FLOAT32;
+}
+
 /* static */ already_AddRefed<AudioBuffer>
 AudioBuffer::Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
                     uint32_t aLength, float aSampleRate,
                     already_AddRefed<ThreadSharedFloatArrayBufferList>
                       aInitialContents,
                     ErrorResult& aRv)
 {
-  // Note that a buffer with zero channels is permitted here for the sake of
-  // AudioProcessingEvent, where channel counts must match parameters passed
-  // to createScriptProcessor(), one of which may be zero.
-  if (aSampleRate < WebAudioUtils::MinSampleRate ||
-      aSampleRate > WebAudioUtils::MaxSampleRate ||
-      aNumberOfChannels > WebAudioUtils::MaxChannelCount ||
-      !aLength || aLength > INT32_MAX) {
-    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+  RefPtr<ThreadSharedFloatArrayBufferList> initialContents = aInitialContents;
+  RefPtr<AudioBuffer> buffer =
+    new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate, aRv);
+  if (aRv.Failed()) {
     return nullptr;
   }
 
+  if (initialContents) {
+    MOZ_ASSERT(initialContents->GetChannels() == aNumberOfChannels);
+    buffer->SetSharedChannels(initialContents.forget());
+  }
+
+  return buffer.forget();
+}
+
+/* static */ already_AddRefed<AudioBuffer>
+AudioBuffer::Create(nsPIDOMWindowInner* aWindow, float aSampleRate,
+                    AudioChunk&& aInitialContents)
+{
+  AudioChunk initialContents = aInitialContents;
+  ErrorResult rv;
   RefPtr<AudioBuffer> buffer =
-    new AudioBuffer(aWindow, aNumberOfChannels, aLength, aSampleRate,
-                    Move(aInitialContents));
+    new AudioBuffer(aWindow, initialContents.ChannelCount(),
+                    initialContents.mDuration, aSampleRate, rv);
+  if (rv.Failed()) {
+    return nullptr;
+  }
+  buffer->mSharedChannels = Move(aInitialContents);
 
   return buffer.forget();
 }
 
 JSObject*
 AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return AudioBufferBinding::Wrap(aCx, this, aGivenProto);
 }
 
+static void
+CopyChannelDataToFloat(const AudioChunk& aChunk, uint32_t aChannel,
+                       uint32_t aSrcOffset, float* aOutput, uint32_t aLength)
+{
+  MOZ_ASSERT(aChunk.mVolume == 1.0f);
+  if (aChunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
+    mozilla::PodCopy(aOutput,
+                     aChunk.ChannelData<float>()[aChannel] + aSrcOffset,
+                     aLength);
+  } else {
+    MOZ_ASSERT(aChunk.mBufferFormat == AUDIO_FORMAT_S16);
+    ConvertAudioSamples(aChunk.ChannelData<int16_t>()[aChannel] + aSrcOffset,
+                        aOutput, aLength);
+  }
+}
+
 bool
 AudioBuffer::RestoreJSChannelData(JSContext* aJSContext)
 {
   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
     if (mJSChannels[i]) {
       // Already have data in JS array.
       continue;
     }
 
     // The following code first zeroes the array and then copies our data
     // into it. We could avoid this with additional JS APIs to construct
     // an array (or ArrayBuffer) containing initial data.
     JS::Rooted<JSObject*> array(aJSContext,
-                                JS_NewFloat32Array(aJSContext, mLength));
+                                JS_NewFloat32Array(aJSContext, Length()));
     if (!array) {
       return false;
     }
-    if (mSharedChannels) {
+    if (!mSharedChannels.IsNull()) {
       // "4. Attach ArrayBuffers containing copies of the data to the
       // AudioBuffer, to be returned by the next call to getChannelData."
-      const float* data = mSharedChannels->GetData(i);
       JS::AutoCheckCannotGC nogc;
       bool isShared;
-      mozilla::PodCopy(JS_GetFloat32ArrayData(array, &isShared, nogc), data, mLength);
+      float* jsData = JS_GetFloat32ArrayData(array, &isShared, nogc);
       MOZ_ASSERT(!isShared); // Was created as unshared above
+      CopyChannelDataToFloat(mSharedChannels, i, 0, jsData, Length());
     }
     mJSChannels[i] = array;
   }
 
-  mSharedChannels = nullptr;
+  mSharedChannels.mBuffer = nullptr;
+  mSharedChannels.mChannelData.Clear();
 
   return true;
 }
 
 void
 AudioBuffer::CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
                              uint32_t aStartInChannel, ErrorResult& aRv)
 {
   aDestination.ComputeLengthAndData();
 
   uint32_t length = aDestination.Length();
   CheckedInt<uint32_t> end = aStartInChannel;
   end += length;
   if (aChannelNumber >= NumberOfChannels() ||
-      !end.isValid() || end.value() > mLength) {
+      !end.isValid() || end.value() > Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   JS::AutoCheckCannotGC nogc;
   JSObject* channelArray = mJSChannels[aChannelNumber];
-  const float* sourceData = nullptr;
   if (channelArray) {
-    if (JS_GetTypedArrayLength(channelArray) != mLength) {
+    if (JS_GetTypedArrayLength(channelArray) != Length()) {
       // The array's buffer was detached.
       aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
       return;
     }
 
     bool isShared = false;
-    sourceData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
+    const float* sourceData =
+      JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
     // The sourceData arrays should all have originated in
     // RestoreJSChannelData, where they are created unshared.
     MOZ_ASSERT(!isShared);
-  } else if (mSharedChannels) {
-    sourceData = mSharedChannels->GetData(aChannelNumber);
+    PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
+    return;
   }
 
-  if (sourceData) {
-    PodMove(aDestination.Data(), sourceData + aStartInChannel, length);
-  } else {
-    PodZero(aDestination.Data(), length);
+  if (!mSharedChannels.IsNull()) {
+    CopyChannelDataToFloat(mSharedChannels, aChannelNumber, aStartInChannel,
+                           aDestination.Data(), length);
+    return;
   }
+
+  PodZero(aDestination.Data(), length);
 }
 
 void
 AudioBuffer::CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
                            uint32_t aChannelNumber, uint32_t aStartInChannel,
                            ErrorResult& aRv)
 {
   aSource.ComputeLengthAndData();
 
   uint32_t length = aSource.Length();
   CheckedInt<uint32_t> end = aStartInChannel;
   end += length;
   if (aChannelNumber >= NumberOfChannels() ||
-      !end.isValid() || end.value() > mLength) {
+      !end.isValid() || end.value() > Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   if (!RestoreJSChannelData(aJSContext)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   JS::AutoCheckCannotGC nogc;
   JSObject* channelArray = mJSChannels[aChannelNumber];
-  if (JS_GetTypedArrayLength(channelArray) != mLength) {
+  if (JS_GetTypedArrayLength(channelArray) != Length()) {
     // The array's buffer was detached.
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   bool isShared = false;
   float* channelData = JS_GetFloat32ArrayData(channelArray, &isShared, nogc);
   // The channelData arrays should all have originated in
@@ -368,17 +423,17 @@ AudioBuffer::GetChannelData(JSContext* a
 already_AddRefed<ThreadSharedFloatArrayBufferList>
 AudioBuffer::StealJSArrayDataIntoSharedChannels(JSContext* aJSContext)
 {
   // "1. If any of the AudioBuffer's ArrayBuffer have been detached, abort
   // these steps, and return a zero-length channel data buffers to the
   // invoker."
   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
     JSObject* channelArray = mJSChannels[i];
-    if (!channelArray || mLength != JS_GetTypedArrayLength(channelArray)) {
+    if (!channelArray || Length() != JS_GetTypedArrayLength(channelArray)) {
       // Either empty buffer or one of the arrays' buffers was detached.
       return nullptr;
     }
   }
 
   // "2. Detach all ArrayBuffers for arrays previously returned by
   // getChannelData on this AudioBuffer."
   // "3. Retain the underlying data buffers from those ArrayBuffers and return
@@ -409,31 +464,35 @@ AudioBuffer::StealJSArrayDataIntoSharedC
 
   for (uint32_t i = 0; i < mJSChannels.Length(); ++i) {
     mJSChannels[i] = nullptr;
   }
 
   return result.forget();
 }
 
-ThreadSharedFloatArrayBufferList*
+const AudioChunk&
 AudioBuffer::GetThreadSharedChannelsForRate(JSContext* aJSContext)
 {
-  if (!mSharedChannels) {
-    mSharedChannels = StealJSArrayDataIntoSharedChannels(aJSContext);
+  if (mSharedChannels.IsNull()) {
+    // mDuration is set in constructor
+    RefPtr<ThreadSharedFloatArrayBufferList> buffer =
+      StealJSArrayDataIntoSharedChannels(aJSContext);
+
+    if (buffer) {
+      SetSharedChannels(buffer.forget());
+    }
   }
 
   return mSharedChannels;
 }
 
 size_t
 AudioBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   size_t amount = aMallocSizeOf(this);
   amount += mJSChannels.ShallowSizeOfExcludingThis(aMallocSizeOf);
-  if (mSharedChannels) {
-    amount += mSharedChannels->SizeOfIncludingThis(aMallocSizeOf);
-  }
+  amount += mSharedChannels.SizeOfExcludingThis(aMallocSizeOf, false);
   return amount;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webaudio/AudioBuffer.h
+++ b/dom/media/webaudio/AudioBuffer.h
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 AudioBuffer_h_
 #define AudioBuffer_h_
 
+#include "AudioSegment.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/StaticMutex.h"
 #include "nsTArray.h"
 #include "js/TypeDecls.h"
 #include "mozilla/MemoryReporting.h"
@@ -48,16 +49,21 @@ public:
   Create(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
          uint32_t aLength, float aSampleRate,
          ErrorResult& aRv)
   {
     return Create(aWindow, aNumberOfChannels, aLength, aSampleRate,
                   nullptr, aRv);
   }
 
+  // Non-unit AudioChunk::mVolume is not supported
+  static already_AddRefed<AudioBuffer>
+  Create(nsPIDOMWindowInner* aWindow, float aSampleRate,
+         AudioChunk&& aInitialContents);
+
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AudioBuffer)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AudioBuffer)
 
   static already_AddRefed<AudioBuffer>
   Constructor(const GlobalObject& aGlobal,
               const AudioBufferOptions& aOptions, ErrorResult& aRv);
@@ -72,22 +78,22 @@ public:
 
   float SampleRate() const
   {
     return mSampleRate;
   }
 
   uint32_t Length() const
   {
-    return mLength;
+    return mSharedChannels.mDuration;
   }
 
   double Duration() const
   {
-    return mLength / static_cast<double> (mSampleRate);
+    return Length() / static_cast<double> (mSampleRate);
   }
 
   uint32_t NumberOfChannels() const
   {
     return mJSChannels.Length();
   }
 
   /**
@@ -100,43 +106,44 @@ public:
 
   void CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
                        uint32_t aStartInChannel, ErrorResult& aRv);
   void CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
                      uint32_t aChannelNumber, uint32_t aStartInChannel,
                      ErrorResult& aRv);
 
   /**
-   * Returns a ThreadSharedFloatArrayBufferList containing the sample data.
-   * Can return null if there is no data.
+   * Returns a reference to an AudioChunk containing the sample data.
+   * The AudioChunk can have a null buffer if there is no data.
    */
-  ThreadSharedFloatArrayBufferList* GetThreadSharedChannelsForRate(JSContext* aContext);
+  const AudioChunk& GetThreadSharedChannelsForRate(JSContext* aContext);
 
 protected:
   AudioBuffer(nsPIDOMWindowInner* aWindow, uint32_t aNumberOfChannels,
-              uint32_t aLength, float aSampleRate,
-              already_AddRefed<ThreadSharedFloatArrayBufferList>
-                aInitialContents);
+              uint32_t aLength, float aSampleRate, ErrorResult& aRv);
   ~AudioBuffer();
 
+  void
+  SetSharedChannels(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer);
+
   bool RestoreJSChannelData(JSContext* aJSContext);
 
   already_AddRefed<ThreadSharedFloatArrayBufferList>
   StealJSArrayDataIntoSharedChannels(JSContext* aJSContext);
 
   void ClearJSChannels();
 
-  nsWeakPtr mOwnerWindow;
   // Float32Arrays
   AutoTArray<JS::Heap<JSObject*>, 2> mJSChannels;
+  // mSharedChannels aggregates the data from mJSChannels. This is non-null
+  // if and only if the mJSChannels' buffers are detached, but its mDuration
+  // member keeps the buffer length regardless of whether the buffer is
+  // provided by mJSChannels or mSharedChannels.
+  AudioChunk mSharedChannels;
 
-  // mSharedChannels aggregates the data from mJSChannels. This is non-null
-  // if and only if the mJSChannels' buffers are detached.
-  RefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels;
-
-  uint32_t mLength;
+  nsWeakPtr mOwnerWindow;
   float mSampleRate;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -139,17 +139,17 @@ public:
     case AudioBufferSourceNode::LOOPEND:
       MOZ_ASSERT(aParam >= 0);
       mLoopEnd = aParam;
       break;
     default:
       NS_ERROR("Bad AudioBufferSourceNodeEngine Int32Parameter");
     }
   }
-  void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override
+  void SetBuffer(AudioChunk&& aBuffer) override
   {
     mBuffer = aBuffer;
   }
 
   bool BegunResampling()
   {
     return mBeginProcessing == -STREAM_TIME_MAX;
   }
@@ -210,36 +210,40 @@ public:
     }
   }
 
   // Borrow a full buffer of size WEBAUDIO_BLOCK_SIZE from the source buffer
   // at offset aSourceOffset.  This avoids copying memory.
   void BorrowFromInputBuffer(AudioBlock* aOutput,
                              uint32_t aChannels)
   {
-    aOutput->SetBuffer(mBuffer);
+    aOutput->SetBuffer(mBuffer.mBuffer);
     aOutput->mChannelData.SetLength(aChannels);
     for (uint32_t i = 0; i < aChannels; ++i) {
-      aOutput->mChannelData[i] = mBuffer->GetData(i) + mBufferPosition;
+      aOutput->mChannelData[i] =
+        mBuffer.ChannelData<float>()[i] + mBufferPosition;
     }
-    aOutput->mVolume = 1.0f;
+    aOutput->mVolume = mBuffer.mVolume;
     aOutput->mBufferFormat = AUDIO_FORMAT_FLOAT32;
   }
 
   // Copy aNumberOfFrames frames from the source buffer at offset aSourceOffset
   // and put it at offset aBufferOffset in the destination buffer.
-  void CopyFromInputBuffer(AudioBlock* aOutput,
-                           uint32_t aChannels,
-                           uintptr_t aOffsetWithinBlock,
-                           uint32_t aNumberOfFrames) {
+  template <typename T> void
+  CopyFromInputBuffer(AudioBlock* aOutput,
+                      uint32_t aChannels,
+                      uintptr_t aOffsetWithinBlock,
+                      uint32_t aNumberOfFrames)
+  {
+    MOZ_ASSERT(mBuffer.mVolume == 1.0f);
     for (uint32_t i = 0; i < aChannels; ++i) {
       float* baseChannelData = aOutput->ChannelFloatsForWrite(i);
-      memcpy(baseChannelData + aOffsetWithinBlock,
-             mBuffer->GetData(i) + mBufferPosition,
-             aNumberOfFrames * sizeof(float));
+      ConvertAudioSamples(mBuffer.ChannelData<T>()[i] + mBufferPosition,
+                          baseChannelData + aOffsetWithinBlock,
+                          aNumberOfFrames);
     }
   }
 
   // Resamples input data to an output buffer, according to |mBufferSampleRate| and
   // the playbackRate/detune.
   // The number of frames consumed/produced depends on the amount of space
   // remaining in both the input and output buffer, and the playback rate (that
   // is, the ratio between the output samplerate and the input samplerate).
@@ -285,27 +289,38 @@ public:
         }
         speex_resampler_set_skip_frac_num(resampler,
                                   std::min<int64_t>(skipFracNum, UINT32_MAX));
 
         mBeginProcessing = -STREAM_TIME_MAX;
       }
       inputLimit = std::min(inputLimit, availableInInputBuffer);
 
+      MOZ_ASSERT(mBuffer.mVolume == 1.0f);
       for (uint32_t i = 0; true; ) {
         uint32_t inSamples = inputLimit;
-        const float* inputData = mBuffer->GetData(i) + mBufferPosition;
 
         uint32_t outSamples = aAvailableInOutput;
         float* outputData =
           aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
 
-        WebAudioUtils::SpeexResamplerProcess(resampler, i,
-                                             inputData, &inSamples,
-                                             outputData, &outSamples);
+        if (mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
+          const float* inputData =
+            mBuffer.ChannelData<float>()[i] + mBufferPosition;
+          WebAudioUtils::SpeexResamplerProcess(resampler, i,
+                                               inputData, &inSamples,
+                                               outputData, &outSamples);
+        } else {
+          MOZ_ASSERT(mBuffer.mBufferFormat == AUDIO_FORMAT_S16);
+          const int16_t* inputData =
+            mBuffer.ChannelData<int16_t>()[i] + mBufferPosition;
+          WebAudioUtils::SpeexResamplerProcess(resampler, i,
+                                               inputData, &inSamples,
+                                               outputData, &outSamples);
+        }
         if (++i == aChannels) {
           mBufferPosition += inSamples;
           MOZ_ASSERT(mBufferPosition <= mBufferEnd || mLoop);
           *aOffsetWithinBlock += outSamples;
           *aCurrentPosition += outSamples;
           if (inSamples == availableInInputBuffer && !mLoop) {
             // We'll feed in enough zeros to empty out the resampler's memory.
             // This handles the output latency as well as capturing the low
@@ -413,32 +428,42 @@ public:
         mBufferSampleRate / mResamplerOutRate;
       mBufferPosition += end - start;
       return;
     }
 
     uint32_t numFrames = std::min(aBufferMax - mBufferPosition,
                                   availableInOutput);
 
-    bool inputBufferAligned = true;
-    for (uint32_t i = 0; i < aChannels; ++i) {
-      if (!IS_ALIGNED16(mBuffer->GetData(i) + mBufferPosition)) {
-        inputBufferAligned = false;
+    bool shouldBorrow = false;
+    if (numFrames == WEBAUDIO_BLOCK_SIZE &&
+        mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
+      shouldBorrow = true;
+      for (uint32_t i = 0; i < aChannels; ++i) {
+        if (!IS_ALIGNED16(mBuffer.ChannelData<float>()[i] + mBufferPosition)) {
+          shouldBorrow = false;
+          break;
+        }
       }
     }
-
-    if (numFrames == WEBAUDIO_BLOCK_SIZE && inputBufferAligned) {
-      MOZ_ASSERT(mBufferPosition < aBufferMax);
+    MOZ_ASSERT(mBufferPosition < aBufferMax);
+    if (shouldBorrow) {
       BorrowFromInputBuffer(aOutput, aChannels);
     } else {
       if (*aOffsetWithinBlock == 0) {
         aOutput->AllocateChannels(aChannels);
       }
-      MOZ_ASSERT(mBufferPosition < aBufferMax);
-      CopyFromInputBuffer(aOutput, aChannels, *aOffsetWithinBlock, numFrames);
+      if (mBuffer.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
+        CopyFromInputBuffer<float>(aOutput, aChannels,
+                                   *aOffsetWithinBlock, numFrames);
+      } else {
+        MOZ_ASSERT(mBuffer.mBufferFormat == AUDIO_FORMAT_S16);
+        CopyFromInputBuffer<int16_t>(aOutput, aChannels,
+                                     *aOffsetWithinBlock, numFrames);
+      }
     }
     *aOffsetWithinBlock += numFrames;
     *aCurrentPosition += numFrames;
     mBufferPosition += numFrames;
   }
 
   int32_t ComputeFinalOutSampleRate(float aPlaybackRate, float aDetune)
   {
@@ -484,17 +509,17 @@ public:
   {
     if (mBufferSampleRate == 0) {
       // start() has not yet been called or no buffer has yet been set
       aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
       return;
     }
 
     StreamTime streamPosition = mDestination->GraphTimeToStreamTime(aFrom);
-    uint32_t channels = mBuffer ? mBuffer->GetChannels() : 0;
+    uint32_t channels = mBuffer.ChannelCount();
 
     UpdateSampleRateIfNeeded(channels, streamPosition);
 
     uint32_t written = 0;
     while (written < WEBAUDIO_BLOCK_SIZE) {
       if (mStop != STREAM_TIME_MAX &&
           streamPosition >= mStop) {
         FillWithZeroes(aOutput, channels, &written, &streamPosition, STREAM_TIME_MAX);
@@ -564,17 +589,17 @@ public:
 
   double mStart; // including the fractional position between ticks
   // Low pass filter effects from the resampler mean that samples before the
   // start time are influenced by resampling the buffer.  mBeginProcessing
   // includes the extent of this filter.  The special value of -STREAM_TIME_MAX
   // indicates that the resampler has begun processing.
   StreamTime mBeginProcessing;
   StreamTime mStop;
-  RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
+  AudioChunk mBuffer;
   SpeexResamplerState* mResampler;
   // mRemainingResamplerTail, like mBufferPosition, and
   // mBufferEnd, is measured in input buffer samples.
   uint32_t mRemainingResamplerTail;
   uint32_t mBufferEnd;
   uint32_t mLoopStart;
   uint32_t mLoopEnd;
   uint32_t mBufferPosition;
@@ -725,26 +750,25 @@ void
 AudioBufferSourceNode::SendBufferParameterToStream(JSContext* aCx)
 {
   AudioNodeStream* ns = mStream;
   if (!ns) {
     return;
   }
 
   if (mBuffer) {
-    RefPtr<ThreadSharedFloatArrayBufferList> data =
-      mBuffer->GetThreadSharedChannelsForRate(aCx);
-    ns->SetBuffer(data.forget());
+    AudioChunk data = mBuffer->GetThreadSharedChannelsForRate(aCx);
+    ns->SetBuffer(Move(data));
 
     if (mStartCalled) {
       SendOffsetAndDurationParametersToStream(ns);
     }
   } else {
     ns->SetInt32Parameter(BUFFEREND, 0);
-    ns->SetBuffer(nullptr);
+    ns->SetBuffer(AudioChunk());
 
     MarkInactive();
   }
 }
 
 void
 AudioBufferSourceNode::SendOffsetAndDurationParametersToStream(AudioNodeStream* aStream)
 {
--- a/dom/media/webaudio/AudioNodeEngine.h
+++ b/dom/media/webaudio/AudioNodeEngine.h
@@ -43,16 +43,22 @@ public:
   /**
    * Create with buffers suitable for transfer to
    * JS_NewArrayBufferWithContents().  The buffer contents are uninitialized
    * and so should be set using GetDataForWrite().
    */
   static already_AddRefed<ThreadSharedFloatArrayBufferList>
   Create(uint32_t aChannelCount, size_t aLength, const mozilla::fallible_t&);
 
+  ThreadSharedFloatArrayBufferList*
+  AsThreadSharedFloatArrayBufferList() override
+  {
+    return this;
+  };
+
   struct Storage final
   {
     Storage() :
       mDataToFree(nullptr),
       mFree(nullptr),
       mSampleData(nullptr)
     {}
     ~Storage() {
@@ -283,17 +289,17 @@ public:
   {
     NS_ERROR("Invalid RecvTimelineEvent index");
   }
   virtual void SetThreeDPointParameter(uint32_t aIndex,
                                        const dom::ThreeDPoint& aValue)
   {
     NS_ERROR("Invalid SetThreeDPointParameter index");
   }
-  virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
+  virtual void SetBuffer(AudioChunk&& aBuffer)
   {
     NS_ERROR("SetBuffer called on engine that doesn't support it");
   }
   // This consumes the contents of aData.  aData will be emptied after this returns.
   virtual void SetRawArrayData(nsTArray<float>& aData)
   {
     NS_ERROR("SetRawArrayData called on an engine that doesn't support it");
   }
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -249,34 +249,33 @@ AudioNodeStream::SetThreeDPointParameter
     ThreeDPoint mValue;
     uint32_t mIndex;
   };
 
   GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
 }
 
 void
-AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer)
+AudioNodeStream::SetBuffer(AudioChunk&& aBuffer)
 {
   class Message final : public ControlMessage
   {
   public:
-    Message(AudioNodeStream* aStream,
-            already_AddRefed<ThreadSharedFloatArrayBufferList>& aBuffer)
+    Message(AudioNodeStream* aStream, AudioChunk&& aBuffer)
       : ControlMessage(aStream), mBuffer(aBuffer)
     {}
     void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
-          SetBuffer(mBuffer.forget());
+        SetBuffer(Move(mBuffer));
     }
-    RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
+    AudioChunk mBuffer;
   };
 
-  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aBuffer));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, Move(aBuffer)));
 }
 
 void
 AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
 {
   class Message final : public ControlMessage
   {
   public:
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -86,17 +86,17 @@ public:
    * Sets a parameter that's a time relative to some stream's played time.
    * This time is converted to a time relative to this stream when it's set.
    */
   void SetStreamTimeParameter(uint32_t aIndex, AudioContext* aContext,
                               double aStreamTime);
   void SetDoubleParameter(uint32_t aIndex, double aValue);
   void SetInt32Parameter(uint32_t aIndex, int32_t aValue);
   void SetThreeDPointParameter(uint32_t aIndex, const dom::ThreeDPoint& aValue);
-  void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer);
+  void SetBuffer(AudioChunk&& aBuffer);
   // This sends a single event to the timeline on the MSG thread side.
   void SendTimelineEvent(uint32_t aIndex, const dom::AudioTimelineEvent& aEvent);
   // This consumes the contents of aData.  aData will be emptied after this returns.
   void SetRawArrayData(nsTArray<float>& aData);
   void SetChannelMixingParameters(uint32_t aNumberOfChannels,
                                   ChannelCountMode aChannelCountMoe,
                                   ChannelInterpretation aChannelInterpretation);
   void SetPassThrough(bool aPassThrough);
--- a/dom/media/webaudio/ConvolverNode.cpp
+++ b/dom/media/webaudio/ConvolverNode.cpp
@@ -25,39 +25,30 @@ NS_IMPL_ADDREF_INHERITED(ConvolverNode, 
 NS_IMPL_RELEASE_INHERITED(ConvolverNode, AudioNode)
 
 class ConvolverNodeEngine final : public AudioNodeEngine
 {
   typedef PlayingRefChangeHandler PlayingRefChanged;
 public:
   ConvolverNodeEngine(AudioNode* aNode, bool aNormalize)
     : AudioNodeEngine(aNode)
-    , mBufferLength(0)
     , mLeftOverData(INT32_MIN)
     , mSampleRate(0.0f)
     , mUseBackgroundThreads(!aNode->Context()->IsOffline())
     , mNormalize(aNormalize)
   {
   }
 
   enum Parameters {
-    BUFFER_LENGTH,
     SAMPLE_RATE,
     NORMALIZE
   };
   void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
   {
     switch (aIndex) {
-    case BUFFER_LENGTH:
-      // BUFFER_LENGTH is the first parameter that we set when setting a new buffer,
-      // so we should be careful to invalidate the rest of our state here.
-      mSampleRate = 0.0f;
-      mBufferLength = aParam;
-      mLeftOverData = INT32_MIN;
-      break;
     case NORMALIZE:
       mNormalize = !!aParam;
       break;
     default:
       NS_ERROR("Bad ConvolverNodeEngine Int32Parameter");
     }
   }
   void SetDoubleParameter(uint32_t aIndex, double aParam) override
@@ -67,36 +58,34 @@ public:
       mSampleRate = aParam;
       // The buffer is passed after the sample rate.
       // mReverb will be set using this sample rate when the buffer is received.
       break;
     default:
       NS_ERROR("Bad ConvolverNodeEngine DoubleParameter");
     }
   }
-  void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override
+  void SetBuffer(AudioChunk&& aBuffer) override
   {
-    RefPtr<ThreadSharedFloatArrayBufferList> buffer = aBuffer;
-
     // Note about empirical tuning (this is copied from Blink)
     // The maximum FFT size affects reverb performance and accuracy.
     // If the reverb is single-threaded and processes entirely in the real-time audio thread,
     // it's important not to make this too high.  In this case 8192 is a good value.
     // But, the Reverb object is multi-threaded, so we want this as high as possible without losing too much accuracy.
     // Very large FFTs will have worse phase errors. Given these constraints 32768 is a good compromise.
     const size_t MaxFFTSize = 32768;
 
-    if (!buffer || !mBufferLength || !mSampleRate) {
+    mLeftOverData = INT32_MIN; // reset
+
+    if (aBuffer.IsNull() || !mSampleRate) {
       mReverb = nullptr;
-      mLeftOverData = INT32_MIN;
       return;
     }
 
-    mReverb = new WebCore::Reverb(buffer, mBufferLength,
-                                  MaxFFTSize, mUseBackgroundThreads,
+    mReverb = new WebCore::Reverb(aBuffer, MaxFFTSize, mUseBackgroundThreads,
                                   mNormalize, mSampleRate);
   }
 
   void ProcessBlock(AudioNodeStream* aStream,
                     GraphTime aFrom,
                     const AudioBlock& aInput,
                     AudioBlock* aOutput,
                     bool* aFinished) override
@@ -137,17 +126,17 @@ public:
       }
 
       if (mLeftOverData <= 0) {
         RefPtr<PlayingRefChanged> refchanged =
           new PlayingRefChanged(aStream, PlayingRefChanged::ADDREF);
         aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
           refchanged.forget());
       }
-      mLeftOverData = mBufferLength;
+      mLeftOverData = mReverb->impulseResponseLength();
       MOZ_ASSERT(mLeftOverData > 0);
     }
     aOutput->AllocateChannels(2);
 
     mReverb->process(&input, aOutput);
   }
 
   bool IsActive() const override
@@ -168,17 +157,16 @@ public:
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   }
 
 private:
   nsAutoPtr<WebCore::Reverb> mReverb;
-  int32_t mBufferLength;
   int32_t mLeftOverData;
   float mSampleRate;
   bool mUseBackgroundThreads;
   bool mNormalize;
 };
 
 ConvolverNode::ConvolverNode(AudioContext* aContext)
   : AudioNode(aContext,
@@ -258,32 +246,55 @@ ConvolverNode::SetBuffer(JSContext* aCx,
       // Supported number of channels
       break;
     default:
       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
       return;
     }
   }
 
-  mBuffer = aBuffer;
-
   // Send the buffer to the stream
   AudioNodeStream* ns = mStream;
   MOZ_ASSERT(ns, "Why don't we have a stream here?");
-  if (mBuffer) {
-    uint32_t length = mBuffer->Length();
-    RefPtr<ThreadSharedFloatArrayBufferList> data =
-      mBuffer->GetThreadSharedChannelsForRate(aCx);
-    SendInt32ParameterToStream(ConvolverNodeEngine::BUFFER_LENGTH, length);
+  if (aBuffer) {
+    AudioChunk data = aBuffer->GetThreadSharedChannelsForRate(aCx);
+    if (data.mBufferFormat == AUDIO_FORMAT_S16) {
+      // Reverb expects data in float format.
+      // Convert on the main thread so as to minimize allocations on the audio
+      // thread.
+      // Reverb will dispose of the buffer once initialized, so convert here
+      // and leave the smaller arrays in the AudioBuffer.
+      // There is currently no value in providing 16/32-byte aligned data
+      // because PadAndMakeScaledDFT() will copy the data (without SIMD
+      // instructions) to aligned arrays for the FFT.
+      RefPtr<SharedBuffer> floatBuffer =
+        SharedBuffer::Create(sizeof(float) *
+                             data.mDuration * data.ChannelCount());
+      if (!floatBuffer) {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return;
+      }
+      auto floatData = static_cast<float*>(floatBuffer->Data());
+      for (size_t i = 0; i < data.ChannelCount(); ++i) {
+        ConvertAudioSamples(data.ChannelData<int16_t>()[i],
+                            floatData, data.mDuration);
+        data.mChannelData[i] = floatData;
+        floatData += data.mDuration;
+      }
+      data.mBuffer = Move(floatBuffer);
+      data.mBufferFormat = AUDIO_FORMAT_FLOAT32;
+    }
     SendDoubleParameterToStream(ConvolverNodeEngine::SAMPLE_RATE,
-                                mBuffer->SampleRate());
-    ns->SetBuffer(data.forget());
+                                aBuffer->SampleRate());
+    ns->SetBuffer(Move(data));
   } else {
-    ns->SetBuffer(nullptr);
+    ns->SetBuffer(AudioChunk());
   }
+
+  mBuffer = aBuffer;
 }
 
 void
 ConvolverNode::SetNormalize(bool aNormalize)
 {
   mNormalize = aNormalize;
   SendInt32ParameterToStream(ConvolverNodeEngine::NORMALIZE, aNormalize);
 }
--- a/dom/media/webaudio/MediaBufferDecoder.cpp
+++ b/dom/media/webaudio/MediaBufferDecoder.cpp
@@ -333,86 +333,114 @@ MediaDecodeTask::FinishDecode()
     resampler = speex_resampler_init(channelCount,
                                      sampleRate,
                                      destSampleRate,
                                      SPEEX_RESAMPLER_QUALITY_DEFAULT, nullptr);
     speex_resampler_skip_zeros(resampler);
     resampledFrames += speex_resampler_get_output_latency(resampler);
   }
 
-  // Allocate the channel buffers.  Note that if we end up resampling, we may
-  // write fewer bytes than mResampledFrames to the output buffer, in which
-  // case mWriteIndex will tell us how many valid samples we have.
-  mDecodeJob.mBuffer = ThreadSharedFloatArrayBufferList::
+  // Allocate contiguous channel buffers.  Note that if we end up resampling,
+  // we may write fewer bytes than mResampledFrames to the output buffer, in
+  // which case writeIndex will tell us how many valid samples we have.
+  mDecodeJob.mBuffer.mChannelData.SetLength(channelCount);
+#if AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_FLOAT32
+  // This buffer has separate channel arrays that could be transferred to
+  // JS_NewArrayBufferWithContents(), but AudioBuffer::RestoreJSChannelData()
+  // does not yet take advantage of this.
+  RefPtr<ThreadSharedFloatArrayBufferList> buffer =
+    ThreadSharedFloatArrayBufferList::
     Create(channelCount, resampledFrames, fallible);
-  if (!mDecodeJob.mBuffer) {
+  if (!buffer) {
     ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
     return;
   }
+  for (uint32_t i = 0; i < channelCount; ++i) {
+    mDecodeJob.mBuffer.mChannelData[i] = buffer->GetData(i);
+  }
+#else
+  RefPtr<SharedBuffer> buffer =
+    SharedBuffer::Create(sizeof(AudioDataValue) *
+                         resampledFrames * channelCount);
+  if (!buffer) {
+    ReportFailureOnMainThread(WebAudioDecodeJob::UnknownError);
+    return;
+  }
+  auto data = static_cast<AudioDataValue*>(floatBuffer->Data());
+  for (uint32_t i = 0; i < channelCount; ++i) {
+    mDecodeJob.mBuffer.mChannelData[i] = data;
+    data += resampledFrames;
+  }
+#endif
+  mDecodeJob.mBuffer.mBuffer = buffer.forget();
+  mDecodeJob.mBuffer.mVolume = 1.0f;
+  mDecodeJob.mBuffer.mBufferFormat = AUDIO_OUTPUT_FORMAT;
 
+  uint32_t writeIndex = 0;
   RefPtr<AudioData> audioData;
   while ((audioData = mAudioQueue.PopFront())) {
     audioData->EnsureAudioBuffer(); // could lead to a copy :(
-    AudioDataValue* bufferData = static_cast<AudioDataValue*>
+    const AudioDataValue* bufferData = static_cast<AudioDataValue*>
       (audioData->mAudioBuffer->Data());
 
     if (sampleRate != destSampleRate) {
-      const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex;
+      const uint32_t maxOutSamples = resampledFrames - writeIndex;
 
       for (uint32_t i = 0; i < audioData->mChannels; ++i) {
         uint32_t inSamples = audioData->mFrames;
         uint32_t outSamples = maxOutSamples;
-        float* outData =
-          mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
+        AudioDataValue* outData = mDecodeJob.mBuffer.
+          ChannelDataForWrite<AudioDataValue>(i) + writeIndex;
 
         WebAudioUtils::SpeexResamplerProcess(
             resampler, i, &bufferData[i * audioData->mFrames], &inSamples,
             outData, &outSamples);
 
         if (i == audioData->mChannels - 1) {
-          mDecodeJob.mWriteIndex += outSamples;
-          MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames);
+          writeIndex += outSamples;
+          MOZ_ASSERT(writeIndex <= resampledFrames);
           MOZ_ASSERT(inSamples == audioData->mFrames);
         }
       }
     } else {
       for (uint32_t i = 0; i < audioData->mChannels; ++i) {
-        float* outData =
-          mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
-        ConvertAudioSamples(&bufferData[i * audioData->mFrames],
-                            outData, audioData->mFrames);
+        AudioDataValue* outData = mDecodeJob.mBuffer.
+          ChannelDataForWrite<AudioDataValue>(i) + writeIndex;
+        PodCopy(outData, &bufferData[i * audioData->mFrames],
+                audioData->mFrames);
 
         if (i == audioData->mChannels - 1) {
-          mDecodeJob.mWriteIndex += audioData->mFrames;
+          writeIndex += audioData->mFrames;
         }
       }
     }
   }
 
   if (sampleRate != destSampleRate) {
     uint32_t inputLatency = speex_resampler_get_input_latency(resampler);
-    const uint32_t maxOutSamples = resampledFrames - mDecodeJob.mWriteIndex;
+    const uint32_t maxOutSamples = resampledFrames - writeIndex;
     for (uint32_t i = 0; i < channelCount; ++i) {
       uint32_t inSamples = inputLatency;
       uint32_t outSamples = maxOutSamples;
-      float* outData =
-        mDecodeJob.mBuffer->GetDataForWrite(i) + mDecodeJob.mWriteIndex;
+      AudioDataValue* outData =
+        mDecodeJob.mBuffer.ChannelDataForWrite<AudioDataValue>(i) + writeIndex;
 
       WebAudioUtils::SpeexResamplerProcess(
           resampler, i, (AudioDataValue*)nullptr, &inSamples,
           outData, &outSamples);
 
       if (i == channelCount - 1) {
-        mDecodeJob.mWriteIndex += outSamples;
-        MOZ_ASSERT(mDecodeJob.mWriteIndex <= resampledFrames);
+        writeIndex += outSamples;
+        MOZ_ASSERT(writeIndex <= resampledFrames);
         MOZ_ASSERT(inSamples == inputLatency);
       }
     }
   }
 
+  mDecodeJob.mBuffer.mDuration = writeIndex;
   mPhase = PhaseEnum::AllocateBuffer;
   mMainThread->Dispatch(do_AddRef(this));
 }
 
 void
 MediaDecodeTask::AllocateBuffer()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -439,22 +467,19 @@ MediaDecodeTask::CallbackTheResult()
 
 bool
 WebAudioDecodeJob::AllocateBuffer()
 {
   MOZ_ASSERT(!mOutput);
   MOZ_ASSERT(NS_IsMainThread());
 
   // Now create the AudioBuffer
-  ErrorResult rv;
-  uint32_t channelCount = mBuffer->GetChannels();
-  mOutput = AudioBuffer::Create(mContext->GetOwner(), channelCount,
-                                mWriteIndex, mContext->SampleRate(),
-                                mBuffer.forget(), rv);
-  return !rv.Failed();
+  mOutput = AudioBuffer::Create(mContext->GetOwner(),
+                                mContext->SampleRate(), Move(mBuffer));
+  return mOutput != nullptr;
 }
 
 void
 AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
                     uint32_t aLength, WebAudioDecodeJob& aDecodeJob)
 {
   Maybe<MediaContainerType> containerType = MakeMediaContainerType(aContentType);
   // Do not attempt to decode the media if we were not successful at sniffing
@@ -490,17 +515,16 @@ AsyncDecodeWebAudio(const char* aContent
 }
 
 WebAudioDecodeJob::WebAudioDecodeJob(const nsACString& aContentType,
                                      AudioContext* aContext,
                                      Promise* aPromise,
                                      DecodeSuccessCallback* aSuccessCallback,
                                      DecodeErrorCallback* aFailureCallback)
   : mContentType(aContentType)
-  , mWriteIndex(0)
   , mContext(aContext)
   , mPromise(aPromise)
   , mSuccessCallback(aSuccessCallback)
   , mFailureCallback(aFailureCallback)
 {
   MOZ_ASSERT(aContext);
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(WebAudioDecodeJob);
@@ -590,19 +614,17 @@ WebAudioDecodeJob::SizeOfExcludingThis(M
     amount += mSuccessCallback->SizeOfIncludingThis(aMallocSizeOf);
   }
   if (mFailureCallback) {
     amount += mFailureCallback->SizeOfIncludingThis(aMallocSizeOf);
   }
   if (mOutput) {
     amount += mOutput->SizeOfIncludingThis(aMallocSizeOf);
   }
-  if (mBuffer) {
-    amount += mBuffer->SizeOfIncludingThis(aMallocSizeOf);
-  }
+  amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf, false);
   return amount;
 }
 
 size_t
 WebAudioDecodeJob::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
--- a/dom/media/webaudio/MediaBufferDecoder.h
+++ b/dom/media/webaudio/MediaBufferDecoder.h
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 MediaBufferDecoder_h_
 #define MediaBufferDecoder_h_
 
+#include "AudioSegment.h"
 #include "nsWrapperCache.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "mozilla/dom/TypedArray.h"
 #include "mozilla/MemoryReporting.h"
 
 namespace mozilla {
@@ -50,24 +51,23 @@ struct WebAudioDecodeJob final
   void OnSuccess(ErrorCode /* ignored */);
   void OnFailure(ErrorCode aErrorCode);
 
   bool AllocateBuffer();
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
+  AudioChunk mBuffer;
   nsCString mContentType;
-  uint32_t mWriteIndex;
   RefPtr<dom::AudioContext> mContext;
   RefPtr<dom::Promise> mPromise;
   RefPtr<dom::DecodeSuccessCallback> mSuccessCallback;
   RefPtr<dom::DecodeErrorCallback> mFailureCallback; // can be null
   RefPtr<dom::AudioBuffer> mOutput;
-  RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
 };
 
 void AsyncDecodeWebAudio(const char* aContentType, uint8_t* aBuffer,
                          uint32_t aLength, WebAudioDecodeJob& aDecodeJob);
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/OscillatorNode.cpp
+++ b/dom/media/webaudio/OscillatorNode.cpp
@@ -36,33 +36,31 @@ public:
     // Keep the default values in sync with OscillatorNode::OscillatorNode.
     , mFrequency(440.f)
     , mDetune(0.f)
     , mType(OscillatorType::Sine)
     , mPhase(0.)
     , mFinalFrequency(0.)
     , mPhaseIncrement(0.)
     , mRecomputeParameters(true)
-    , mCustomLength(0)
     , mCustomDisableNormalization(false)
   {
     MOZ_ASSERT(NS_IsMainThread());
     mBasicWaveFormCache = aDestination->Context()->GetBasicWaveFormCache();
   }
 
   void SetSourceStream(AudioNodeStream* aSource)
   {
     mSource = aSource;
   }
 
   enum Parameters {
     FREQUENCY,
     DETUNE,
     TYPE,
-    PERIODICWAVE_LENGTH,
     DISABLE_NORMALIZATION,
     START,
     STOP,
   };
   void RecvTimelineEvent(uint32_t aIndex,
                          AudioTimelineEvent& aEvent) override
   {
     mRecomputeParameters = true;
@@ -100,19 +98,17 @@ public:
   void SetInt32Parameter(uint32_t aIndex, int32_t aParam) override
   {
     switch (aIndex) {
       case TYPE:
         // Set the new type.
         mType = static_cast<OscillatorType>(aParam);
         if (mType == OscillatorType::Sine) {
           // Forget any previous custom data.
-          mCustomLength = 0;
           mCustomDisableNormalization = false;
-          mCustom = nullptr;
           mPeriodicWave = nullptr;
           mRecomputeParameters = true;
         }
         switch (mType) {
           case OscillatorType::Sine:
             mPhase = 0.0;
             break;
           case OscillatorType::Square:
@@ -122,41 +118,37 @@ public:
             break;
           case OscillatorType::Custom:
             break;
           default:
             NS_ERROR("Bad OscillatorNodeEngine type parameter.");
         }
         // End type switch.
         break;
-      case PERIODICWAVE_LENGTH:
-        MOZ_ASSERT(aParam >= 0, "negative custom array length");
-        mCustomLength = static_cast<uint32_t>(aParam);
-        break;
       case DISABLE_NORMALIZATION:
         MOZ_ASSERT(aParam >= 0, "negative custom array length");
         mCustomDisableNormalization = static_cast<uint32_t>(aParam);
         break;
       default:
         NS_ERROR("Bad OscillatorNodeEngine Int32Parameter.");
     }
     // End index switch.
   }
 
-  void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer) override
+  void SetBuffer(AudioChunk&& aBuffer) override
   {
-    MOZ_ASSERT(mCustomLength, "Custom buffer sent before length");
-    mCustom = aBuffer;
-    MOZ_ASSERT(mCustom->GetChannels() == 2,
+    MOZ_ASSERT(aBuffer.ChannelCount() == 2,
                "PeriodicWave should have sent two channels");
-    mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(),
-                                                  mCustom->GetData(0),
-                                                  mCustom->GetData(1),
-                                                  mCustomLength,
-                                                  mCustomDisableNormalization);
+    MOZ_ASSERT(aBuffer.mVolume == 1.0f);
+    mPeriodicWave =
+      WebCore::PeriodicWave::create(mSource->SampleRate(),
+                                    aBuffer.ChannelData<float>()[0],
+                                    aBuffer.ChannelData<float>()[1],
+                                    aBuffer.mDuration,
+                                    mCustomDisableNormalization);
   }
 
   void IncrementPhase()
   {
     const float twoPiFloat = float(2 * M_PI);
     mPhase += mPhaseIncrement;
     if (mPhase > twoPiFloat) {
       mPhase -= twoPiFloat;
@@ -368,20 +360,16 @@ public:
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
 
     // Not owned:
     // - mSource
     // - mDestination
     // - mFrequency (internal ref owned by node)
     // - mDetune (internal ref owned by node)
 
-    if (mCustom) {
-      amount += mCustom->SizeOfIncludingThis(aMallocSizeOf);
-    }
-
     if (mPeriodicWave) {
       amount += mPeriodicWave->sizeOfIncludingThis(aMallocSizeOf);
     }
 
     return amount;
   }
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
@@ -396,19 +384,17 @@ public:
   StreamTime mStop;
   AudioParamTimeline mFrequency;
   AudioParamTimeline mDetune;
   OscillatorType mType;
   float mPhase;
   float mFinalFrequency;
   float mPhaseIncrement;
   bool mRecomputeParameters;
-  RefPtr<ThreadSharedFloatArrayBufferList> mCustom;
   RefPtr<BasicWaveFormCache> mBasicWaveFormCache;
-  uint32_t mCustomLength;
   bool mCustomDisableNormalization;
   RefPtr<WebCore::PeriodicWave> mPeriodicWave;
 };
 
 OscillatorNode::OscillatorNode(AudioContext* aContext)
   : AudioScheduledSourceNode(aContext,
                              2,
                              ChannelCountMode::Max,
@@ -509,23 +495,20 @@ OscillatorNode::SendTypeToStream()
 }
 
 void OscillatorNode::SendPeriodicWaveToStream()
 {
   NS_ASSERTION(mType == OscillatorType::Custom,
                "Sending custom waveform to engine thread with non-custom type");
   MOZ_ASSERT(mStream, "Missing node stream.");
   MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
-  SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE_LENGTH,
-                             mPeriodicWave->DataLength());
   SendInt32ParameterToStream(OscillatorNodeEngine::DISABLE_NORMALIZATION,
                              mPeriodicWave->DisableNormalization());
-  RefPtr<ThreadSharedFloatArrayBufferList> data =
-    mPeriodicWave->GetThreadSharedBuffer();
-  mStream->SetBuffer(data.forget());
+  AudioChunk data = mPeriodicWave->GetThreadSharedBuffer();
+  mStream->SetBuffer(Move(data));
 }
 
 void
 OscillatorNode::Start(double aWhen, ErrorResult& aRv)
 {
   if (!WebAudioUtils::IsTimeValid(aWhen)) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return;
--- a/dom/media/webaudio/PeriodicWave.cpp
+++ b/dom/media/webaudio/PeriodicWave.cpp
@@ -25,41 +25,47 @@ PeriodicWave::PeriodicWave(AudioContext*
   : mContext(aContext)
   , mDisableNormalization(aDisableNormalization)
 {
   MOZ_ASSERT(aContext);
   MOZ_ASSERT(aRealData || aImagData);
 
   // Caller should have checked this and thrown.
   MOZ_ASSERT(aLength > 0);
-  mLength = aLength;
+  mCoefficients.mDuration = aLength;
 
-  // Copy coefficient data. The two arrays share an allocation.
-  mCoefficients = new ThreadSharedFloatArrayBufferList(2);
-  float* buffer = static_cast<float*>(malloc(aLength*sizeof(float)*2));
-  if (buffer == nullptr) {
+  // Copy coefficient data.
+  // The SharedBuffer and two arrays share a single allocation.
+  RefPtr<SharedBuffer> buffer =
+    SharedBuffer::Create(sizeof(float) * aLength * 2, fallible);
+  if (!buffer) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
+  auto data = static_cast<float*>(buffer->Data());
+  mCoefficients.mBuffer = Move(buffer);
+
   if (aRealData) {
-    PodCopy(buffer, aRealData, aLength);
+    PodCopy(data, aRealData, aLength);
   } else {
-    PodZero(buffer, aLength);
+    PodZero(data, aLength);
   }
-
-  mCoefficients->SetData(0, buffer, free, buffer);
+  mCoefficients.mChannelData.AppendElement(data);
 
+  data += aLength;
   if (aImagData) {
-    PodCopy(buffer+aLength, aImagData, aLength);
+    PodCopy(data, aImagData, aLength);
   } else {
-    PodZero(buffer+aLength, aLength);
+    PodZero(data, aLength);
   }
+  mCoefficients.mChannelData.AppendElement(data);
 
-  mCoefficients->SetData(1, nullptr, free, buffer+aLength);
+  mCoefficients.mVolume = 1.0f;
+  mCoefficients.mBufferFormat = AUDIO_FORMAT_FLOAT32;
 }
 
 /* static */ already_AddRefed<PeriodicWave>
 PeriodicWave::Constructor(const GlobalObject& aGlobal,
                           AudioContext& aAudioContext,
                           const PeriodicWaveOptions& aOptions,
                           ErrorResult& aRv)
 {
@@ -97,19 +103,17 @@ PeriodicWave::Constructor(const GlobalOb
 }
 
 size_t
 PeriodicWave::SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const
 {
   // Not owned:
   // - mContext
   size_t amount = 0;
-  if (!mCoefficients->IsShared()) {
-    amount += mCoefficients->SizeOfIncludingThis(aMallocSizeOf);
-  }
+  amount += mCoefficients.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
 
   return amount;
 }
 
 size_t
 PeriodicWave::SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const
 {
   return aMallocSizeOf(this) + SizeOfExcludingThisIfNotShared(aMallocSizeOf);
--- a/dom/media/webaudio/PeriodicWave.h
+++ b/dom/media/webaudio/PeriodicWave.h
@@ -41,37 +41,36 @@ public:
   {
     return mContext;
   }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   uint32_t DataLength() const
   {
-    return mLength;
+    return mCoefficients.mDuration;
   }
 
   bool DisableNormalization() const
   {
     return mDisableNormalization;
   }
 
-  ThreadSharedFloatArrayBufferList* GetThreadSharedBuffer() const
+  const AudioChunk& GetThreadSharedBuffer() const
   {
     return mCoefficients;
   }
 
   size_t SizeOfExcludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
   size_t SizeOfIncludingThisIfNotShared(MallocSizeOf aMallocSizeOf) const;
 
 private:
   ~PeriodicWave() = default;
 
+  AudioChunk mCoefficients;
   RefPtr<AudioContext> mContext;
-  RefPtr<ThreadSharedFloatArrayBufferList> mCoefficients;
-  uint32_t mLength;
   bool mDisableNormalization;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/media/webaudio/ScriptProcessorNode.cpp
+++ b/dom/media/webaudio/ScriptProcessorNode.cpp
@@ -116,18 +116,17 @@ public:
       MutexAutoLock lock(mOutputQueue.Lock());
       amount += mOutputQueue.SizeOfExcludingThis(aMallocSizeOf);
     }
 
     return amount;
   }
 
   // main thread
-  void FinishProducingOutputBuffer(ThreadSharedFloatArrayBufferList* aBuffer,
-                                   uint32_t aBufferSize)
+  void FinishProducingOutputBuffer(const AudioChunk& aBuffer)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     TimeStamp now = TimeStamp::Now();
 
     if (mLastEventTime.IsNull()) {
       mLastEventTime = now;
     } else {
@@ -137,47 +136,38 @@ public:
       // latency is also reset to 0.
       // It could happen that the output queue becomes empty before the input
       // node has fully caught up. In this case there will be events where
       // |(now - mLastEventTime)| is very short, making mLatency negative.
       // As this happens and the size of |mLatency| becomes greater than
       // MAX_LATENCY_S, frame dropping starts again to maintain an as short
       // output queue as possible.
       float latency = (now - mLastEventTime).ToSeconds();
-      float bufferDuration = aBufferSize / mSampleRate;
+      float bufferDuration = aBuffer.mDuration / mSampleRate;
       mLatency += latency - bufferDuration;
       mLastEventTime = now;
       if (fabs(mLatency) > MAX_LATENCY_S) {
         mDroppingBuffers = true;
       }
     }
 
     MutexAutoLock lock(mOutputQueue.Lock());
     if (mDroppingBuffers) {
       if (mOutputQueue.ReadyToConsume()) {
         return;
       }
       mDroppingBuffers = false;
       mLatency = 0;
     }
 
-    for (uint32_t offset = 0; offset < aBufferSize; offset += WEBAUDIO_BLOCK_SIZE) {
+    for (uint32_t offset = 0; offset < aBuffer.mDuration;
+         offset += WEBAUDIO_BLOCK_SIZE) {
       AudioChunk& chunk = mOutputQueue.Produce();
-      if (aBuffer) {
-        chunk.mDuration = WEBAUDIO_BLOCK_SIZE;
-        chunk.mBuffer = aBuffer;
-        chunk.mChannelData.SetLength(aBuffer->GetChannels());
-        for (uint32_t i = 0; i < aBuffer->GetChannels(); ++i) {
-          chunk.mChannelData[i] = aBuffer->GetData(i) + offset;
-        }
-        chunk.mVolume = 1.0f;
-        chunk.mBufferFormat = AUDIO_FORMAT_FLOAT32;
-      } else {
-        chunk.SetNull(WEBAUDIO_BLOCK_SIZE);
-      }
+      chunk = aBuffer;
+      chunk.SliceTo(offset, offset + WEBAUDIO_BLOCK_SIZE);
     }
   }
 
   // graph thread
   AudioChunk GetOutputBuffer()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     AudioChunk buffer;
@@ -379,67 +369,67 @@ private:
         , mStream(aStream)
         , mInputBuffer(aInputBuffer)
         , mPlaybackTime(aPlaybackTime)
       {
       }
 
       NS_IMETHOD Run() override
       {
-        RefPtr<ThreadSharedFloatArrayBufferList> output;
 
         auto engine =
           static_cast<ScriptProcessorNodeEngine*>(mStream->Engine());
+        AudioChunk output;
+        output.SetNull(engine->mBufferSize);
         {
           auto node = static_cast<ScriptProcessorNode*>
             (engine->NodeMainThread());
           if (!node) {
             return NS_OK;
           }
 
           if (node->HasListenersFor(nsGkAtoms::onaudioprocess)) {
-            output = DispatchAudioProcessEvent(node);
+            DispatchAudioProcessEvent(node, &output);
           }
           // The node may have been destroyed during event dispatch.
         }
 
         // Append it to our output buffer queue
-        engine->GetSharedBuffers()->
-          FinishProducingOutputBuffer(output, engine->mBufferSize);
+        engine->GetSharedBuffers()->FinishProducingOutputBuffer(output);
 
         return NS_OK;
       }
 
-      // Returns the output buffers if set in event handlers.
-      ThreadSharedFloatArrayBufferList*
-        DispatchAudioProcessEvent(ScriptProcessorNode* aNode)
+      // Sets up |output| iff buffers are set in event handlers.
+      void DispatchAudioProcessEvent(ScriptProcessorNode* aNode,
+                                     AudioChunk* aOutput)
       {
         AudioContext* context = aNode->Context();
         if (!context) {
-          return nullptr;
+          return;
         }
 
         AutoJSAPI jsapi;
         if (NS_WARN_IF(!jsapi.Init(aNode->GetOwner()))) {
-          return nullptr;
+          return;
         }
         JSContext* cx = jsapi.cx();
         uint32_t inputChannelCount = aNode->ChannelCount();
 
         // Create the input buffer
         RefPtr<AudioBuffer> inputBuffer;
         if (mInputBuffer) {
           ErrorResult rv;
           inputBuffer =
             AudioBuffer::Create(context->GetOwner(), inputChannelCount,
                                 aNode->BufferSize(), context->SampleRate(),
                                 mInputBuffer.forget(), rv);
           if (rv.Failed()) {
             rv.SuppressException();
-            return nullptr;
+            return;
           }
         }
 
         // Ask content to produce data in the output buffer
         // Note that we always avoid creating the output buffer here, and we try to
         // avoid creating the input buffer as well.  The AudioProcessingEvent class
         // knows how to lazily create them if needed once the script tries to access
         // them.  Otherwise, we may be able to get away without creating them!
@@ -453,20 +443,21 @@ private:
         // FinishProducingOutputBuffer() will optimize output = null.
         // GetThreadSharedChannelsForRate() may also return null after OOM.
         if (event->HasOutputBuffer()) {
           ErrorResult rv;
           AudioBuffer* buffer = event->GetOutputBuffer(rv);
           // HasOutputBuffer() returning true means that GetOutputBuffer()
           // will not fail.
           MOZ_ASSERT(!rv.Failed());
-          return buffer->GetThreadSharedChannelsForRate(cx);
+          *aOutput = buffer->GetThreadSharedChannelsForRate(cx);
+          MOZ_ASSERT(aOutput->IsNull() ||
+                     aOutput->mBufferFormat == AUDIO_FORMAT_FLOAT32,
+                     "AudioBuffers initialized from JS have float data");
         }
-
-        return nullptr;
       }
     private:
       RefPtr<AudioNodeStream> mStream;
       RefPtr<ThreadSharedFloatArrayBufferList> mInputBuffer;
       double mPlaybackTime;
     };
 
     RefPtr<Command> command = new Command(aStream, mInputBuffer.forget(),
--- a/dom/media/webaudio/blink/Reverb.cpp
+++ b/dom/media/webaudio/blink/Reverb.cpp
@@ -39,25 +39,25 @@ namespace WebCore {
 
 // Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal
 const float GainCalibration = 0.00125f;
 const float GainCalibrationSampleRate = 44100;
 
 // A minimum power value to when normalizing a silent (or very quiet) impulse response
 const float MinPower = 0.000125f;
 
-static float calculateNormalizationScale(ThreadSharedFloatArrayBufferList* response, size_t aLength, float sampleRate)
+static float calculateNormalizationScale(const nsTArray<const float*>& response, size_t aLength, float sampleRate)
 {
     // Normalize by RMS power
-    size_t numberOfChannels = response->GetChannels();
+    size_t numberOfChannels = response.Length();
 
     float power = 0;
 
     for (size_t i = 0; i < numberOfChannels; ++i) {
-        float channelPower = AudioBufferSumOfSquares(static_cast<const float*>(response->GetData(i)), aLength);
+        float channelPower = AudioBufferSumOfSquares(response[i], aLength);
         power += channelPower;
     }
 
     power = sqrt(power / (numberOfChannels * aLength));
 
     // Protect against accidental overload
     if (!IsFinite(power) || IsNaN(power) || power < MinPower)
         power = MinPower;
@@ -66,43 +66,41 @@ static float calculateNormalizationScale
 
     scale *= GainCalibration; // calibrate to make perceived volume same as unprocessed
 
     // Scale depends on sample-rate.
     if (sampleRate)
         scale *= GainCalibrationSampleRate / sampleRate;
 
     // True-stereo compensation
-    if (response->GetChannels() == 4)
+    if (numberOfChannels == 4)
         scale *= 0.5f;
 
     return scale;
 }
 
-Reverb::Reverb(ThreadSharedFloatArrayBufferList* impulseResponse, size_t impulseResponseBufferLength, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate)
+Reverb::Reverb(const AudioChunk& impulseResponse, size_t maxFFTSize, bool useBackgroundThreads, bool normalize, float sampleRate)
 {
-    float scale = 1;
+    size_t impulseResponseBufferLength = impulseResponse.mDuration;
+    float scale = impulseResponse.mVolume;
 
-    AutoTArray<const float*,4> irChannels;
-    for (size_t i = 0; i < impulseResponse->GetChannels(); ++i) {
-        irChannels.AppendElement(impulseResponse->GetData(i));
-    }
+    AutoTArray<const float*,4> irChannels(impulseResponse.ChannelData<float>());
     AutoTArray<float,1024> tempBuf;
 
     if (normalize) {
-        scale = calculateNormalizationScale(impulseResponse, impulseResponseBufferLength, sampleRate);
+        scale = calculateNormalizationScale(irChannels, impulseResponseBufferLength, sampleRate);
+    }
 
-        if (scale) {
-            tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength);
-            for (uint32_t i = 0; i < irChannels.Length(); ++i) {
-                float* buf = &tempBuf[i*impulseResponseBufferLength];
-                AudioBufferCopyWithScale(irChannels[i], scale, buf,
-                                         impulseResponseBufferLength);
-                irChannels[i] = buf;
-            }
+    if (scale != 1.0f) {
+        tempBuf.SetLength(irChannels.Length()*impulseResponseBufferLength);
+        for (uint32_t i = 0; i < irChannels.Length(); ++i) {
+            float* buf = &tempBuf[i*impulseResponseBufferLength];
+            AudioBufferCopyWithScale(irChannels[i], scale, buf,
+                                     impulseResponseBufferLength);
+            irChannels[i] = buf;
         }
     }
 
     initialize(irChannels, impulseResponseBufferLength,
                maxFFTSize, useBackgroundThreads);
 }
 
 size_t Reverb::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
--- a/dom/media/webaudio/blink/Reverb.h
+++ b/dom/media/webaudio/blink/Reverb.h
@@ -30,31 +30,26 @@
 #define Reverb_h
 
 #include "ReverbConvolver.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 #include "AudioBlock.h"
 #include "mozilla/MemoryReporting.h"
 
-namespace mozilla {
-class ThreadSharedFloatArrayBufferList;
-} // namespace mozilla
-
 namespace WebCore {
 
 // Multi-channel convolution reverb with channel matrixing - one or more ReverbConvolver objects are used internally.
 
 class Reverb {
 public:
     enum { MaxFrameSize = 256 };
 
     // renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread).
-    Reverb(mozilla::ThreadSharedFloatArrayBufferList* impulseResponseBuffer,
-           size_t impulseResponseBufferLength, size_t maxFFTSize,
+    Reverb(const mozilla::AudioChunk& impulseResponseBuffer, size_t maxFFTSize,
            bool useBackgroundThreads, bool normalize, float sampleRate);
 
     void process(const mozilla::AudioBlock* sourceBus,
                  mozilla::AudioBlock* destinationBus);
 
     size_t impulseResponseLength() const { return m_impulseResponseLength; }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
--- a/dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html
+++ b/dom/tests/mochitest/pointerlock/file_targetOutOfFocus.html
@@ -38,17 +38,17 @@
 
         function runTests () {
           ok(divPointerLock, "Pointer should be locked even if " +
             "the element being locked is not focused");
         }
 
         input.addEventListener("focus", function() {
           div.requestPointerLock();
-        });
+        }, { once: true });
 
         document.addEventListener("pointerlockchange", function (e) {
           if (document.pointerLockElement === div) {
             divPointerLock = true;
             addFullscreenChangeContinuation("exit", function() {
               runTests();
               SimpleTest.finish();
             });
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -662,16 +662,17 @@ private:
                                                                HardwareVideoDecodingForceEnabled, bool, false);
 #ifdef XP_WIN
   DECL_GFX_PREF(Live, "media.windows-media-foundation.allow-d3d11-dxva", PDMWMFAllowD3D11, bool, true);
   DECL_GFX_PREF(Live, "media.windows-media-foundation.max-dxva-videos", PDMWMFMaxDXVAVideos, uint32_t, 8);
   DECL_GFX_PREF(Live, "media.windows-media-foundation.use-nv12-format", PDMWMFUseNV12Format, bool, true);
   DECL_GFX_PREF(Once, "media.windows-media-foundation.use-sync-texture", PDMWMFUseSyncTexture, bool, true);
   DECL_GFX_PREF(Live, "media.wmf.low-latency.enabled", PDMWMFLowLatencyEnabled, bool, false);
   DECL_GFX_PREF(Live, "media.wmf.skip-blacklist", PDMWMFSkipBlacklist, bool, false);
+  DECL_GFX_PREF(Live, "media.wmf.deblacklisting-for-telemetry-in-gpu-process", PDMWMFDeblacklistingForTelemetryInGPUProcess, bool, false);
 #endif
 
   // These affect how line scrolls from wheel events will be accelerated.
   DECL_GFX_PREF(Live, "mousewheel.acceleration.factor",        MouseWheelAccelerationFactor, int32_t, -1);
   DECL_GFX_PREF(Live, "mousewheel.acceleration.start",         MouseWheelAccelerationStart, int32_t, -1);
 
   // This affects whether events will be routed through APZ or not.
   DECL_GFX_PREF(Live, "mousewheel.system_scroll_override_on_root_content.enabled",
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -3548,18 +3548,21 @@ nsDisplayBackgroundImage::CanBuildWebRen
 
 bool
 nsDisplayBackgroundImage::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
                                                   const StackingContextHelper& aSc,
                                                   nsTArray<WebRenderParentCommand>& aParentCommands,
                                                   WebRenderLayerManager* aManager,
                                                   nsDisplayListBuilder* aDisplayListBuilder)
 {
-  if (!CanBuildWebRenderDisplayItems(aManager)) {
-    return false;
+  if (aManager->IsLayersFreeTransaction()) {
+    ContainerLayerParameters parameter;
+    if (GetLayerState(aDisplayListBuilder, aManager, parameter) != LAYER_ACTIVE) {
+      return false;
+    }
   }
 
   if (aDisplayListBuilder) {
     mImageFlags = aDisplayListBuilder->GetBackgroundPaintFlags();
   }
   CheckForBorderItem(this, mImageFlags);
   nsCSSRendering::PaintBGParams params =
     nsCSSRendering::PaintBGParams::ForSingleLayer(*StyleFrame()->PresContext(),
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2767,19 +2767,17 @@ Gecko_DocumentRule_UseForPresentation(Ra
   return css::DocumentRule::UseForPresentation(doc, docURI, docURISpec,
                                                *aPattern, aURLMatchingFunction);
 }
 
 void
 Gecko_SetJemallocThreadLocalArena(bool enabled)
 {
 #if defined(MOZ_MEMORY)
-  // At this point we convert |enabled| from a plain C++ bool to a
-  // |jemalloc_bool|, so be on the safe side.
-  jemalloc_thread_local_arena(!!enabled);
+  jemalloc_thread_local_arena(enabled);
 #endif
 }
 
 #include "nsStyleStructList.h"
 
 #undef STYLE_STRUCT
 
 #ifndef MOZ_STYLO
deleted file mode 100644
--- a/memory/build/jemalloc_config.cpp
+++ /dev/null
@@ -1,10 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include <mozilla/Assertions.h>
-
-/* Provide an abort function for use in jemalloc code */
-extern "C" void moz_abort() {
-  MOZ_CRASH();
-}
--- a/memory/build/malloc_decls.h
+++ b/memory/build/malloc_decls.h
@@ -56,16 +56,16 @@ MALLOC_DECL(memalign, void *, size_t, si
 MALLOC_DECL(valloc, void *, size_t)
 MALLOC_DECL(malloc_usable_size, size_t, usable_ptr_t)
 MALLOC_DECL(malloc_good_size, size_t, size_t)
 #  endif
 #  if MALLOC_FUNCS & MALLOC_FUNCS_JEMALLOC
 MALLOC_DECL_VOID(jemalloc_stats, jemalloc_stats_t *)
 MALLOC_DECL_VOID(jemalloc_purge_freed_pages)
 MALLOC_DECL_VOID(jemalloc_free_dirty_pages)
-MALLOC_DECL_VOID(jemalloc_thread_local_arena, jemalloc_bool)
+MALLOC_DECL_VOID(jemalloc_thread_local_arena, bool)
 #  endif
 
 #  undef MALLOC_DECL_VOID
 #endif /* MALLOC_DECL */
 
 #undef MALLOC_DECL
 #undef MALLOC_FUNCS
--- a/memory/build/moz.build
+++ b/memory/build/moz.build
@@ -15,17 +15,16 @@ DEFINES['MOZ_MEMORY_IMPL'] = True
 if CONFIG['MOZ_REPLACE_MALLOC']:
     EXPORTS += [
         'malloc_decls.h',
         'replace_malloc.h',
         'replace_malloc_bridge.h',
     ]
 
 SOURCES += [
-    'jemalloc_config.cpp',
     'mozmemory_wrap.c',
 ]
 
 if CONFIG['MOZ_REPLACE_MALLOC']:
     SOURCES += [
         'replace_malloc.c',
     ]
 
--- a/memory/build/mozmemory.h
+++ b/memory/build/mozmemory.h
@@ -80,11 +80,11 @@ MOZ_JEMALLOC_API void jemalloc_purge_fre
 /*
  * Free all unused dirty pages in all arenas. Calling this function will slow
  * down subsequent allocations so it is recommended to use it only when
  * memory needs to be reclaimed at all costs (see bug 805855). This function
  * provides functionality similar to mallctl("arenas.purge") in jemalloc 3.
  */
 MOZ_JEMALLOC_API void jemalloc_free_dirty_pages();
 
-MOZ_JEMALLOC_API void jemalloc_thread_local_arena(jemalloc_bool enabled);
+MOZ_JEMALLOC_API void jemalloc_thread_local_arena(bool enabled);
 
 #endif /* mozmemory_h */
--- a/memory/mozjemalloc/mozjemalloc.cpp
+++ b/memory/mozjemalloc/mozjemalloc.cpp
@@ -239,18 +239,16 @@ typedef long ssize_t;
 #include <malloc/malloc.h>
 #endif
 
 #endif
 
 #include "mozjemalloc_types.h"
 #include "linkedlist.h"
 
-extern "C" void moz_abort();
-
 /* Some tools, such as /dev/dsp wrappers, LD_PRELOAD libraries that
  * happen to override mmap() and call dlsym() from their overridden
  * mmap(). The problem is that dlsym() calls malloc(), and this ends
  * up in a dead lock in jemalloc.
  * On these systems, we prefer to directly use the system call.
  * We do that for Linux systems and kfreebsd with GNU userland.
  * Note sanity checks are not done (alignment of offset, ...) because
  * the uses of mmap are pretty limited, in jemalloc.
@@ -1267,25 +1265,25 @@ pages_decommit(void *addr, size_t size)
 	* to VirtualAlloc and recycled, so decommitting the entire region in one
 	* go may not be valid. However, since we allocate at least a chunk at a
 	* time, we may touch any region in chunksized increments.
 	*/
 	size_t pages_size = std::min(size, chunksize -
 		CHUNK_ADDR2OFFSET((uintptr_t)addr));
 	while (size > 0) {
 		if (!VirtualFree(addr, pages_size, MEM_DECOMMIT))
-			moz_abort();
+			MOZ_CRASH();
 		addr = (void *)((uintptr_t)addr + pages_size);
 		size -= pages_size;
 		pages_size = std::min(size, chunksize);
 	}
 #else
 	if (mmap(addr, size, PROT_NONE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1,
 	    0) == MAP_FAILED)
-		moz_abort();
+		MOZ_CRASH();
 	MozTagAnonymousMemory(addr, size, "jemalloc-decommitted");
 #endif
 }
 
 static inline void
 pages_commit(void *addr, size_t size)
 {
 
@@ -1295,25 +1293,25 @@ pages_commit(void *addr, size_t size)
 	* to VirtualAlloc and recycled, so committing the entire region in one
 	* go may not be valid. However, since we allocate at least a chunk at a
 	* time, we may touch any region in chunksized increments.
 	*/
 	size_t pages_size = std::min(size, chunksize -
 		CHUNK_ADDR2OFFSET((uintptr_t)addr));
 	while (size > 0) {
 		if (!VirtualAlloc(addr, pages_size, MEM_COMMIT, PAGE_READWRITE))
-			moz_abort();
+			MOZ_CRASH();
 		addr = (void *)((uintptr_t)addr + pages_size);
 		size -= pages_size;
 		pages_size = std::min(size, chunksize);
 	}
 #  else
 	if (mmap(addr, size, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE |
 	    MAP_ANON, -1, 0) == MAP_FAILED)
-		moz_abort();
+		MOZ_CRASH();
 	MozTagAnonymousMemory(addr, size, "jemalloc");
 #  endif
 }
 
 static bool
 base_pages_alloc(size_t minsize)
 {
 	size_t csize;
@@ -4274,17 +4272,17 @@ malloc_init_hard(void)
 #endif
 
 	/* We assume that the page size is a power of 2. */
 	MOZ_ASSERT(((result - 1) & result) == 0);
 #ifdef MALLOC_STATIC_SIZES
 	if (pagesize % (size_t) result) {
 		_malloc_message(_getprogname(),
 				"Compile-time page size does not divide the runtime one.\n");
-		moz_abort();
+		MOZ_CRASH();
 	}
 #else
 	pagesize = (size_t) result;
 	pagesize_mask = (size_t) result - 1;
 	pagesize_2pow = ffs((int)result) - 1;
 #endif
 
 	/* Get runtime configuration. */
@@ -5097,17 +5095,17 @@ void
 
 #if defined(MOZ_MEMORY_DARWIN)
 
 __attribute__((constructor))
 void
 jemalloc_darwin_init(void)
 {
 	if (malloc_init_hard())
-		moz_abort();
+		MOZ_CRASH();
 }
 
 #endif
 
 /*
  * is_malloc(malloc_impl) is some macro magic to detect if malloc_impl is
  * defined as "malloc" in mozmemory_wrap.h
  */
--- a/memory/mozjemalloc/mozjemalloc_types.h
+++ b/memory/mozjemalloc/mozjemalloc_types.h
@@ -33,34 +33,33 @@
 #define _JEMALLOC_TYPES_H_
 
 /* grab size_t */
 #ifdef _MSC_VER
 #include <crtdefs.h>
 #else
 #include <stddef.h>
 #endif
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef unsigned char jemalloc_bool;
-
 /*
  * jemalloc_stats() is not a stable interface.  When using jemalloc_stats_t, be
  * sure that the compiled results of jemalloc.c are in sync with this header
  * file.
  */
 typedef struct {
 	/*
 	 * Run-time configuration settings.
 	 */
-	jemalloc_bool	opt_junk;	/* Fill allocated memory with kAllocJunk? */
-	jemalloc_bool	opt_zero;	/* Fill allocated memory with 0x0? */
+	bool	opt_junk;	/* Fill allocated memory with kAllocJunk? */
+	bool	opt_zero;	/* Fill allocated memory with 0x0? */
 	size_t	narenas;	/* Number of arenas. */
 	size_t	quantum;	/* Allocation quantum. */
 	size_t	small_max;	/* Max quantum-spaced allocation size. */
 	size_t	large_max;	/* Max sub-chunksize allocation size. */
 	size_t	chunksize;	/* Size of each virtual memory mapping. */
 	size_t	dirty_max;	/* Max dirty pages per arena. */
 
 	/*
--- a/memory/replace/replace/ReplaceMalloc.cpp
+++ b/memory/replace/replace/ReplaceMalloc.cpp
@@ -248,16 +248,16 @@ replace_jemalloc_free_dirty_pages(void)
   gFuncs->jemalloc_free_dirty_pages();
   const malloc_hook_table_t* hook_table = gHookTable;
   if (hook_table && hook_table->jemalloc_free_dirty_pages_hook) {
     hook_table->jemalloc_free_dirty_pages_hook();
   }
 }
 
 void
-replace_jemalloc_thread_local_arena(jemalloc_bool aEnabled)
+replace_jemalloc_thread_local_arena(bool aEnabled)
 {
   gFuncs->jemalloc_thread_local_arena(aEnabled);
   const malloc_hook_table_t* hook_table = gHookTable;
   if (hook_table && hook_table->jemalloc_thread_local_arena_hook) {
     hook_table->jemalloc_thread_local_arena_hook(aEnabled);
   }
 }
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -61,16 +61,17 @@
 #    define MOZ_HAVE_NEVER_INLINE        __attribute__((noinline))
 #  endif
 #  if __has_attribute(noreturn)
 #    define MOZ_HAVE_NORETURN            __attribute__((noreturn))
 #  endif
 #elif defined(__GNUC__)
 #  define MOZ_HAVE_NEVER_INLINE          __attribute__((noinline))
 #  define MOZ_HAVE_NORETURN              __attribute__((noreturn))
+#  define MOZ_HAVE_NORETURN_PTR          __attribute__((noreturn))
 #endif
 
 /*
  * When built with clang analyzer (a.k.a scan-build), define MOZ_HAVE_NORETURN
  * to mark some false positives
  */
 #ifdef __clang_analyzer__
 #  if __has_extension(attribute_analyzer_noreturn)
@@ -97,23 +98,31 @@
  *
  *   MOZ_NORETURN void abort(const char* msg);
  *
  * This modifier permits the compiler to optimize code assuming a call to such a
  * function will never return.  It also enables the compiler to avoid spurious
  * warnings about not initializing variables, or about any other seemingly-dodgy
  * operations performed after the function returns.
  *
+ * There are two variants. The GCC version of NORETURN may be applied to a
+ * function pointer, while for MSVC it may not.
+ *
  * This modifier does not affect the corresponding function's linking behavior.
  */
 #if defined(MOZ_HAVE_NORETURN)
 #  define MOZ_NORETURN          MOZ_HAVE_NORETURN
 #else
 #  define MOZ_NORETURN          /* no support */
 #endif
+#if defined(MOZ_HAVE_NORETURN_PTR)
+#  define MOZ_NORETURN_PTR      MOZ_HAVE_NORETURN_PTR
+#else
+#  define MOZ_NORETURN_PTR      /* no support */
+#endif
 
 /**
  * MOZ_COLD tells the compiler that a function is "cold", meaning infrequently
  * executed. This may lead it to optimize for size more aggressively than speed,
  * or to allocate the body of the function in a distant part of the text segment
  * to help keep it from taking up unnecessary icache when it isn't in use.
  *
  * Place this attribute at the very beginning of a function definition. For
--- a/mobile/android/app/src/main/res/layout-large/tabs_layout_item_view.xml
+++ b/mobile/android/app/src/main/res/layout-large/tabs_layout_item_view.xml
@@ -25,17 +25,19 @@
                android:layout_width="0dip"
                android:layout_height="wrap_content"
                android:layout_weight="1.0"
                style="@style/TabLayoutItemTextAppearance"
                android:textSize="14sp"
                android:textColor="@color/tab_item_title"
                android:singleLine="true"
                android:duplicateParentState="true"
+               android:layout_gravity="center_vertical"
                gecko:fadeWidth="15dp"
+               android:minHeight="24dp"
                android:paddingRight="5dp"
                android:paddingEnd="5dp"
                android:paddingLeft="0dp"
                android:paddingStart="0dp"
                android:drawablePadding="6dp"/>
 
         <!-- Use of baselineAlignBottom only supported from API 11+ - if this needs to work on lower API versions
              we'll need to override getBaseLine() and return image height, but we assume this won't happen -->
--- a/mobile/android/app/src/main/res/layout/activity_stream_topsites_page.xml
+++ b/mobile/android/app/src/main/res/layout/activity_stream_topsites_page.xml
@@ -1,6 +1,16 @@
 <?xml version="1.0" encoding="utf-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/. -->
+
+<!-- The paddingEnd visible to the user comes from the padding between items. As such, paddingEnd here should be 0dp.
+     We need to specify it explicitly to ensure paddingLeft (for devices that don't support paddingStart) doesn't take
+     effect in addition to paddingStart in RTL layouts. -->
 <org.mozilla.gecko.activitystream.homepanel.topsites.TopSitesPage xmlns:android="http://schemas.android.com/apk/res/android"
                                                                   android:orientation="vertical"
-                                                                  android:layout_width="wrap_content"
+                                                                  android:layout_width="match_parent"
                                                                   android:layout_height="match_parent"
-                                                                  android:paddingLeft="10dp"/>
+                                                                  android:paddingStart="10dp"
+                                                                  android:paddingLeft="10dp"
+                                                                  android:paddingEnd="0dp"
+                                                                  android:paddingRight="0dp"/>
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/main/res/menu-large-v26/browser_app_menu.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-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/. -->
+
+<!-- We disable AlwaysShowAction because we interpret the menu
+     attributes ourselves and thus the warning isn't relevant to us. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:tools="http://schemas.android.com/tools"
+      tools:ignore="AlwaysShowAction">
+
+    <item android:id="@+id/reload"
+          android:icon="@drawable/ic_menu_reload"
+          android:title="@string/reload"
+          android:showAsAction="always"/>
+
+    <item android:id="@+id/back"
+          android:icon="@drawable/ic_menu_back"
+          android:title="@string/back"
+          android:visible="false"/>
+
+    <item android:id="@+id/forward"
+          android:icon="@drawable/ic_menu_forward"
+          android:title="@string/forward"
+          android:visible="false"/>
+
+    <item android:id="@+id/bookmark"
+          android:icon="@drawable/ic_menu_bookmark_add"
+          android:title="@string/bookmark"
+          android:showAsAction="ifRoom"/>
+
+    <item android:id="@+id/share"
+          android:icon="@drawable/ic_menu_share"
+          android:title="@string/share"
+          android:showAsAction="ifRoom"/>
+
+    <item android:id="@+id/new_tab"
+          android:title="@string/new_tab"/>
+
+    <item android:id="@+id/new_private_tab"
+          android:title="@string/new_private_tab"/>
+
+    <item android:id="@+id/bookmarks_list"
+          android:title="@string/bookmarks_title"/>
+
+    <item android:id="@+id/history_list"
+          android:title="@string/history_title"/>
+
+    <item android:id="@+id/find_in_page"
+          android:title="@string/find_in_page" />
+
+    <item android:id="@+id/desktop_mode"
+          android:title="@string/desktop_mode"
+          android:checkable="true" />
+
+    <item android:id="@+id/addons_top_level"
+          android:title="@string/addons"
+          android:visible="false" />
+
+    <item android:id="@+id/page"
+          android:title="@string/page">
+
+        <menu>
+
+            <item android:id="@+id/subscribe"
+                  android:title="@string/contextmenu_subscribe"/>
+
+            <item android:id="@+id/save_as_pdf"
+                  android:title="@string/save_as_pdf"/>
+
+            <item android:id="@+id/print"
+                  android:title="@string/print"/>
+
+            <item android:id="@+id/add_search_engine"
+                  android:title="@string/contextmenu_add_search_engine"/>
+
+            <item android:id="@+id/set_as_homepage"
+                  android:title="@string/contextmenu_set_as_homepage"/>
+
+        </menu>
+
+    </item>
+
+    <item android:id="@+id/tools"
+          android:title="@string/tools">
+
+        <menu>
+
+            <item android:id="@+id/downloads"
+                  android:title="@string/downloads"/>
+
+            <item android:id="@+id/addons"
+                  android:title="@string/addons"/>
+
+            <item android:id="@+id/logins"
+                  android:title="@string/logins"/>
+
+            <item android:id="@+id/new_guest_session"
+                  android:visible="false"
+                  android:title="@string/new_guest_session"/>
+
+            <item android:id="@+id/exit_guest_session"
+                  android:visible="false"
+                  android:title="@string/exit_guest_session"/>
+
+        </menu>
+
+    </item>
+
+    <item android:id="@+id/char_encoding"
+          android:visible="false"
+          android:title="@string/char_encoding"/>
+
+    <item android:id="@+id/settings"
+          android:title="@string/settings" />
+
+    <item android:id="@+id/help"
+          android:title="@string/help_menu" />
+
+</menu>
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/main/res/menu-v26/activitystream_contextmenu.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Group ID's are required, otherwise NavigationView won't show any dividers. The ID's are unused, but still required. -->
+    <group android:id="@+id/group0">
+        <item
+            android:id="@+id/open_new_tab"
+            android:icon="@drawable/as_tab"
+            android:title="@string/contextmenu_open_new_tab"/>
+        <item
+            android:id="@+id/open_new_private_tab"
+            android:icon="@drawable/as_private"
+            android:title="@string/contextmenu_open_private_tab"/>
+    </group>
+
+    <group android:id="@+id/group1">
+        <item
+            android:id="@+id/dismiss"
+            android:icon="@drawable/as_dismiss"
+            android:title="@string/activity_stream_remove"/>
+
+        <item
+            android:id="@+id/delete"
+            android:icon="@drawable/as_bin"
+            android:visible="false"
+            android:title="@string/activity_stream_delete_history"/>
+    </group>
+
+    <group android:id="@+id/group2">
+        <item
+            android:id="@+id/bookmark"
+            android:icon="@drawable/as_bookmark"
+            android:title="@string/bookmark"/>
+        <item
+            android:id="@+id/share"
+            android:icon="@drawable/as_share"
+            android:title="@string/share"/>
+        <item
+            android:id="@+id/copy_url"
+            android:icon="@drawable/as_copy"
+            android:title="@string/contextmenu_copyurl"/>
+        <item
+            android:id="@+id/pin"
+            android:icon="@drawable/as_pin"
+            android:title="@string/contextmenu_top_sites_pin"/>
+    </group>
+</menu>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/main/res/menu-v26/browser_app_menu.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-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/. -->
+
+<!-- We disable AlwaysShowAction because we interpret the menu
+     attributes ourselves and thus the warning isn't relevant to us. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:tools="http://schemas.android.com/tools"
+      tools:ignore="AlwaysShowAction">
+
+    <item android:id="@+id/back"
+          android:icon="@drawable/ic_menu_back"
+          android:title="@string/back"
+          android:showAsAction="always"/>
+
+    <item android:id="@+id/forward"
+          android:icon="@drawable/ic_menu_forward"
+          android:title="@string/forward"
+          android:showAsAction="always"/>
+
+    <item android:id="@+id/bookmark"
+          android:icon="@drawable/ic_menu_bookmark_add"
+          android:title="@string/bookmark"
+          android:showAsAction="always"/>
+
+    <item android:id="@+id/reload"
+          android:icon="@drawable/ic_menu_reload"
+          android:title="@string/reload"
+          android:showAsAction="always"/>
+
+    <item android:id="@+id/share"
+          android:icon="@drawable/ic_menu_share"
+          android:title="@string/share"
+          android:showAsAction="ifRoom"/>
+
+    <item android:id="@+id/new_tab"
+          android:title="@string/new_tab"/>
+
+    <item android:id="@+id/new_private_tab"
+          android:title="@string/new_private_tab"/>
+
+    <item android:id="@+id/bookmarks_list"
+          android:title="@string/bookmarks_title"/>
+
+    <item android:id="@+id/history_list"
+          android:title="@string/history_title"/>
+
+    <item android:id="@+id/find_in_page"
+          android:title="@string/find_in_page" />
+
+    <item android:id="@+id/desktop_mode"
+          android:title="@string/desktop_mode"
+          android:checkable="true" />
+
+    <item android:id="@+id/addons_top_level"
+          android:title="@string/addons"
+          android:visible="false" />
+
+    <item android:id="@+id/page"
+          android:title="@string/page">
+
+        <menu>
+
+            <item android:id="@+id/subscribe"
+                  android:title="@string/contextmenu_subscribe"/>
+
+            <item android:id="@+id/save_as_pdf"
+                  android:title="@string/save_as_pdf"/>
+
+            <item android:id="@+id/print"
+                  android:title="@string/print"/>
+
+            <item android:id="@+id/add_search_engine"
+                  android:title="@string/contextmenu_add_search_engine"/>
+
+            <item android:id="@+id/set_as_homepage"
+                  android:title="@string/contextmenu_set_as_homepage"/>
+
+        </menu>
+
+    </item>
+
+    <item android:id="@+id/tools"
+          android:title="@string/tools">
+
+        <menu>
+
+            <item android:id="@+id/downloads"
+                  android:title="@string/downloads"/>
+
+            <item android:id="@+id/addons"
+                  android:title="@string/addons"/>
+
+            <item android:id="@+id/logins"
+                  android:title="@string/logins"/>
+
+            <item android:id="@+id/new_guest_session"
+                  android:visible="false"
+                  android:title="@string/new_guest_session"/>
+
+            <item android:id="@+id/exit_guest_session"
+                  android:visible="false"
+                  android:title="@string/exit_guest_session"/>
+
+        </menu>
+
+    </item>
+
+    <item android:id="@+id/char_encoding"
+          android:visible="false"
+          android:title="@string/char_encoding"/>
+
+    <item android:id="@+id/settings"
+          android:title="@string/settings" />
+
+    <item android:id="@+id/help"
+          android:title="@string/help_menu" />
+
+</menu>
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/main/res/menu-v26/home_contextmenu.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-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/. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/home_open_new_tab"
+          android:title="@string/contextmenu_open_new_tab"/>
+
+    <item android:id="@+id/home_open_private_tab"
+          android:title="@string/contextmenu_open_private_tab"/>
+
+    <item android:id="@+id/home_copyurl"
+          android:title="@string/contextmenu_copyurl"/>
+
+    <item android:id="@+id/home_share"
+          android:title="@string/contextmenu_share"/>
+
+    <item android:id="@+id/top_sites_edit"
+          android:title="@string/contextmenu_top_sites_edit"/>
+
+    <item android:id="@+id/top_sites_pin"
+          android:title="@string/contextmenu_top_sites_pin"/>
+
+    <item android:id="@+id/top_sites_unpin"
+          android:title="@string/contextmenu_top_sites_unpin"/>
+
+    <item android:id="@+id/home_edit_bookmark"
+          android:title="@string/contextmenu_edit_bookmark"/>
+
+    <item android:id="@+id/home_remove"
+          android:title="@string/contextmenu_remove"/>
+
+    <item android:id="@+id/home_as_pin"
+        android:title="@string/contextmenu_top_sites_pin"/>
+
+    <item android:id="@+id/home_set_as_homepage"
+          android:title="@string/contextmenu_set_as_homepage"/>
+
+</menu>
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/main/res/menu-v26/titlebar_contextmenu.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-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/. -->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/pasteandgo"
+          android:title="@string/contextmenu_pasteandgo"/>
+
+    <item android:id="@+id/paste"
+          android:title="@string/contextmenu_paste"/>
+
+    <item android:id="@+id/subscribe"
+          android:title="@string/contextmenu_subscribe"/>
+
+    <item android:id="@+id/add_search_engine"
+          android:title="@string/contextmenu_add_search_engine"/>
+
+    <item android:id="@+id/copyurl"
+          android:title="@string/contextmenu_copyurl"/>
+
+    <item android:id="@+id/set_as_homepage"
+          android:title="@string/contextmenu_set_as_homepage"/>
+
+</menu>
new file mode 100644
--- /dev/null
+++ b/mobile/android/app/src/main/res/menu-xlarge-v26/browser_app_menu.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-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/. -->
+
+<!-- We disable AlwaysShowAction because we interpret the menu
+     attributes ourselves and thus the warning isn't relevant to us. -->
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+      xmlns:tools="http://schemas.android.com/tools"
+      tools:ignore="AlwaysShowAction">
+
+    <item android:id="@+id/reload"
+          android:icon="@drawable/ic_menu_reload"
+          android:title="@string/reload"
+          android:showAsAction="always"/>
+
+    <item android:id="@+id/back"
+          android:icon="@drawable/ic_menu_back"
+          android:title="@string/back"
+          android:visible="false"/>
+
+    <item android:id="@+id/forward"
+          android:icon="@drawable/ic_menu_forward"
+          android:title="@string/forward"
+          android:visible="false"/>
+
+    <item android:id="@+id/bookmark"
+          android:icon="@drawable/ic_menu_bookmark_add"
+          android:title="@string/bookmark"
+          android:showAsAction="always"/>
+
+    <item android:id="@+id/share"
+          android:icon="@drawable/ic_menu_share"
+          android:title="@string/share"
+          android:showAsAction="ifRoom"/>
+
+    <item android:id="@+id/new_tab"
+          android:title="@string/new_tab"/>
+
+    <item android:id="@+id/new_private_tab"
+          android:title="@string/new_private_tab"/>
+
+    <item android:id="@+id/bookmarks_list"
+          android:title="@string/bookmarks_title"/>
+
+    <item android:id="@+id/history_list"
+          android:title="@string/history_title"/>
+
+    <item android:id="@+id/find_in_page"
+          android:title="@string/find_in_page" />
+
+    <item android:id="@+id/addons_top_level"
+          android:title="@string/addons"
+          android:visible="false" />
+
+    <item android:id="@+id/desktop_mode"
+          android:title="@string/desktop_mode"
+          android:checkable="true" />
+
+
+    <item android:id="@+id/page"
+          android:title="@string/page">
+
+        <menu>
+
+            <item android:id="@+id/subscribe"
+                  android:title="@string/contextmenu_subscribe"/>
+
+            <item android:id="@+id/save_as_pdf"
+                  android:title="@string/save_as_pdf"/>
+
+            <item android:id="@+id/print"
+                  android:title="@string/print"/>
+
+            <item android:id="@+id/add_search_engine"
+                  android:title="@string/contextmenu_add_search_engine"/>
+
+            <item android:id="@+id/set_as_homepage"
+                  android:title="@string/contextmenu_set_as_homepage"/>
+
+        </menu>
+
+    </item>
+
+    <item android:id="@+id/tools"
+          android:title="@string/tools">
+
+        <menu>
+
+            <item android:id="@+id/downloads"
+                  android:title="@string/downloads"/>
+
+            <item android:id="@+id/addons"
+                  android:title="@string/addons"/>
+
+            <item android:id="@+id/logins"
+                  android:title="@string/logins"/>
+
+            <item android:id="@+id/new_guest_session"
+                  android:visible="false"
+                  android:title="@string/new_guest_session"/>
+
+            <item android:id="@+id/exit_guest_session"
+                  android:visible="false"
+                  android:title="@string/exit_guest_session"/>
+
+        </menu>
+
+    </item>
+
+    <item android:id="@+id/char_encoding"
+          android:visible="false"
+          android:title="@string/char_encoding"/>
+
+    <item android:id="@+id/settings"
+          android:title="@string/settings" />
+
+    <item android:id="@+id/help"
+          android:title="@string/help_menu" />
+
+</menu>
--- a/mobile/android/base/java/org/mozilla/gecko/delegates/BookmarkStateChangeDelegate.java
+++ b/mobile/android/base/java/org/mozilla/gecko/delegates/BookmarkStateChangeDelegate.java
@@ -11,16 +11,17 @@ import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.support.design.widget.Snackbar;
 import android.support.v4.content.ContextCompat;
 import android.view.View;
 import android.widget.ListView;
 
 import org.mozilla.gecko.AboutPages;
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.SnackbarBuilder;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
 import org.mozilla.gecko.Telemetry;
@@ -187,21 +188,28 @@ public class BookmarkStateChangeDelegate
                                 GeckoApplication.createShortcut(title, url);
                             }
                         });
                     }
                 }
             }
         });
 
-        final PromptListItem[] items = new PromptListItem[2];
-        items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark));
-        items[1] = new PromptListItem(res.getString(R.string.contextmenu_add_to_launcher));
+        if (AppConstants.Versions.feature26Plus) {
+            final PromptListItem[] items = new PromptListItem[1];
+            items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark));
+            ps.show("", "", items, ListView.CHOICE_MODE_NONE);
+        } else {
+            final PromptListItem[] items = new PromptListItem[2];
+            items[0] = new PromptListItem(res.getString(R.string.contextmenu_edit_bookmark));
+            items[1] = new PromptListItem(res.getString(R.string.contextmenu_add_to_launcher));
+            ps.show("", "", items, ListView.CHOICE_MODE_NONE);
+        }
 
-        ps.show("", "", items, ListView.CHOICE_MODE_NONE);
+
     }
 
     private void showReaderModeBookmarkAddedSnackbar() {
         final BrowserApp browserApp = getBrowserApp();
         if (browserApp == null) {
             return;
         }
 
--- a/mobile/android/base/java/org/mozilla/gecko/promotion/AddToHomeScreenPromotion.java
+++ b/mobile/android/base/java/org/mozilla/gecko/promotion/AddToHomeScreenPromotion.java
@@ -8,16 +8,17 @@ package org.mozilla.gecko.promotion;
 import android.app.Activity;
 import android.content.Context;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.support.annotation.CallSuper;
 import android.support.annotation.Nullable;
 import android.util.Log;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.switchboard.SwitchBoard;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.BrowserApp;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.Tab;
@@ -131,16 +132,21 @@ public class AddToHomeScreenPromotion ex
         }
 
         if (isTabsTrayVisible()) {
             // We only want to show this prompt if this tab is in the foreground and not on top
             // of the tabs tray.
             return;
         }
 
+        // Temporary remove add to home screen
+        if (AppConstants.Versions.feature26Plus) {
+            return;
+        }
+
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 maybeShowPromotionForUrl(tab.getURL(), tab.getTitle());
             }
         });
     }
 
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoScreenOrientation.java
@@ -364,21 +364,23 @@ public class GeckoScreenOrientation {
      *
      * @param aScreenOrientation
      *        Gecko screen orientation.
      * @return Android ActivityInfo orientation.
      */
     public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) {
         switch (aScreenOrientation) {
             case PORTRAIT:
+                return ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
             case PORTRAIT_PRIMARY:
                 return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
             case PORTRAIT_SECONDARY:
                 return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
             case LANDSCAPE:
+                return ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
             case LANDSCAPE_PRIMARY:
                 return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
             case LANDSCAPE_SECONDARY:
                 return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
             case DEFAULT:
             case NONE:
                 return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
             default:
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/RepositorySession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/RepositorySession.java
@@ -298,31 +298,59 @@ public abstract class RepositorySession 
       this.status = to;
       return;
     }
     Logger.warn(LOG_TAG, "Wanted to transition from " + from + " but in state " + this.status);
     throw new InvalidSessionTransitionException(null);
   }
 
   /**
+   * Indicate if the specified records should be reconciled.
+   *
+   * @param remoteRecord
+   *        The record retrieved from upstream, already adjusted for clock skew.
+   * @param localRecord
+   *        The record retrieved from local storage.
+   *
+   * @return
+   *        true if the records should be passed to shouldReconcileRecords for
+   *        the actual resolution, false otherwise.
+   */
+  public boolean shouldReconcileRecords(final Record remoteRecord,
+                                        final Record localRecord) {
+    Logger.debug(LOG_TAG, "Checking if we should reconcile remote " + remoteRecord.guid + " against local " + localRecord.guid);
+    if (localRecord.equalPayloads(remoteRecord)) {
+      if (remoteRecord.lastModified > localRecord.lastModified) {
+        Logger.debug(LOG_TAG, "Records are equal (remote is newer). No record application needed.");
+        return false;
+      }
+      // Local wins.
+      Logger.debug(LOG_TAG, "Records are equal (local is newer). No record application needed.");
+      return false;
+    }
+    return true;
+  }
+
+  /**
    * Produce a record that is some combination of the remote and local records
    * provided.
    *
    * The returned record must be produced without mutating either remoteRecord
    * or localRecord. It is acceptable to return either remoteRecord or localRecord
    * if no modifications are to be propagated.
    *
    * The returned record *should* have the local androidID and the remote GUID,
    * and some optional merge of data from the two records.
    *
-   * This method can be called with records that are identical, or differ in
-   * any regard.
+   * This method should only be called when shouldReconcileRecords returns true
+   * for the specified records.
    *
    * This method will not be called if:
    *
+   * * shouldReconcileRecords() returns false
    * * either record is marked as deleted, or
    * * there is no local mapping for a new remote record.
    *
    * Otherwise, it will be called precisely once.
    *
    * Side-effects (e.g., for transactional storage) can be hooked in here.
    *
    * @param remoteRecord
@@ -330,34 +358,24 @@ public abstract class RepositorySession 
    * @param localRecord
    *        The record retrieved from local storage.
    * @param lastRemoteRetrieval
    *        The timestamp of the last retrieved set of remote records, adjusted for
    *        clock skew.
    * @param lastLocalRetrieval
    *        The timestamp of the last retrieved set of local records.
    * @return
-   *        A Record instance to apply, or null to apply nothing.
+   *        A Record instance to apply.
    */
   public Record reconcileRecords(final Record remoteRecord,
-                                    final Record localRecord,
-                                    final long lastRemoteRetrieval,
-                                    final long lastLocalRetrieval) {
+                                 final Record localRecord,
+                                 final long lastRemoteRetrieval,
+                                 final long lastLocalRetrieval) {
     Logger.debug(LOG_TAG, "Reconciling remote " + remoteRecord.guid + " against local " + localRecord.guid);
 
-    if (localRecord.equalPayloads(remoteRecord)) {
-      if (remoteRecord.lastModified > localRecord.lastModified) {
-        Logger.debug(LOG_TAG, "Records are equal. No record application needed.");
-        return null;
-      }
-
-      // Local wins.
-      return null;
-    }
-
     // TODO: Decide what to do based on:
     // * Which of the two records is modified;
     // * Whether they are equal or congruent;
     // * The modified times of each record (interpreted through the lens of clock skew);
     // * Whether localVersion==syncVersion or localVersion>syncVersion for localRecord
     // * ...
     boolean localIsMoreRecent = localRecord.lastModified > remoteRecord.lastModified;
     Logger.debug(LOG_TAG, "Local record is more recent? " + localIsMoreRecent);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/PasswordsRepositorySession.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/PasswordsRepositorySession.java
@@ -314,22 +314,23 @@ public class PasswordsRepositorySession 
         trace("Incoming record " + remoteRecord.guid + " dupes to local record " + existingRecord.guid);
         Logger.debug(LOG_TAG, "remote " + remoteRecord.guid + " dupes to " + existingRecord.guid);
 
         if (existingRecord.deleted && existingRecord.lastModified > remoteRecord.lastModified) {
           Logger.debug(LOG_TAG, "Local deletion is newer, not storing remote record.");
           return;
         }
 
-        Record toStore = reconcileRecords(remoteRecord, existingRecord, lastRemoteRetrieval, lastLocalRetrieval);
-        if (toStore == null) {
-          Logger.debug(LOG_TAG, "Reconciling returned null. Not inserting a record.");
+        if (!shouldReconcileRecords(remoteRecord, existingRecord)) {
+          Logger.debug(LOG_TAG, "shouldReconcileRecords returned false. Not inserting a record.");
           return;
         }
 
+        Record toStore = reconcileRecords(remoteRecord, existingRecord, lastRemoteRetrieval, lastLocalRetrieval);
+
         // TODO: pass in timestamps?
         Logger.debug(LOG_TAG, "Replacing " + existingRecord.guid + " with record " + toStore.guid);
         Record replaced = null;
         try {
           replaced = replace(existingRecord, toStore);
         } catch (RemoteException e) {
           Logger.debug(LOG_TAG, "Record replace caused a RemoteException.");
           storeDelegate.onRecordStoreFailed(e, record.guid);
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/SessionHelper.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/repositories/android/SessionHelper.java
@@ -173,16 +173,21 @@ import org.mozilla.gecko.sync.repositori
         }
     }
 
     void finish() {
         // TODO are we paranoid that a reference might persist for too long once we're done with it?
         recordToGuid = null;
     }
 
+    private boolean shouldReconcileRecords(final Record remoteRecord,
+                                           final Record localRecord) {
+        return session.shouldReconcileRecords(remoteRecord, localRecord);
+    }
+
     private void putRecordToGuidMap(String recordString, String guid)
             throws NoGuidForIdException, NullCursorException, ParentNotFoundException {
         if (recordString == null) {
             return;
         }
 
         if (recordToGuid == null) {
             createRecordToGuidMap();
@@ -292,24 +297,22 @@ import org.mozilla.gecko.sync.repositori
                         }
 
                         // We found a local dupe.
                         trace("Incoming record " + record.guid + " dupes to local record " + existingRecord.guid);
 
                         // Populate more expensive fields prior to reconciling.
                         existingRecord = transformRecord(existingRecord);
 
+                        if (!shouldReconcileRecords(record, existingRecord)) {
+                            Logger.debug(LOG_TAG, "shouldReconcileRecords returned false. Not processing a record.");
+                            break;
+                        }
                         // Implementation is expected to decide if it's necessary to "track" the record.
                         Record toStore = reconcileRecords(record, existingRecord, lastRemoteRetrieval, lastLocalRetrieval);
-
-                        if (toStore == null) {
-                            Logger.debug(LOG_TAG, "Reconciling returned null. Not inserting a record.");
-                            break;
-                        }
-
                         Logger.debug(LOG_TAG, "Reconcile attempt #" + reconcileAttempt);
 
                         // This section of code will only run if the incoming record is not
                         // marked as deleted, so we never want to just drop ours from the database:
                         // we need to upload it later.
                         // Implementations are expected to ensure this happens.
                         boolean replaceSuccessful = doReplaceRecord(toStore, existingRecord, storeDelegate);
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -368,16 +368,17 @@ pref("media.wmf.enabled", true);
 pref("media.wmf.decoder.thread-count", -1);
 pref("media.wmf.low-latency.enabled", false);
 pref("media.wmf.skip-blacklist", false);
 pref("media.wmf.vp9.enabled", true);
 pref("media.wmf.allow-unsupported-resolutions", false);
 pref("media.windows-media-foundation.allow-d3d11-dxva", true);
 pref("media.wmf.disable-d3d11-for-dlls", "igd11dxva64.dll: 20.19.15.4463, 20.19.15.4454, 20.19.15.4444, 20.19.15.4416, 20.19.15.4404, 20.19.15.4390, 20.19.15.4380, 20.19.15.4377, 20.19.15.4364, 20.19.15.4360, 20.19.15.4352, 20.19.15.4331, 20.19.15.4326, 20.19.15.4300; igd10iumd32.dll: 20.19.15.4444, 20.19.15.4424, 20.19.15.4409, 20.19.15.4390, 20.19.15.4380, 20.19.15.4360, 10.18.10.4358, 20.19.15.4331, 20.19.15.4312, 20.19.15.4300, 10.18.15.4281, 10.18.15.4279, 10.18.10.4276, 10.18.15.4268, 10.18.15.4256, 10.18.10.4252, 10.18.15.4248, 10.18.14.4112, 10.18.10.3958, 10.18.10.3496, 10.18.10.3431, 10.18.10.3412, 10.18.10.3355, 9.18.10.3234, 9.18.10.3071, 9.18.10.3055, 9.18.10.3006; igd10umd32.dll: 9.17.10.4229, 9.17.10.3040, 9.17.10.2857, 8.15.10.2274, 8.15.10.2272, 8.15.10.2246, 8.15.10.1840, 8.15.10.1808; igd10umd64.dll: 9.17.10.4229, 9.17.10.2857, 10.18.10.3496; isonyvideoprocessor.dll: 4.1.2247.8090, 4.1.2153.6200; tosqep.dll: 1.2.15.526, 1.1.12.201, 1.0.11.318, 1.0.11.215, 1.0.10.1224; tosqep64.dll: 1.1.12.201, 1.0.11.215; nvwgf2um.dll: 22.21.13.8253, 22.21.13.8233, 22.21.13.8205, 22.21.13.8189, 22.21.13.8178, 22.21.13.8165, 21.21.13.7892, 21.21.13.7878, 21.21.13.7866, 21.21.13.7849, 21.21.13.7654, 21.21.13.7653, 21.21.13.7633, 21.21.13.7619, 21.21.13.7563, 21.21.13.7306, 21.21.13.7290, 21.21.13.7270, 21.21.13.7254, 21.21.13.6939, 21.21.13.6926, 21.21.13.6909, 21.21.13.4201, 21.21.13.4200, 10.18.13.6881, 10.18.13.6839, 10.18.13.6510, 10.18.13.6472, 10.18.13.6143, 10.18.13.5946, 10.18.13.5923, 10.18.13.5921, 10.18.13.5891, 10.18.13.5887, 10.18.13.5582, 10.18.13.5445, 10.18.13.5382, 10.18.13.5362, 9.18.13.4788, 9.18.13.4752, 9.18.13.4725, 9.18.13.4709, 9.18.13.4195, 9.18.13.4192, 9.18.13.4144, 9.18.13.4052, 9.18.13.3788, 9.18.13.3523, 9.18.13.3235, 9.18.13.3165, 9.18.13.2723, 9.18.13.2702, 9.18.13.1422, 9.18.13.1407, 9.18.13.1106, 9.18.13.546; atidxx32.dll: 21.19.151.3, 21.19.142.257, 21.19.137.514, 21.19.137.1, 21.19.134.1, 21.19.128.7, 21.19.128.4, 20.19.0.32837, 20.19.0.32832, 8.17.10.682, 8.17.10.671, 8.17.10.661, 8.17.10.648, 8.17.10.644, 8.17.10.625, 8.17.10.605, 8.17.10.581, 8.17.10.569, 8.17.10.560, 8.17.10.545, 8.17.10.539, 8.17.10.531, 8.17.10.525, 8.17.10.520, 8.17.10.519, 8.17.10.514, 8.17.10.511, 8.17.10.494, 8.17.10.489, 8.17.10.483, 8.17.10.453, 8.17.10.451, 8.17.10.441, 8.17.10.436, 8.17.10.432, 8.17.10.425, 8.17.10.418, 8.17.10.414, 8.17.10.401, 8.17.10.395, 8.17.10.385, 8.17.10.378, 8.17.10.362, 8.17.10.355, 8.17.10.342, 8.17.10.331, 8.17.10.318, 8.17.10.310, 8.17.10.286, 8.17.10.269, 8.17.10.261, 8.17.10.247, 8.17.10.240, 8.15.10.212; atidxx64.dll: 21.19.151.3, 21.19.142.257, 21.19.137.514, 21.19.137.1, 21.19.134.1, 21.19.128.7, 21.19.128.4, 20.19.0.32832, 8.17.10.682, 8.17.10.661, 8.17.10.644, 8.17.10.625; nvumdshim.dll: 10.18.13.6822");
 pref("media.wmf.disable-d3d9-for-dlls", "igdumd64.dll: 8.15.10.2189, 8.15.10.2119, 8.15.10.2104, 8.15.10.2102, 8.771.1.0; atiumd64.dll: 7.14.10.833, 7.14.10.867, 7.14.10.885, 7.14.10.903, 7.14.10.911, 8.14.10.768, 9.14.10.1001, 9.14.10.1017, 9.14.10.1080, 9.14.10.1128, 9.14.10.1162, 9.14.10.1171, 9.14.10.1183, 9.14.10.1197, 9.14.10.945, 9.14.10.972, 9.14.10.984, 9.14.10.996");
+pref("media.wmf.deblacklisting-for-telemetry-in-gpu-process", true);
 #endif
 #if defined(MOZ_FFMPEG)
 #if defined(XP_MACOSX)
 pref("media.ffmpeg.enabled", false);
 #else
 pref("media.ffmpeg.enabled", true);
 #endif
 pref("media.libavcodec.allow-obsolete", false);
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -295,17 +295,17 @@ printf_stderr(const char *fmt, ...)
   vfprintf(fp, fmt, args);
   va_end(args);
 
   fclose(fp);
 }
 
 
 #ifdef _M_IX86
-typedef void (__fastcall* BaseThreadInitThunk_func)(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
+typedef MOZ_NORETURN_PTR void (__fastcall* BaseThreadInitThunk_func)(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
 static BaseThreadInitThunk_func stub_BaseThreadInitThunk = nullptr;
 #endif
 
 typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
 static LdrLoadDll_func stub_LdrLoadDll;
 
 #ifdef _M_AMD64
 typedef decltype(RtlInstallFunctionTableCallback)* RtlInstallFunctionTableCallback_func;
@@ -515,17 +515,17 @@ DllBlockSet::Write(HANDLE file)
 
   // Because this method is called after a crash occurs, and uses heap memory,
   // protect this entire block with a structured exception handler.
   MOZ_SEH_TRY {
     DWORD nBytes;
     for (DllBlockSet* b = gFirst; b; b = b->mNext) {
       // write name[,v.v.v.v];
       WriteFile(file, b->mName, strlen(b->mName), &nBytes, nullptr);
-      if (b->mVersion != -1) {
+      if (b->mVersion != ALL_VERSIONS) {
         WriteFile(file, ",", 1, &nBytes, nullptr);
         uint16_t parts[4];
         parts[0] = b->mVersion >> 48;
         parts[1] = (b->mVersion >> 32) & 0xFFFF;
         parts[2] = (b->mVersion >> 16) & 0xFFFF;
         parts[3] = b->mVersion & 0xFFFF;
         for (int p = 0; p < 4; ++p) {
           char buf[32];
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_tls_flags.js
@@ -0,0 +1,233 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+"use strict";
+
+// a fork of test_be_conservative
+
+// Tests that nsIHttpChannelInternal.tlsFlags can be used to set the
+// client max version level. Flags can also be used to set the
+// level of intolerance rollback and to test out an experimental 1.3
+// hello, though they are not tested here.
+
+const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+
+// Get a profile directory and ensure PSM initializes NSS.
+do_get_profile();
+Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
+
+function getCert() {
+  return new Promise((resolve, reject) => {
+    let certService = Cc["@mozilla.org/security/local-cert-service;1"]
+                        .getService(Ci.nsILocalCertService);
+    certService.getOrCreateCert("tlsflags-test", {
+      handleCert: function(c, rv) {
+        if (rv) {
+          reject(rv);
+          return;
+        }
+        resolve(c);
+      }
+    });
+  });
+}
+
+class InputStreamCallback {
+  constructor(output) {
+    this.output = output;
+    this.stopped = false;
+  }
+
+  onInputStreamReady(stream) {
+    do_print("input stream ready");
+    if (this.stopped) {
+      do_print("input stream callback stopped - bailing");
+      return;
+    }
+    let available = 0;
+    try {
+      available = stream.available();
+    } catch (e) {
+      // onInputStreamReady may fire when the stream has been closed.
+      equal(e.result, Cr.NS_BASE_STREAM_CLOSED,
+            "error should be NS_BASE_STREAM_CLOSED");
+    }
+    if (available > 0) {
+      let request = NetUtil.readInputStreamToString(stream, available,
+                                                    { charset: "utf8"});
+      ok(request.startsWith("GET / HTTP/1.1\r\n"),
+         "Should get a simple GET / HTTP/1.1 request");
+      let response = "HTTP/1.1 200 OK\r\n" +
+                     "Content-Length: 2\r\n" +
+                     "Content-Type: text/plain\r\n" +
+                     "\r\nOK";
+      let written = this.output.write(response, response.length);
+      equal(written, response.length,
+            "should have been able to write entire response");
+    }
+    this.output.close();
+    do_print("done with input stream ready");
+  }
+
+  stop() {
+    this.stopped = true;
+    this.output.close();
+  }
+}
+
+class TLSServerSecurityObserver {
+  constructor(input, output, expectedVersion) {
+    this.input = input;
+    this.output = output;
+    this.expectedVersion = expectedVersion;
+    this.callbacks = [];
+    this.stopped = false;
+  }
+
+  onHandshakeDone(socket, status) {
+    do_print("TLS handshake done");
+    do_print(`TLS version used: ${status.tlsVersionUsed}`);
+    do_print(this.expectedVersion);
+    equal(status.tlsVersionUsed, this.expectedVersion, "expected version check");
+    if (this.stopped) {
+      do_print("handshake done callback stopped - bailing");
+      return;
+    }
+
+    let callback = new InputStreamCallback(this.output);
+    this.callbacks.push(callback);
+    this.input.asyncWait(callback, 0, 0, Services.tm.currentThread);
+  }
+
+  stop() {
+    this.stopped = true;
+    this.input.close();
+    this.output.close();
+    this.callbacks.forEach((callback) => {
+      callback.stop();
+    });
+  }
+}
+
+
+function startServer(cert, minServerVersion, maxServerVersion, expectedVersion) {
+  let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"]
+                    .createInstance(Ci.nsITLSServerSocket);
+  tlsServer.init(-1, true, -1);
+  tlsServer.serverCert = cert;
+  tlsServer.setVersionRange(minServerVersion, maxServerVersion);
+  tlsServer.setSessionCache(false);
+  tlsServer.setSessionTickets(false);
+
+  let listener = {
+    securityObservers : [],
+
+    onSocketAccepted: function(socket, transport) {
+      do_print("accepted TLS client connection");
+      let connectionInfo = transport.securityInfo
+          .QueryInterface(Ci.nsITLSServerConnectionInfo);
+      let input = transport.openInputStream(0, 0, 0);
+      let output = transport.openOutputStream(0, 0, 0);
+      let securityObserver = new TLSServerSecurityObserver(input, output, expectedVersion);
+      this.securityObservers.push(securityObserver);
+      connectionInfo.setSecurityObserver(securityObserver);
+    },
+
+    // For some reason we get input stream callback events after we've stopped
+    // listening, so this ensures we just drop those events.
+    onStopListening: function() {
+      do_print("onStopListening");
+      this.securityObservers.forEach((observer) => {
+        observer.stop();
+      });
+    }
+  }
+  tlsServer.asyncListen(listener);
+  return tlsServer;
+}
+
+const hostname = "example.com"
+
+function storeCertOverride(port, cert) {
+  let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
+                              .getService(Ci.nsICertOverrideService);
+  let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
+                     Ci.nsICertOverrideService.ERROR_MISMATCH;
+  certOverrideService.rememberValidityOverride(hostname, port, cert,
+                                               overrideBits, true);
+}
+
+function startClient(port, tlsFlags, expectSuccess) {
+  let req = new XMLHttpRequest();
+  req.open("GET", `https://${hostname}:${port}`);
+  let internalChannel = req.channel.QueryInterface(Ci.nsIHttpChannelInternal);
+  internalChannel.tlsFlags = tlsFlags;
+  return new Promise((resolve, reject) => {
+    req.onload = () => {
+      ok(expectSuccess,
+         `should ${expectSuccess ? "" : "not "}have gotten load event`);
+      equal(req.responseText, "OK", "response text should be 'OK'");
+      resolve();
+    };
+    req.onerror = () => {
+      ok(!expectSuccess,
+         `should ${!expectSuccess ? "" : "not "}have gotten an error`);
+      resolve();
+    };
+
+    req.send();
+  });
+}
+
+add_task(async function() {
+  Services.prefs.setIntPref("security.tls.version.max", 4);
+  Services.prefs.setCharPref("network.dns.localDomains", hostname);
+  let cert = await getCert();
+
+  // server that accepts 1.1->1.3 and a client max 1.3. expect 1.3
+  do_print("TEST 1");
+  let server = startServer(cert,
+                           Ci.nsITLSClientStatus.TLS_VERSION_1_1,
+                           Ci.nsITLSClientStatus.TLS_VERSION_1_3,
+                           Ci.nsITLSClientStatus.TLS_VERSION_1_3);
+  storeCertOverride(server.port, cert);
+  await startClient(server.port, 4, true /*should succeed*/);
+  server.close();
+
+  // server that accepts 1.1->1.3 and a client max 1.1. expect 1.1
+  do_print("TEST 2");
+  server = startServer(cert,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_1,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_3,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_1);
+  storeCertOverride(server.port, cert);
+  await startClient(server.port, 2, true);
+  server.close();
+
+  // server that accepts 1.2->1.2 and a client max 1.3. expect 1.2
+  do_print("TEST 3");
+  server = startServer(cert,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_2);
+  storeCertOverride(server.port, cert);
+  await startClient(server.port, 4, true);
+  server.close();
+
+  // server that accepts 1.2->1.2 and a client max 1.1. expect fail
+  do_print("TEST 4");
+  server = startServer(cert,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_2,
+                       Ci.nsITLSClientStatus.TLS_VERSION_1_2, 0);
+  storeCertOverride(server.port, cert);
+  await startClient(server.port, 2, false);
+
+  server.close();
+});
+
+do_register_cleanup(function() {
+  Services.prefs.clearUserPref("security.tls.version.max");
+  Services.prefs.clearUserPref("network.dns.localDomains");
+});
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -392,8 +392,9 @@ skip-if = os == "android"
 [test_race_cache_with_network.js]
 [test_channel_priority.js]
 [test_bug1312774_http1.js]
 [test_1351443-missing-NewChannel2.js]
 [test_bug1312782_http1.js]
 [test_bug1355539_http1.js]
 [test_bug1378385_http1.js]
 [test_tls_flags_separate_connections.js]
+[test_tls_flags.js]
--- a/python/mozbuild/mozbuild/action/langpack_manifest.py
+++ b/python/mozbuild/mozbuild/action/langpack_manifest.py
@@ -13,18 +13,16 @@ from __future__ import absolute_import
 import argparse
 import sys
 import os
 import json
 import io
 from mozpack.chrome.manifest import (
     Manifest,
     ManifestLocale,
-    ManifestOverride,
-    ManifestResource,
     parse_manifest,
 )
 from mozbuild.preprocessor import Preprocessor
 
 
 def write_file(path, content):
     with io.open(path, 'w', encoding='utf-8') as out:
         out.write(content + '\n')
@@ -76,20 +74,94 @@ def convert_contributors(str):
     str = str.replace('<em:contributor>', '')
     tokens = str.split('</em:contributor>')
     tokens = map(lambda t: t.strip(), tokens)
     tokens = filter(lambda t: t != '', tokens)
     return ', '.join(tokens)
 
 
 ###
+# Build the manifest author string based on the author string
+# and optionally adding the list of contributors, if provided.
+#
+# Args:
+#    author (str)       - a string with the name of the author
+#    contributors (str) - RDF based list of contributors from a chrome manifest
+#
+# Returns:
+#    (str) - a string to be placed in the author field of the manifest.json
+#
+# Example:
+#    s = build_author_string(
+#    'Aviary.pl',
+#    '
+#        <em:contributor>Marek Wawoczny</em:contributor>
+#        <em:contributor>Marek Stepien</em:contributor>
+#    ')
+#    s == 'Aviary.pl (contributors: Marek Wawoczny, Marek Stepien)'
+###
+def build_author_string(author, contributors):
+    contrib = convert_contributors(contributors)
+    if len(contrib) == 0:
+        return author
+    return '{0} (contributors: {1})'.format(author, contrib)
+
+
+##
+# Converts the list of chrome manifest entry flags to the list of platforms
+# for the langpack manifest.
+#
+# The list of result platforms is taken from AppConstants.platform.
+#
+# Args:
+#    flags (FlagList) - a list of Chrome Manifest entry flags
+#
+# Returns:
+#    (list) - a list of platform the entry applies to
+#
+# Example:
+#    str(flags) == "os==MacOS os==Windows"
+#    platforms = convert_entry_flags_to_platform_codes(flags)
+#    platforms == ['mac', 'win']
+#
+# The method supports only `os` flag name and equality operator.
+# It will throw if tried with other flags or operators.
+###
+def convert_entry_flags_to_platform_codes(flags):
+    if not flags:
+        return None
+
+    ret = []
+    for key in flags:
+        if key != 'os':
+            raise Exception('Unknown flag name')
+
+        for value in flags[key].values:
+            if value[0] != '==':
+                raise Exception('Inequality flag cannot be converted')
+
+            if value[1] == 'Android':
+                ret.append('android')
+            elif value[1] == 'LikeUnix':
+                ret.append('linux')
+            elif value[1] == 'Darwin':
+                ret.append('macosx')
+            elif value[1] == 'WINNT':
+                ret.append('win')
+            else:
+                raise Exception('Unknown flag value {0}'.format(value[1]))
+
+    return ret
+
+
+###
 # Recursively parse a chrome manifest file appending new entries
 # to the result list
 #
-# The function can handle three entry types: 'locale', 'override' and 'resource'
+# The function can handle two entry types: 'locale' and 'manifest'
 #
 # Args:
 #    path           (str)  - a path to a chrome manifest
 #    base_path      (str)  - a path to the base directory all chrome registry
 #                            entries will be relative to
 #    chrome_entries (list) - a list to which entries will be appended to
 #
 # Example:
@@ -97,22 +169,24 @@ def convert_contributors(str):
 #    chrome_entries = {}
 #    parse_manifest('./chrome.manifest', './', chrome_entries)
 #
 #    chrome_entries == [
 #        {
 #            'type': 'locale',
 #            'alias': 'devtools',
 #            'locale': 'pl',
+#            'platforms': null,
 #            'path': 'chrome/pl/locale/pl/devtools/'
 #        },
 #        {
 #            'type': 'locale',
 #            'alias': 'autoconfig',
 #            'locale': 'pl',
+#            'platforms': ['win', 'mac'],
 #            'path': 'chrome/pl/locale/pl/autoconfig/'
 #        },
 #    ]
 ###
 def parse_chrome_manifest(path, base_path, chrome_entries):
     for entry in parse_manifest(None, path):
         if isinstance(entry, Manifest):
             parse_chrome_manifest(
@@ -120,38 +194,27 @@ def parse_chrome_manifest(path, base_pat
                 base_path,
                 chrome_entries
             )
         elif isinstance(entry, ManifestLocale):
             chrome_entries.append({
                 'type': 'locale',
                 'alias': entry.name,
                 'locale': entry.id,
+                'platforms': convert_entry_flags_to_platform_codes(entry.flags),
                 'path': os.path.join(
                     os.path.relpath(
                         os.path.dirname(path),
                         base_path
                     ),
                     entry.relpath
                 )
             })
-        elif isinstance(entry, ManifestOverride):
-            chrome_entries.append({
-                'type': 'override',
-                'real-path': entry.overloaded,
-                'overlay-path': entry.overload
-            })
-        elif isinstance(entry, ManifestResource):
-            chrome_entries.append({
-                'type': 'resource',
-                'alias': entry.name,
-                'path': entry.target
-            })
         else:
-            raise Exception('Unknown type %s' % entry[0])
+            raise Exception('Unknown type {0}'.format(entry.name))
 
 
 ###
 # Generates a new web manifest dict with values specific for a language pack.
 #
 # Args:
 #    locstr         (str)  - A string with a comma separated list of locales
 #                            for which resources are embedded in the
@@ -163,81 +226,99 @@ def parse_chrome_manifest(path, base_pat
 #
 # Returns:
 #    (dict) - a web manifest
 #
 # Example:
 #    manifest = create_webmanifest(
 #      ['pl'],
 #      '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}',
-#      '55.0a1',
+#      '57.0',
 #      {'MOZ_LANG_TITLE': 'Polski'},
 #      chrome_entries
 #    )
 #    manifest == {
-#        'languages': ['pl'],
+#        'languages': {
+#            'pl': {
+#                'version': '201709121481',
+#                'resources': None,
+#                'chrome_resources': {
+#                    'alert': 'chrome/pl/locale/pl/alert/',
+#                    'branding': 'browser/chrome/pl/locale/global/',
+#                    'global-platform': {
+#                      'macosx': 'chrome/pl/locale/pl/global-platform/mac/',
+#                      'win': 'chrome/pl/locale/pl/global-platform/win/',
+#                      'linux': 'chrome/pl/locale/pl/global-platform/unix/',
+#                      'android': 'chrome/pl/locale/pl/global-platform/unix/',
+#                    },
+#                    'forms': 'browser/chrome/pl/locale/forms/',
+#                    ...
+#                }
+#            }
+#        },
 #        'applications': {
 #            'gecko':  {
-#                'strict_min_version': '55.0a1',
-#                'id': '',
+#                'strict_min_version': '57.0',
+#                'strict_max_version': '57.0.*',
+#                'id': 'langpack-pl@mozilla.org',
 #            }
 #        },
-#        'version': '55.0a1',
+#        'version': '57.0',
 #        'name': 'Polski Language Pack',
 #        ...
 #    }
 ###
 def create_webmanifest(locstr, appver, defines, chrome_entries):
     locales = map(lambda loc: loc.strip(), locstr.split(','))
     main_locale = locales[0]
 
-    contributors = convert_contributors(defines['MOZ_LANGPACK_CONTRIBUTORS'])
+    author = build_author_string(
+        defines['MOZ_LANGPACK_CREATOR'],
+        defines['MOZ_LANGPACK_CONTRIBUTORS']
+    )
 
     manifest = {
-        'langpack-id': main_locale,
+        'langpack_id': main_locale,
         'manifest_version': 2,
         'applications': {
             'gecko': {
-                'id': "langpack-" + main_locale + "@mozilla.org",
-                'strict_min_version': appver
+                'id': 'langpack-{0}@firefox.mozilla.org'.format(main_locale),
+                'strict_min_version': appver,
+                'strict_max_version': '{0}.*'.format(appver)
             }
         },
-        'name': defines['MOZ_LANG_TITLE'] + ' Language Pack',
-        'description': 'Language pack for Firefox for ' + main_locale,
+        'name': '{0} Language Pack'.format(defines['MOZ_LANG_TITLE']),
+        'description': 'Language pack for Firefox for {0}'.format(main_locale),
         'version': appver,
-        'languages': locales,
-        'author': '%s (contributors: %s)' % (defines['MOZ_LANGPACK_CREATOR'], contributors),
-        'chrome_entries': [
-        ]
+        'languages': {},
+        'author': author
     }
 
+    cr = {}
     for entry in chrome_entries:
-        line = ''
         if entry['type'] == 'locale':
-            line = '%s %s %s %s' % (
-                entry['type'],
-                entry['alias'],
-                entry['locale'],
-                entry['path']
-            )
-        elif entry['type'] == 'override':
-            line = '%s %s %s' % (
-                entry['type'],
-                entry['real-path'],
-                entry['overlay-path']
-            )
-        elif entry['type'] == 'resource':
-            line = '%s %s %s' % (
-                entry['type'],
-                entry['alias'],
-                entry['path']
-            )
+            platforms = entry['platforms']
+            if platforms:
+                if entry['alias'] not in cr:
+                    cr[entry['alias']] = {}
+                for platform in platforms:
+                    cr[entry['alias']][platform] = entry['path']
+            else:
+                assert entry['alias'] not in cr
+                cr[entry['alias']] = entry['path']
         else:
-            raise Exception('Unknown type %s' % entry['type'])
-        manifest['chrome_entries'].append(line)
+            raise Exception('Unknown type {0}'.format(entry['type']))
+
+    for loc in locales:
+        manifest['languages'][loc] = {
+            'version': appver,
+            'resources': None,
+            'chrome_resources': cr
+        }
+
     return json.dumps(manifest, indent=2, ensure_ascii=False, encoding='utf8')
 
 
 def main(args):
     parser = argparse.ArgumentParser()
     parser.add_argument('--locales',
                         help='List of language codes provided by the langpack')
     parser.add_argument('--appver',
--- a/security/manager/ssl/SharedSSLState.cpp
+++ b/security/manager/ssl/SharedSSLState.cpp
@@ -112,25 +112,28 @@ PrivateBrowsingObserver::Observe(nsISupp
                                  const char16_t *aData)
 {
   if (!nsCRT::strcmp(aTopic, "last-pb-context-exited")) {
     mOwner->ResetStoredData();
   }
   return NS_OK;
 }
 
-SharedSSLState::SharedSSLState()
-: mClientAuthRemember(new nsClientAuthRememberService)
+SharedSSLState::SharedSSLState(uint32_t aTlsFlags)
+: mIOLayerHelpers(aTlsFlags)
 , mMutex("SharedSSLState::mMutex")
 , mSocketCreated(false)
 , mOCSPStaplingEnabled(false)
 , mOCSPMustStapleEnabled(false)
 {
   mIOLayerHelpers.Init();
-  mClientAuthRemember->Init();
+  if (!aTlsFlags) { // the per socket flags don't need memory
+    mClientAuthRemember = new nsClientAuthRememberService();
+    mClientAuthRemember->Init();
+  }
 }
 
 SharedSSLState::~SharedSSLState()
 {
 }
 
 void
 SharedSSLState::NotePrivateBrowsingStatus()
@@ -139,16 +142,19 @@ SharedSSLState::NotePrivateBrowsingStatu
   mObserver = new PrivateBrowsingObserver(this);
   nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
   obsSvc->AddObserver(mObserver, "last-pb-context-exited", false);
 }
 
 void
 SharedSSLState::ResetStoredData()
 {
+  if (!mClientAuthRemember) {
+    return;
+  }
   MOZ_ASSERT(NS_IsMainThread(), "Not on main thread");
   mClientAuthRemember->ClearRememberedDecisions();
   mIOLayerHelpers.clearStoredData();
 }
 
 void
 SharedSSLState::NoteSocketCreated()
 {
--- a/security/manager/ssl/SharedSSLState.h
+++ b/security/manager/ssl/SharedSSLState.h
@@ -14,17 +14,17 @@ class nsClientAuthRememberService;
 class nsIObserver;
 
 namespace mozilla {
 namespace psm {
 
 class SharedSSLState {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedSSLState)
-  SharedSSLState();
+  explicit SharedSSLState(uint32_t aTlsFlags = 0);
 
   static void GlobalInit();
   static void GlobalCleanup();
 
   nsClientAuthRememberService* GetClientAuthRememberService() {
     return mClientAuthRemember;
   }
 
--- a/security/manager/ssl/nsClientAuthRemember.cpp
+++ b/security/manager/ssl/nsClientAuthRemember.cpp
@@ -80,20 +80,26 @@ void nsClientAuthRememberService::ClearR
   ReentrantMonitorAutoEnter lock(monitor);
   RemoveAllFromMemory();
 }
 
 void nsClientAuthRememberService::ClearAllRememberedDecisions()
 {
   RefPtr<nsClientAuthRememberService> svc =
     PublicSSLState()->GetClientAuthRememberService();
-  svc->ClearRememberedDecisions();
+  MOZ_ASSERT(svc);
+  if (svc) {
+    svc->ClearRememberedDecisions();
+  }
 
   svc = PrivateSSLState()->GetClientAuthRememberService();
-  svc->ClearRememberedDecisions();
+  MOZ_ASSERT(svc);
+  if (svc) {
+    svc->ClearRememberedDecisions();
+  }
 }
 
 void
 nsClientAuthRememberService::RemoveAllFromMemory()
 {
   mSettingsTable.Clear();
 }
 
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -39,32 +39,71 @@
 #include "pkix/pkixtypes.h"
 #include "prmem.h"
 #include "prnetdb.h"
 #include "secder.h"
 #include "secerr.h"
 #include "ssl.h"
 #include "sslerr.h"
 #include "sslproto.h"
+#include "sslexp.h"
 
 using namespace mozilla;
 using namespace mozilla::psm;
 
 //#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
                             //reports when doing SSL read/write
 
 //#define DUMP_BUFFER  //Enable this define along with
                        //DEBUG_SSL_VERBOSE to dump SSL
                        //read/write buffer to a log.
                        //Uses PR_LOG except on Mac where
                        //we always write out to our own
                        //file.
 
 namespace {
 
+// The NSSSocketInfo tls flags are meant to be opaque to most calling applications
+// but provide a mechanism for direct TLS manipulation when experimenting with new
+// features in the scope of a single socket. They do not create a persistent ABI.
+//
+// Use of these flags creates a new 'sharedSSLState' so existing states for intolerance
+// are not carried to sockets that use these flags (and intolerance they discover
+// does not impact other normal sockets not using the flags.)
+//
+// Their current definitions are:
+//
+// bits 0-2 (mask 0x07) specify the max tls version
+//          0 means no override 1->4 are 1.0, 1.1, 1.2, 1.3, 4->7 unused
+// bits 3-5 (mask 0x38) specify the tls fallback limit
+//          0 means no override, values 1->4 match prefs
+// bit    6 (mask 0x40) specifies use of SSL_AltServerHelloType on handshake
+
+enum {
+  kTLSProviderFlagMaxVersion10   = 0x01,
+  kTLSProviderFlagMaxVersion11   = 0x02,
+  kTLSProviderFlagMaxVersion12   = 0x03,
+  kTLSProviderFlagMaxVersion13   = 0x04,
+};
+
+static uint32_t getTLSProviderFlagMaxVersion(uint32_t flags)
+{
+  return (flags & 0x07);
+}
+
+static uint32_t getTLSProviderFlagFallbackLimit(uint32_t flags)
+{
+  return (flags & 0x38) >> 3;
+}
+
+static bool getTLSProviderFlagAltServerHello(uint32_t flags)
+{
+  return (flags & 0x40);
+}
+
 #define MAX_ALPN_LENGTH 255
 
 void
 getSiteKey(const nsACString& hostName, uint16_t port,
            /*out*/ nsACString& key)
 {
   key = hostName;
   key.AppendASCII(":");
@@ -658,16 +697,22 @@ nsNSSSocketInfo::SetCertVerificationResu
 }
 
 SharedSSLState&
 nsNSSSocketInfo::SharedState()
 {
   return mSharedState;
 }
 
+void
+nsNSSSocketInfo::SetSharedOwningReference(SharedSSLState* aRef)
+{
+  mOwningSharedRef = aRef;
+}
+
 void nsSSLIOLayerHelpers::Cleanup()
 {
   MutexAutoLock lock(mutex);
   mTLSIntoleranceInfo.Clear();
   mInsecureFallbackSites.Clear();
 }
 
 static void
@@ -1352,21 +1397,22 @@ nsSSLIOLayerPoll(PRFileDesc* fd, int16_t
   // it reaches any point that would be unsafe to send/receive something before
   // cert validation is complete.
   int16_t result = fd->lower->methods->poll(fd->lower, in_flags, out_flags);
   MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
           ("[%p] poll SSL socket returned %d\n", (void*) fd, (int) result));
   return result;
 }
 
-nsSSLIOLayerHelpers::nsSSLIOLayerHelpers()
+nsSSLIOLayerHelpers::nsSSLIOLayerHelpers(uint32_t aTlsFlags)
   : mTreatUnsafeNegotiationAsBroken(false)
   , mTLSIntoleranceInfo()
   , mVersionFallbackLimit(SSL_LIBRARY_VERSION_TLS_1_0)
   , mutex("nsSSLIOLayerHelpers.mutex")
+  , mTlsFlags(aTlsFlags)
 {
 }
 
 static int
 _PSM_InvalidInt(void)
 {
   MOZ_ASSERT_UNREACHABLE("I/O method is invalid");
   PR_SetError(PR_INVALID_METHOD_ERROR, 0);
@@ -1673,16 +1719,17 @@ nsSSLIOLayerHelpers::~nsSSLIOLayerHelper
         "security.tls.insecure_fallback_hosts");
   }
 }
 
 nsresult
 nsSSLIOLayerHelpers::Init()
 {
   if (!nsSSLIOLayerInitialized) {
+    MOZ_ASSERT(NS_IsMainThread());
     nsSSLIOLayerInitialized = true;
     nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
     nsSSLIOLayerMethods  = *PR_GetDefaultIOMethods();
 
     nsSSLIOLayerMethods.available = (PRAvailableFN) PSMAvailable;
     nsSSLIOLayerMethods.available64 = (PRAvailable64FN) PSMAvailable64;
     nsSSLIOLayerMethods.fsync = (PRFsyncFN) _PSM_InvalidStatus;
     nsSSLIOLayerMethods.seek = (PRSeekFN) _PSM_InvalidInt;
@@ -1714,39 +1761,60 @@ nsSSLIOLayerHelpers::Init()
     nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
     nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
 
     nsSSLPlaintextLayerIdentity = PR_GetUniqueIdentity("Plaintxext PSM layer");
     nsSSLPlaintextLayerMethods  = *PR_GetDefaultIOMethods();
     nsSSLPlaintextLayerMethods.recv = PlaintextRecv;
   }
 
-  bool enabled = false;
-  Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
-  setTreatUnsafeNegotiationAsBroken(enabled);
-
   loadVersionFallbackLimit();
-  initInsecureFallbackSites();
-
-  mPrefObserver = new PrefObserver(this);
-  Preferences::AddStrongObserver(mPrefObserver,
-                                 "security.ssl.treat_unsafe_negotiation_as_broken");
-  Preferences::AddStrongObserver(mPrefObserver,
-                                 "security.tls.version.fallback-limit");
-  Preferences::AddStrongObserver(mPrefObserver,
-                                 "security.tls.insecure_fallback_hosts");
+
+  // non main thread helpers will need to use defaults
+  if (NS_IsMainThread()) {
+    bool enabled = false;
+    Preferences::GetBool("security.ssl.treat_unsafe_negotiation_as_broken", &enabled);
+    setTreatUnsafeNegotiationAsBroken(enabled);
+
+    initInsecureFallbackSites();
+
+    mPrefObserver = new PrefObserver(this);
+    Preferences::AddStrongObserver(mPrefObserver,
+                                   "security.ssl.treat_unsafe_negotiation_as_broken");
+    Preferences::AddStrongObserver(mPrefObserver,
+                                   "security.tls.version.fallback-limit");
+    Preferences::AddStrongObserver(mPrefObserver,
+                                   "security.tls.insecure_fallback_hosts");
+  } else {
+    MOZ_ASSERT(mTlsFlags, "Only per socket version can ignore prefs");
+  }
+
   return NS_OK;
 }
 
 void
 nsSSLIOLayerHelpers::loadVersionFallbackLimit()
 {
   // see nsNSSComponent::setEnabledTLSVersions for pref handling rules
-  uint32_t limit = Preferences::GetUint("security.tls.version.fallback-limit",
-                                        3); // 3 = TLS 1.2
+  uint32_t limit = 3; // TLS 1.2
+
+  if (NS_IsMainThread()) {
+    limit = Preferences::GetUint("security.tls.version.fallback-limit",
+                                 3); // 3 = TLS 1.2
+  }
+
+  // set fallback limit if it is set in the tls flags
+  uint32_t tlsFlagsFallbackLimit = getTLSProviderFlagFallbackLimit(mTlsFlags);
+
+  if (tlsFlagsFallbackLimit) {
+    limit = tlsFlagsFallbackLimit;
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+            ("loadVersionFallbackLimit overriden by tlsFlags %d\n", limit));
+  }
+
   SSLVersionRange defaults = { SSL_LIBRARY_VERSION_TLS_1_2,
                                SSL_LIBRARY_VERSION_TLS_1_2 };
   SSLVersionRange filledInRange;
   nsNSSComponent::FillTLSVersionRange(filledInRange, limit, limit, defaults);
   if (filledInRange.max < SSL_LIBRARY_VERSION_TLS_1_2) {
     filledInRange.max = SSL_LIBRARY_VERSION_TLS_1_2;
   }
 
@@ -2493,17 +2561,47 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, b
     }
   }
 
   SSLVersionRange range;
   if (SSL_VersionRangeGet(fd, &range) != SECSuccess) {
     return NS_ERROR_FAILURE;
   }
 
-  // Use infoObject->GetProviderTlsFlags() to get the TLS flags
+  // setting TLS max version
+  uint32_t versionFlags =
+    getTLSProviderFlagMaxVersion(infoObject->GetProviderTlsFlags());
+  if (versionFlags) {
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+            ("[%p] nsSSLIOLayerSetOptions: version flags %d\n", fd, versionFlags));
+    if (versionFlags == kTLSProviderFlagMaxVersion10) {
+      range.max = SSL_LIBRARY_VERSION_TLS_1_0;
+    } else if (versionFlags == kTLSProviderFlagMaxVersion11) {
+      range.max = SSL_LIBRARY_VERSION_TLS_1_1;
+    } else if (versionFlags == kTLSProviderFlagMaxVersion12) {
+      range.max = SSL_LIBRARY_VERSION_TLS_1_2;
+    } else if (versionFlags == kTLSProviderFlagMaxVersion13) {
+      range.max = SSL_LIBRARY_VERSION_TLS_1_3;
+    } else {
+      MOZ_LOG(gPIPNSSLog, LogLevel::Error,
+              ("[%p] nsSSLIOLayerSetOptions: unknown version flags %d\n",
+               fd, versionFlags));
+    }
+  }
+
+  // enabling alternative server hello
+  if (getTLSProviderFlagAltServerHello(infoObject->GetProviderTlsFlags())) {
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+            ("[%p] nsSSLIOLayerSetOptions: Use AltServerHello\n", fd));
+    if (SECSuccess != SSL_UseAltServerHelloType(fd, PR_TRUE)) {
+          MOZ_LOG(gPIPNSSLog, LogLevel::Error,
+                  ("[%p] nsSSLIOLayerSetOptions: Use AltServerHello failed\n", fd));
+          // continue on default path
+    }
+  }
 
   if ((infoObject->GetProviderFlags() & nsISocketProvider::BE_CONSERVATIVE) &&
       (range.max > SSL_LIBRARY_VERSION_TLS_1_2)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("[%p] nsSSLIOLayerSetOptions: range.max limited to 1.2 due to BE_CONSERVATIVE flag\n",
              fd));
     range.max = SSL_LIBRARY_VERSION_TLS_1_2;
   }
@@ -2626,26 +2724,36 @@ nsSSLIOLayerAddToSocket(int32_t family,
                         uint32_t providerTlsFlags)
 {
   nsNSSShutDownPreventionLock locker;
   PRFileDesc* layer = nullptr;
   PRFileDesc* plaintextLayer = nullptr;
   nsresult rv;
   PRStatus stat;
 
-  SharedSSLState* sharedState =
-    providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE ? PrivateSSLState() : PublicSSLState();
+  SharedSSLState* sharedState = nullptr;
+  RefPtr<SharedSSLState> allocatedState;
+  if (providerTlsFlags) {
+    allocatedState = new SharedSSLState(providerTlsFlags);
+    sharedState = allocatedState.get();
+  } else {
+    sharedState = (providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE) ? PrivateSSLState() : PublicSSLState();
+  }
+
   nsNSSSocketInfo* infoObject = new nsNSSSocketInfo(*sharedState, providerFlags, providerTlsFlags);
   if (!infoObject) return NS_ERROR_FAILURE;
 
   NS_ADDREF(infoObject);
   infoObject->SetForSTARTTLS(forSTARTTLS);
   infoObject->SetHostName(host);
   infoObject->SetPort(port);
   infoObject->SetOriginAttributes(originAttributes);
+  if (allocatedState) {
+    infoObject->SetSharedOwningReference(allocatedState);
+  }
 
   bool haveProxy = false;
   if (proxy) {
     nsCString proxyHost;
     proxy->GetHost(proxyHost);
     haveProxy = !proxyHost.IsEmpty();
   }
 
--- a/security/manager/ssl/nsNSSIOLayer.h
+++ b/security/manager/ssl/nsNSSIOLayer.h
@@ -159,16 +159,18 @@ public:
     MOZ_ASSERT(amount >= mShortWriteOriginalAmount,
                "unexpected amount length after short write");
     MOZ_ASSERT(!memcmp(mShortWriteBufferCheck.get(), data, mShortWriteOriginalAmount),
                "unexpected buffer content after short write");
     mShortWriteBufferCheck = nullptr;
   }
 #endif
 
+  void SetSharedOwningReference(mozilla::psm::SharedSSLState* ref);
+
 protected:
   virtual ~nsNSSSocketInfo();
 
 private:
   PRFileDesc* mFd;
 
   CertVerificationState mCertVerificationState;
 
@@ -220,22 +222,29 @@ private:
   bool    mBypassAuthentication;
 
   uint32_t mProviderFlags;
   uint32_t mProviderTlsFlags;
   mozilla::TimeStamp mSocketCreationTimestamp;
   uint64_t mPlaintextBytesRead;
 
   nsCOMPtr<nsIX509Cert> mClientCert;
+
+  // if non-null this is a reference to the mSharedState (which is
+  // not an owning reference). If this is used, the info has a private
+  // state that does not share things like intolerance lists with the
+  // rest of the session. This is normally used when you have per
+  // socket tls flags overriding session wide defaults.
+  RefPtr<mozilla::psm::SharedSSLState> mOwningSharedRef;
 };
 
 class nsSSLIOLayerHelpers
 {
 public:
-  nsSSLIOLayerHelpers();
+  explicit nsSSLIOLayerHelpers(uint32_t aTlsFlags = 0);
   ~nsSSLIOLayerHelpers();
 
   nsresult Init();
   void Cleanup();
 
   static bool nsSSLIOLayerInitialized;
   static PRDescIdentity nsSSLIOLayerIdentity;
   static PRDescIdentity nsSSLPlaintextLayerIdentity;
@@ -283,16 +292,17 @@ public:
   bool isPublic() const;
   void removeInsecureFallbackSite(const nsACString& hostname, uint16_t port);
   bool isInsecureFallbackSite(const nsACString& hostname);
 
   uint16_t mVersionFallbackLimit;
 private:
   mozilla::Mutex mutex;
   nsCOMPtr<nsIObserver> mPrefObserver;
+  uint32_t mTlsFlags;
 };
 
 nsresult nsSSLIOLayerNewSocket(int32_t family,
                                const char* host,
                                int32_t port,
                                nsIProxyInfo *proxy,
                                const OriginAttributes& originAttributes,
                                PRFileDesc** fd,
--- a/security/nss.symbols
+++ b/security/nss.symbols
@@ -659,16 +659,17 @@ SSL_ClearSessionCache
 SSL_ConfigSecureServer
 SSL_ConfigSecureServerWithCertChain
 SSL_ConfigServerSessionIDCache
 SSL_ExportKeyingMaterial
 SSL_ForceHandshake
 SSL_GetChannelInfo
 SSL_GetCipherSuiteInfo
 SSL_GetClientAuthDataHook
+SSL_GetExperimentalAPI
 SSL_GetImplementedCiphers
 SSL_GetNextProto
 SSL_GetNumImplementedCiphers
 SSL_GetPreliminaryChannelInfo
 SSL_GetSRTPCipher
 SSL_GetStatistics
 SSL_HandshakeCallback
 SSL_HandshakeNegotiatedExtension
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -3527,17 +3527,17 @@ dependencies = [
  "url 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "uuid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "webdriver 0.22.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "webrender"
 version = "0.49.0"
-source = "git+https://github.com/servo/webrender#95a4ba0fb673091f7258d929bd2091e629f1c475"
+source = "git+https://github.com/servo/webrender#108405dd65004b13c41cd3a032d2750663d3f378"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-text 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3555,17 +3555,17 @@ dependencies = [
  "thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
  "webrender_api 0.49.0 (git+https://github.com/servo/webrender)",
 ]
 
 [[package]]
 name = "webrender_api"
 version = "0.49.0"
-source = "git+https://github.com/servo/webrender#95a4ba0fb673091f7258d929bd2091e629f1c475"
+source = "git+https://github.com/servo/webrender#108405dd65004b13c41cd3a032d2750663d3f378"
 dependencies = [
  "app_units 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "bincode 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-foundation 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "core-graphics 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "dwrote 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
--- a/servo/components/layout/block.rs
+++ b/servo/components/layout/block.rs
@@ -1424,16 +1424,19 @@ impl BlockFlow {
                         .to_used_value(containing_block_size);
             }
         }
     }
 
     /// Determines the type of formatting context this is. See the definition of
     /// `FormattingContextType`.
     pub fn formatting_context_type(&self) -> FormattingContextType {
+        if self.is_inline_flex_item() || self.is_block_flex_item() {
+            return FormattingContextType::Other
+        }
         let style = self.fragment.style();
         if style.get_box().float != float::T::none {
             return FormattingContextType::Other
         }
         match style.get_box().display {
             display::T::table_cell |
             display::T::table_caption |
             display::T::table_row_group |
--- a/servo/components/layout/flex.rs
+++ b/servo/components/layout/flex.rs
@@ -2,36 +2,38 @@
  * 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/. */
 
 //! Layout for elements with a CSS `display` property of `flex`.
 
 #![deny(unsafe_code)]
 
 use app_units::{Au, MAX_AU};
-use block::{BlockFlow, MarginsMayCollapseFlag};
+use block::{AbsoluteAssignBSizesTraversal, BlockFlow, MarginsMayCollapseFlag};
 use context::LayoutContext;
 use display_list_builder::{DisplayListBuildState, FlexFlowDisplayListBuilding};
 use euclid::Point2D;
 use floats::FloatKind;
 use flow;
 use flow::{Flow, FlowClass, ImmutableFlowUtils, OpaqueFlow};
 use flow::{INLINE_POSITION_IS_STATIC, IS_ABSOLUTELY_POSITIONED};
 use fragment::{Fragment, FragmentBorderBoxIterator, Overflow};
 use layout_debug;
+use model::{AdjoiningMargins, CollapsibleMargins};
 use model::{IntrinsicISizes, MaybeAuto, SizeConstraint};
 use std::cmp::{max, min};
 use std::ops::Range;
 use style::computed_values::{align_content, align_self, flex_direction, flex_wrap, justify_content};
 use style::logical_geometry::{Direction, LogicalSize};
 use style::properties::ComputedValues;
 use style::servo::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW};
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone};
 use style::values::computed::flex::FlexBasis;
 use style::values::generics::flex::FlexBasis as GenericFlexBasis;
+use traversal::PreorderFlowTraversal;
 
 /// The size of an axis. May be a specified size, a min/max
 /// constraint, or an unlimited size
 #[derive(Debug, Serialize)]
 enum AxisSize {
     Definite(Au),
     MinMax(SizeConstraint),
     Infinite,
@@ -799,17 +801,16 @@ impl FlexFlow {
                         };
                 }
             }
             cur_b += line_interval + line.cross_size;
         }
         let total_block_size = total_cross_size + self.block_flow.fragment.border_padding.block_start_end();
         self.block_flow.fragment.border_box.size.block = total_block_size;
         self.block_flow.base.position.size.block = total_block_size;
-
     }
 }
 
 impl Flow for FlexFlow {
     fn class(&self) -> FlowClass {
         FlowClass::Flex
     }
 
@@ -936,23 +937,39 @@ impl Flow for FlexFlow {
                                                     inline_start_content_edge,
                                                     inline_end_content_edge,
                                                     content_inline_size)
             }
         }
     }
 
     fn assign_block_size(&mut self, layout_context: &LayoutContext) {
-        self.block_flow
-            .assign_block_size_block_base(layout_context,
-                                          None,
-                                          MarginsMayCollapseFlag::MarginsMayNotCollapse);
         match self.main_mode {
-            Direction::Inline => self.inline_mode_assign_block_size(layout_context),
-            Direction::Block => self.block_mode_assign_block_size(),
+            Direction::Inline => {
+                self.inline_mode_assign_block_size(layout_context);
+                let block_start = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_start);
+                let block_end = AdjoiningMargins::from_margin(self.block_flow.fragment.margin.block_end);
+                self.block_flow.base.collapsible_margins = CollapsibleMargins::Collapse(block_start, block_end);
+
+                // TODO(stshine): assign proper static position for absolute descendants.
+                if (&*self as &Flow).contains_roots_of_absolute_flow_tree() {
+                    // Assign block-sizes for all flows in this absolute flow tree.
+                    // This is preorder because the block-size of an absolute flow may depend on
+                    // the block-size of its containing block, which may also be an absolute flow.
+                    let assign_abs_b_sizes = AbsoluteAssignBSizesTraversal(layout_context.shared_context());
+                    assign_abs_b_sizes.traverse_absolute_flows(&mut *self);
+                }
+            }
+            Direction::Block =>{
+                self.block_flow
+                    .assign_block_size_block_base(layout_context,
+                                                  None,
+                                                  MarginsMayCollapseFlag::MarginsMayNotCollapse);
+                self.block_mode_assign_block_size();
+            }
         }
     }
 
     fn compute_stacking_relative_position(&mut self, layout_context: &LayoutContext) {
         self.block_flow.compute_stacking_relative_position(layout_context)
     }
 
     fn place_float_if_applicable<'a>(&mut self) {
--- a/servo/components/layout/flow.rs
+++ b/servo/components/layout/flow.rs
@@ -1274,19 +1274,17 @@ impl<'a> ImmutableFlowUtils for &'a Flow
 
     fn baseline_offset_of_last_line_box_in_flow(self) -> Option<Au> {
         for kid in base(self).children.iter().rev() {
             if kid.is_inline_flow() {
                 if let Some(baseline_offset) = kid.as_inline().baseline_offset_of_last_line() {
                     return Some(base(kid).position.start.b + baseline_offset)
                 }
             }
-            if kid.is_block_like() &&
-                    kid.as_block().formatting_context_type() == FormattingContextType::None &&
-                    !base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) {
+            if kid.is_block_like() && !base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) {
                 if let Some(baseline_offset) = kid.baseline_offset_of_last_line_box_in_flow() {
                     return Some(base(kid).position.start.b + baseline_offset)
                 }
             }
         }
         None
     }
 }
--- a/servo/components/layout/fragment.rs
+++ b/servo/components/layout/fragment.rs
@@ -5,52 +5,53 @@
 //! The `Fragment` type, which represents the leaves of the layout tree.
 
 #![deny(unsafe_code)]
 
 use ServoArc;
 use app_units::Au;
 use canvas_traits::canvas::CanvasMsg;
 use context::{LayoutContext, with_thread_local_font_context};
-use euclid::{Transform3D, Point2D, Vector2D, Radians, Rect, Size2D};
+use euclid::{Transform3D, Point2D, Vector2D, Rect, Size2D};
 use floats::ClearType;
 use flow::{self, ImmutableFlowUtils};
 use flow_ref::FlowRef;
 use gfx;
 use gfx::display_list::{BLUR_INFLATION_FACTOR, OpaqueNode};
 use gfx::text::glyph::ByteIndex;
 use gfx::text::text_run::{TextRun, TextRunSlice};
 use gfx_traits::StackingContextId;
 use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFragmentContext, InlineFragmentNodeInfo};
 use inline::{InlineMetrics, LAST_FRAGMENT_OF_ELEMENT, LineMetrics};
 use ipc_channel::ipc::IpcSender;
 #[cfg(debug_assertions)]
 use layout_debug;
 use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint};
-use model::{style_length, ToGfxMatrix};
+use model::style_length;
 use msg::constellation_msg::{BrowsingContextId, PipelineId};
 use net_traits::image::base::{Image, ImageMetadata};
 use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
 use range::*;
 use script_layout_interface::{HTMLCanvasData, HTMLCanvasDataSource};
 use script_layout_interface::SVGSVGData;
 use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
 use serde::ser::{Serialize, SerializeStruct, Serializer};
 use servo_url::ServoUrl;
 use std::{f32, fmt};
 use std::borrow::ToOwned;
 use std::cmp::{Ordering, max, min};
 use std::collections::LinkedList;
 use std::sync::{Arc, Mutex};
 use style::computed_values::{border_collapse, box_sizing, clear, color, display, mix_blend_mode};
-use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration_line, transform};
+use style::computed_values::{overflow_wrap, overflow_x, position, text_decoration_line};
 use style::computed_values::{transform_style, vertical_align, white_space, word_break};
 use style::computed_values::content::ContentItem;
 use style::logical_geometry::{Direction, LogicalMargin, LogicalRect, LogicalSize, WritingMode};
 use style::properties::ComputedValues;
+use style::properties::longhands::transform::computed_value::T as TransformList;
 use style::selector_parser::RestyleDamage;
 use style::servo::restyle_damage::RECONSTRUCT_FLOW;
 use style::str::char_is_whitespace;
 use style::values::{self, Either, Auto};
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
 use text;
 use text::TextRunScanner;
 use webrender_api;
@@ -2858,22 +2859,22 @@ impl Fragment {
             SpecificFragmentInfo::TruncatedFragment(_) |
             SpecificFragmentInfo::Svg(_) |
             SpecificFragmentInfo::UnscannedText(_) => true
         }
     }
 
     /// Returns the 4D matrix representing this fragment's transform.
     pub fn transform_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> {
-        let operations = match self.style.get_box().transform.0 {
+        let list = &self.style.get_box().transform;
+        let transform = match list.to_transform_3d_matrix(Some(stacking_relative_border_box)) {
+            Some(transform) => transform,
             None => return None,
-            Some(ref operations) => operations,
         };
 
-        let mut transform = Transform3D::identity();
         let transform_origin = &self.style.get_box().transform_origin;
         let transform_origin_x =
             transform_origin.horizontal
                 .to_used_value(stacking_relative_border_box.size.width)
                 .to_f32_px();
         let transform_origin_y =
             transform_origin.vertical
                 .to_used_value(stacking_relative_border_box.size.height)
@@ -2882,65 +2883,16 @@ impl Fragment {
 
         let pre_transform = Transform3D::create_translation(transform_origin_x,
                                                             transform_origin_y,
                                                             transform_origin_z);
         let post_transform = Transform3D::create_translation(-transform_origin_x,
                                                              -transform_origin_y,
                                                              -transform_origin_z);
 
-        for operation in operations {
-            let matrix = match *operation {
-                transform::ComputedOperation::Rotate(ax, ay, az, theta) => {
-                    // https://www.w3.org/TR/css-transforms-1/#funcdef-rotate3d
-                    // A direction vector that cannot be normalized, such as [0, 0, 0], will cause
-                    // the rotation to not be applied, so we use identity matrix in this case.
-                    let len = (ax * ax + ay * ay + az * az).sqrt();
-                    if len > 0. {
-                        let theta = 2.0f32 * f32::consts::PI - theta.radians();
-                        Transform3D::create_rotation(ax / len, ay / len, az / len,
-                                                     Radians::new(theta))
-                    } else {
-                        Transform3D::identity()
-                    }
-                }
-                transform::ComputedOperation::Perspective(d) => {
-                    create_perspective_matrix(d)
-                }
-                transform::ComputedOperation::Scale(sx, sy, sz) => {
-                    Transform3D::create_scale(sx, sy, sz)
-                }
-                transform::ComputedOperation::Translate(tx, ty, tz) => {
-                    let tx = tx.to_used_value(stacking_relative_border_box.size.width).to_f32_px();
-                    let ty = ty.to_used_value(stacking_relative_border_box.size.height).to_f32_px();
-                    let tz = tz.to_f32_px();
-                    Transform3D::create_translation(tx, ty, tz)
-                }
-                transform::ComputedOperation::Matrix(m) => {
-                    m.to_gfx_matrix()
-                }
-                transform::ComputedOperation::MatrixWithPercents(_) => {
-                    // `-moz-transform` is not implemented in Servo yet.
-                    unreachable!()
-                }
-                transform::ComputedOperation::Skew(theta_x, theta_y) => {
-                    Transform3D::create_skew(Radians::new(theta_x.radians()),
-                                          Radians::new(theta_y.radians()))
-                }
-                transform::ComputedOperation::InterpolateMatrix { .. } |
-                transform::ComputedOperation::AccumulateMatrix { .. } => {
-                    // TODO: Convert InterpolateMatrix/AccmulateMatrix into a valid Transform3D by
-                    // the reference box.
-                    Transform3D::identity()
-                }
-            };
-
-            transform = transform.pre_mul(&matrix);
-        }
-
         Some(pre_transform.pre_mul(&transform).pre_mul(&post_transform))
     }
 
     /// Returns the 4D matrix representing this fragment's perspective.
     pub fn perspective_matrix(&self, stacking_relative_border_box: &Rect<Au>) -> Option<Transform3D<f32>> {
         match self.style().get_box().perspective {
             Either::First(length) => {
                 let perspective_origin = self.style().get_box().perspective_origin;
@@ -2955,17 +2907,17 @@ impl Fragment {
 
                 let pre_transform = Transform3D::create_translation(perspective_origin.x,
                                                                     perspective_origin.y,
                                                                     0.0);
                 let post_transform = Transform3D::create_translation(-perspective_origin.x,
                                                                      -perspective_origin.y,
                                                                      0.0);
 
-                let perspective_matrix = create_perspective_matrix(length);
+                let perspective_matrix = TransformList::create_perspective_matrix(length);
 
                 Some(pre_transform.pre_mul(&perspective_matrix).pre_mul(&post_transform))
             }
             Either::Second(values::None_) => {
                 None
             }
         }
     }
@@ -3199,25 +3151,8 @@ impl Serialize for DebugId {
 }
 
 #[cfg(debug_assertions)]
 impl Serialize for DebugId {
     fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
         serializer.serialize_u16(self.0)
     }
 }
-
-// TODO(gw): The transforms spec says that perspective length must
-// be positive. However, there is some confusion between the spec
-// and browser implementations as to handling the case of 0 for the
-// perspective value. Until the spec bug is resolved, at least ensure
-// that a provided perspective value of <= 0.0 doesn't cause panics
-// and behaves as it does in other browsers.
-// See https://lists.w3.org/Archives/Public/www-style/2016Jan/0020.html for more details.
-#[inline]
-fn create_perspective_matrix(d: Au) -> Transform3D<f32> {
-    let d = d.to_f32_px();
-    if d <= 0.0 {
-        Transform3D::identity()
-    } else {
-        Transform3D::create_perspective(d)
-    }
-}
--- a/servo/components/layout/model.rs
+++ b/servo/components/layout/model.rs
@@ -2,21 +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/. */
 
 //! Borders, padding, and margins.
 
 #![deny(unsafe_code)]
 
 use app_units::Au;
-use euclid::{Transform3D, SideOffsets2D, Size2D};
+use euclid::{SideOffsets2D, Size2D};
 use fragment::Fragment;
 use std::cmp::{max, min};
 use std::fmt;
-use style::computed_values::transform::ComputedMatrix;
 use style::logical_geometry::{LogicalMargin, WritingMode};
 use style::properties::ComputedValues;
 use style::values::computed::{BorderCornerRadius, LengthOrPercentageOrAuto};
 use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrNone};
 
 /// A collapsible margin. See CSS 2.1 § 8.3.1.
 #[derive(Clone, Copy, Debug)]
 pub struct AdjoiningMargins {
@@ -503,30 +502,16 @@ pub fn specified_margin_from_style(style
     let margin_style = style.get_margin();
     LogicalMargin::from_physical(writing_mode, SideOffsets2D::new(
         MaybeAuto::from_style(margin_style.margin_top, Au(0)).specified_or_zero(),
         MaybeAuto::from_style(margin_style.margin_right, Au(0)).specified_or_zero(),
         MaybeAuto::from_style(margin_style.margin_bottom, Au(0)).specified_or_zero(),
         MaybeAuto::from_style(margin_style.margin_left, Au(0)).specified_or_zero()))
 }
 
-pub trait ToGfxMatrix {
-    fn to_gfx_matrix(&self) -> Transform3D<f32>;
-}
-
-impl ToGfxMatrix for ComputedMatrix {
-    fn to_gfx_matrix(&self) -> Transform3D<f32> {
-        Transform3D::row_major(
-            self.m11 as f32, self.m12 as f32, self.m13 as f32, self.m14 as f32,
-            self.m21 as f32, self.m22 as f32, self.m23 as f32, self.m24 as f32,
-            self.m31 as f32, self.m32 as f32, self.m33 as f32, self.m34 as f32,
-            self.m41 as f32, self.m42 as f32, self.m43 as f32, self.m44 as f32)
-    }
-}
-
 /// A min-size and max-size constraint. The constructor has a optional `border`
 /// parameter, and when it is present the constraint will be subtracted. This is
 /// used to adjust the constraint for `box-sizing: border-box`, and when you do so
 /// make sure the size you want to clamp is intended to be used for content box.
 #[derive(Clone, Copy, Debug, Serialize)]
 pub struct SizeConstraint {
     min_size: Au,
     max_size: Option<Au>,
--- a/servo/components/style/custom_properties.rs
+++ b/servo/components/style/custom_properties.rs
@@ -12,17 +12,17 @@ use parser::ParserContext;
 use properties::{CSSWideKeyword, DeclaredValue};
 use selectors::parser::SelectorParseError;
 use servo_arc::Arc;
 use std::ascii::AsciiExt;
 use std::borrow::{Borrow, Cow};
 use std::collections::{HashMap, HashSet};
 use std::fmt;
 use std::hash::Hash;
-use style_traits::{HasViewportPercentage, ToCss, StyleParseError, ParseError};
+use style_traits::{ToCss, StyleParseError, ParseError};
 
 /// A custom property name is just an `Atom`.
 ///
 /// Note that this does not include the `--` prefix
 pub type Name = Atom;
 
 /// Parse a custom property name.
 ///
@@ -46,22 +46,16 @@ pub struct SpecifiedValue {
 
     first_token_type: TokenSerializationType,
     last_token_type: TokenSerializationType,
 
     /// Custom property names in var() functions.
     references: HashSet<Name>,
 }
 
-impl HasViewportPercentage for SpecifiedValue {
-    fn has_viewport_percentage(&self) -> bool {
-        panic!("has_viewport_percentage called before resolving!");
-    }
-}
-
 /// This struct is a cheap borrowed version of a `SpecifiedValue`.
 pub struct BorrowedSpecifiedValue<'a> {
     css: &'a str,
     first_token_type: TokenSerializationType,
     last_token_type: TokenSerializationType,
     references: Option<&'a HashSet<Name>>,
 }
 
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -260,17 +260,17 @@ impl Atom {
                       "Called from_static for a non-static atom!");
         atom
     }
 
     /// Creates an atom from a dynamic atom pointer that has already had AddRef
     /// called on it.
     #[inline]
     pub unsafe fn from_addrefed(ptr: *mut nsIAtom) -> Self {
-        debug_assert!(!ptr.is_null());
+        assert!(!ptr.is_null());
         unsafe {
             Atom(WeakAtom::new(ptr))
         }
     }
 
     /// Convert this atom into an addrefed nsIAtom pointer.
     #[inline]
     pub fn into_addrefed(self) -> *mut nsIAtom {
@@ -373,17 +373,17 @@ impl From<String> for Atom {
     fn from(string: String) -> Atom {
         Atom::from(&*string)
     }
 }
 
 impl From<*mut nsIAtom> for Atom {
     #[inline]
     fn from(ptr: *mut nsIAtom) -> Atom {
-        debug_assert!(!ptr.is_null());
+        assert!(!ptr.is_null());
         unsafe {
             let ret = Atom(WeakAtom::new(ptr));
             if !ret.is_static() {
                 Gecko_AddRefAtom(ptr);
             }
             ret
         }
     }
--- a/servo/components/style/macros.rs
+++ b/servo/components/style/macros.rs
@@ -52,36 +52,34 @@ macro_rules! define_numbered_css_keyword
                 match *self {
                     $( $name::$variant => dest.write_str($css) ),+
                 }
             }
         }
     }
 }
 
-/// A macro for implementing `ComputedValueAsSpecified`, `Parse`
-/// and `HasViewportPercentage` traits for the enums defined
-/// using `define_css_keyword_enum` macro.
+/// A macro for implementing `ComputedValueAsSpecified`, and `Parse` traits for
+/// the enums defined using `define_css_keyword_enum` macro.
 ///
 /// NOTE: We should either move `Parse` trait to `style_traits`
 /// or `define_css_keyword_enum` macro to this crate, but that
 /// may involve significant cleanup in both the crates.
 macro_rules! add_impls_for_keyword_enum {
     ($name:ident) => {
         impl $crate::parser::Parse for $name {
             #[inline]
             fn parse<'i, 't>(_context: &$crate::parser::ParserContext,
                              input: &mut ::cssparser::Parser<'i, 't>)
                              -> Result<Self, ::style_traits::ParseError<'i>> {
                 $name::parse(input)
             }
         }
 
         impl $crate::values::computed::ComputedValueAsSpecified for $name {}
-        no_viewport_percentage!($name);
     };
 }
 
 macro_rules! define_keyword_type {
     ($name: ident, $css: expr) => {
         #[allow(missing_docs)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Animate, Clone, ComputeSquaredDistance, Copy, PartialEq)]
@@ -99,11 +97,10 @@ macro_rules! define_keyword_type {
                              input: &mut ::cssparser::Parser<'i, 't>)
                              -> Result<$name, ::style_traits::ParseError<'i>> {
                 input.expect_ident_matching($css).map(|_| $name).map_err(|e| e.into())
             }
         }
 
         impl $crate::values::computed::ComputedValueAsSpecified for $name {}
         impl $crate::values::animated::AnimatedValueAsComputed for $name {}
-        no_viewport_percentage!($name);
     };
 }
--- a/servo/components/style/properties/helpers.mako.rs
+++ b/servo/components/style/properties/helpers.mako.rs
@@ -78,18 +78,16 @@
 </%doc>
 <%def name="vector_longhand(name, animation_value_type=None, allow_empty=False, separator='Comma',
                             need_animatable=False, **kwargs)">
     <%call expr="longhand(name, animation_value_type=animation_value_type, vector=True,
                           need_animatable=need_animatable, **kwargs)">
         #[allow(unused_imports)]
         use smallvec::SmallVec;
         use std::fmt;
-        #[allow(unused_imports)]
-        use style_traits::HasViewportPercentage;
         use style_traits::{Separator, ToCss};
 
         pub mod single_value {
             #[allow(unused_imports)]
             use cssparser::{Parser, BasicParseError};
             #[allow(unused_imports)]
             use parser::{Parse, ParserContext};
             #[allow(unused_imports)]
@@ -174,17 +172,17 @@
                     dest.write_str(::style_traits::${separator}::separator())?;
                     i.to_css(dest)?;
                 }
                 Ok(())
             }
         }
 
         /// The specified value of ${name}.
-        #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+        #[derive(Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct SpecifiedValue(pub Vec<single_value::SpecifiedValue>);
 
         impl ToCss for SpecifiedValue {
             fn to_css<W>(&self, dest: &mut W) -> fmt::Result
                 where W: fmt::Write,
             {
                 let mut iter = self.0.iter();
@@ -415,17 +413,16 @@
             'gecko_constant_prefix', 'gecko_enum_prefix',
             'extra_gecko_values', 'extra_servo_values',
             'custom_consts', 'gecko_inexhaustive',
         ]}
         keyword = keyword=Keyword(name, values, **keyword_kwargs)
     %>
     <%call expr="longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
         use properties::longhands::system_font::SystemFont;
-        no_viewport_percentage!(SpecifiedValue);
 
         pub mod computed_value {
             use cssparser::Parser;
             use parser::{Parse, ParserContext};
 
             use style_traits::{ToCss, ParseError};
             define_css_keyword_enum! { T:
                 % for value in keyword.values_for(product):
@@ -524,17 +521,16 @@
                     }
                 }
             }
         % else:
             use values::computed::ComputedValueAsSpecified;
             impl ComputedValueAsSpecified for SpecifiedValue {}
         % endif
 
-        no_viewport_percentage!(SpecifiedValue);
     </%call>
 </%def>
 
 <%def name="gecko_keyword_conversion(keyword, values=None, type='SpecifiedValue', cast_to=None)">
     <%
         if not values:
             values = keyword.values_for(product)
         maybe_cast = "as %s" % cast_to if cast_to else ""
@@ -943,17 +939,17 @@
         % endif
         use values::specified::${length_type};
 
         pub mod computed_value {
             pub type T = ::values::computed::${length_type};
         }
 
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-        #[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
+        #[derive(Clone, Debug, PartialEq, ToCss)]
         pub struct SpecifiedValue(pub ${length_type});
 
         % if length_type == "MozLength":
         impl SpecifiedValue {
             /// Returns the `auto` value.
             pub fn auto() -> Self {
                 use values::specified::length::LengthOrPercentageOrAuto;
                 SpecifiedValue(MozLength::LengthOrPercentageOrAuto(LengthOrPercentageOrAuto::Auto))
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
 <% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
 
 use app_units::Au;
 use cssparser::Parser;
-use euclid::Point3D;
 #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
 #[cfg(feature = "gecko")] use gecko_string_cache::Atom;
 use itertools::{EitherOrBoth, Itertools};
 use properties::{CSSWideKeyword, PropertyDeclaration};
 use properties::longhands;
@@ -49,16 +48,17 @@ use values::computed::{Angle, BorderCorn
 use values::computed::{ClipRect, Context, ComputedUrl, ComputedValueAsSpecified};
 use values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
 use values::computed::{LengthOrPercentageOrNone, MaxLength, NonNegativeAu};
 use values::computed::{NonNegativeNumber, Number, NumberOrPercentage, Percentage};
 use values::computed::{PositiveIntegerOrAuto, ToComputedValue};
 #[cfg(feature = "gecko")] use values::computed::MozLength;
 use values::computed::length::{NonNegativeLengthOrAuto, NonNegativeLengthOrNormal};
 use values::computed::length::NonNegativeLengthOrPercentage;
+use values::computed::transform::DirectionVector;
 use values::distance::{ComputeSquaredDistance, SquaredDistance};
 use values::generics::NonNegative;
 use values::generics::effects::Filter;
 use values::generics::position as generic_position;
 use values::generics::svg::{SVGLength,  SvgLengthOrPercentageOrNumber, SVGPaint};
 use values::generics::svg::{SVGPaintKind, SVGStrokeDashArray, SVGOpacity};
 
 /// https://drafts.csswg.org/css-transitions/#animtype-repeatable-list
@@ -209,17 +209,16 @@ pub enum TransitionProperty {
             ${prop.camel_case},
         % endif
     % endfor
     /// Unrecognized property which could be any non-transitionable, custom property, or
     /// unknown property.
     Unsupported(CustomIdent)
 }
 
-no_viewport_percentage!(TransitionProperty);
 
 impl ComputedValueAsSpecified for TransitionProperty {}
 
 impl TransitionProperty {
     /// Iterates over each longhand property.
     pub fn each<F: FnMut(&TransitionProperty) -> ()>(mut cb: F) {
         % for prop in data.longhands:
             % if prop.transitionable:
@@ -955,17 +954,17 @@ impl ToAnimatedZero for TransformOperati
                     ty.to_animated_zero()?,
                     tz.to_animated_zero()?,
                 ))
             },
             TransformOperation::Scale(..) => {
                 Ok(TransformOperation::Scale(1.0, 1.0, 1.0))
             },
             TransformOperation::Rotate(x, y, z, a) => {
-                let (x, y, z, _) = get_normalized_vector_and_angle(x, y, z, a);
+                let (x, y, z, _) = TransformList::get_normalized_vector_and_angle(x, y, z, a);
                 Ok(TransformOperation::Rotate(x, y, z, Angle::zero()))
             },
             TransformOperation::Perspective(..) |
             TransformOperation::AccumulateMatrix { .. } |
             TransformOperation::InterpolateMatrix { .. } => {
                 // Perspective: We convert a perspective function into an equivalent
                 //     ComputedMatrix, and then decompose/interpolate/recompose these matrices.
                 // AccumulateMatrix/InterpolateMatrix: We do interpolation on
@@ -1032,18 +1031,20 @@ impl Animate for TransformOperation {
                     animate_multiplicative_factor(*fy, *ty, procedure)?,
                     animate_multiplicative_factor(*fz, *tz, procedure)?,
                 ))
             },
             (
                 &TransformOperation::Rotate(fx, fy, fz, fa),
                 &TransformOperation::Rotate(tx, ty, tz, ta),
             ) => {
-                let (fx, fy, fz, fa) = get_normalized_vector_and_angle(fx, fy, fz, fa);
-                let (tx, ty, tz, ta) = get_normalized_vector_and_angle(tx, ty, tz, ta);
+                let (fx, fy, fz, fa) =
+                    TransformList::get_normalized_vector_and_angle(fx, fy, fz, fa);
+                let (tx, ty, tz, ta) =
+                    TransformList::get_normalized_vector_and_angle(tx, ty, tz, ta);
                 if (fx, fy, fz) == (tx, ty, tz) {
                     let ia = fa.animate(&ta, procedure)?;
                     Ok(TransformOperation::Rotate(fx, fy, fz, ia))
                 } else {
                     let matrix_f = rotate_to_matrix(fx, fy, fz, fa);
                     let matrix_t = rotate_to_matrix(tx, ty, tz, ta);
                     Ok(TransformOperation::Matrix(
                         matrix_f.animate(&matrix_t, procedure)?,
@@ -1446,39 +1447,34 @@ pub struct MatrixDecomposed3D {
     /// The skew component of the transformation.
     pub skew: Skew,
     /// The perspective component of the transformation.
     pub perspective: Perspective,
     /// The quaternion used to represent the rotation.
     pub quaternion: Quaternion,
 }
 
-/// A wrapper of Point3D to represent the direction vector (rotate axis) for Rotate3D.
-#[derive(Clone, Copy, Debug, PartialEq)]
-#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
-pub struct DirectionVector(Point3D<f64>);
-
 impl Quaternion {
     /// Return a quaternion from a unit direction vector and angle (unit: radian).
     #[inline]
     fn from_direction_and_angle(vector: &DirectionVector, angle: f64) -> Self {
-        debug_assert!((vector.length() - 1.).abs() < 0.0001f64,
-                       "Only accept an unit direction vector to create a quaternion");
+        debug_assert!((vector.length() - 1.).abs() < 0.0001,
+                      "Only accept an unit direction vector to create a quaternion");
         // Reference:
         // https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation
         //
         // if the direction axis is (x, y, z) = xi + yj + zk,
         // and the angle is |theta|, this formula can be done using
         // an extension of Euler's formula:
         //   q = cos(theta/2) + (xi + yj + zk)(sin(theta/2))
         //     = cos(theta/2) +
         //       x*sin(theta/2)i + y*sin(theta/2)j + z*sin(theta/2)k
-        Quaternion(vector.0.x * (angle / 2.).sin(),
-                   vector.0.y * (angle / 2.).sin(),
-                   vector.0.z * (angle / 2.).sin(),
+        Quaternion(vector.x as f64 * (angle / 2.).sin(),
+                   vector.y as f64 * (angle / 2.).sin(),
+                   vector.z as f64 * (angle / 2.).sin(),
                    (angle / 2.).cos())
     }
 
     /// Calculate the dot product.
     #[inline]
     fn dot(&self, other: &Self) -> f64 {
         self.0 * other.0 + self.1 * other.1 + self.2 * other.2 + self.3 * other.3
     }
@@ -1490,57 +1486,16 @@ impl ComputeSquaredDistance for Quaterni
         // Use quaternion vectors to get the angle difference. Both q1 and q2 are unit vectors,
         // so we can get their angle difference by:
         // cos(theta/2) = (q1 dot q2) / (|q1| * |q2|) = q1 dot q2.
         let distance = self.dot(other).max(-1.0).min(1.0).acos() * 2.0;
         Ok(SquaredDistance::Value(distance * distance))
     }
 }
 
-impl DirectionVector {
-    /// Create a DirectionVector.
-    #[inline]
-    fn new(x: f32, y: f32, z: f32) -> Self {
-        DirectionVector(Point3D::new(x as f64, y as f64, z as f64))
-    }
-
-    /// Return the normalized direction vector.
-    #[inline]
-    fn normalize(&mut self) -> bool {
-        let len = self.length();
-        if len > 0. {
-            self.0.x = self.0.x / len;
-            self.0.y = self.0.y / len;
-            self.0.z = self.0.z / len;
-            true
-        } else {
-            false
-        }
-    }
-
-    /// Get the length of this vector.
-    #[inline]
-    fn length(&self) -> f64 {
-        self.0.to_array().iter().fold(0f64, |sum, v| sum + v * v).sqrt()
-    }
-}
-
-/// Return the normalized direction vector and its angle.
-// A direction vector that cannot be normalized, such as [0,0,0], will cause the
-// rotation to not be applied. i.e. Use an identity matrix or rotate3d(0, 0, 1, 0).
-fn get_normalized_vector_and_angle(x: f32, y: f32, z: f32, angle: Angle)
-                                   -> (f32, f32, f32, Angle) {
-    let mut vector = DirectionVector::new(x, y, z);
-    if vector.normalize() {
-        (vector.0.x as f32, vector.0.y as f32, vector.0.z as f32, angle)
-    } else {
-        (0., 0., 1., Angle::zero())
-    }
-}
-
 /// Decompose a 3D matrix.
 /// https://drafts.csswg.org/css-transforms/#decomposing-a-3d-matrix
 fn decompose_3d_matrix(mut matrix: ComputedMatrix) -> Result<MatrixDecomposed3D, ()> {
     // Normalize the matrix.
     if matrix.m44 == 0.0 {
         return Err(());
     }
 
@@ -1687,18 +1642,18 @@ fn decompose_3d_matrix(mut matrix: Compu
 fn decompose_2d_matrix(matrix: &ComputedMatrix) -> Result<MatrixDecomposed3D, ()> {
     // The index is column-major, so the equivalent transform matrix is:
     // | m11 m21  0 m41 |  =>  | m11 m21 | and translate(m41, m42)
     // | m12 m22  0 m42 |      | m12 m22 |
     // |   0   0  1   0 |
     // |   0   0  0   1 |
     let (mut m11, mut m12) = (matrix.m11, matrix.m12);
     let (mut m21, mut m22) = (matrix.m21, matrix.m22);
+    // Check if this is a singular matrix.
     if m11 * m22 == m12 * m21 {
-        // singular matrix
         return Err(());
     }
 
     let mut scale_x = (m11 * m11 + m12 * m12).sqrt();
     m11 /= scale_x;
     m12 /= scale_x;
 
     let mut shear_xy = m11 * m21 + m12 * m22;
@@ -1706,18 +1661,20 @@ fn decompose_2d_matrix(matrix: &Computed
     m22 -= m12 * shear_xy;
 
     let scale_y = (m21 * m21 + m22 * m22).sqrt();
     m21 /= scale_y;
     m22 /= scale_y;
     shear_xy /= scale_y;
 
     let determinant = m11 * m22 - m12 * m21;
-    debug_assert!(0.99 < determinant.abs() && determinant.abs() < 1.01,
-                  "determinant should now be 1 or -1");
+    // Determinant should now be 1 or -1.
+    if 0.99 > determinant.abs() || determinant.abs() > 1.01 {
+        return Err(());
+    }
 
     if determinant < 0. {
         m11 = -m11;
         m12 = -m12;
         shear_xy = -shear_xy;
         scale_x = -scale_x;
     }
 
@@ -2195,18 +2152,20 @@ impl ComputeSquaredDistance for Transfor
                     fy.compute_squared_distance(&ty)? +
                     fz.compute_squared_distance(&tz)?,
                 )
             },
             (
                 &TransformOperation::Rotate(fx, fy, fz, fa),
                 &TransformOperation::Rotate(tx, ty, tz, ta),
             ) => {
-                let (fx, fy, fz, angle1) = get_normalized_vector_and_angle(fx, fy, fz, fa);
-                let (tx, ty, tz, angle2) = get_normalized_vector_and_angle(tx, ty, tz, ta);
+                let (fx, fy, fz, angle1) =
+                    TransformList::get_normalized_vector_and_angle(fx, fy, fz, fa);
+                let (tx, ty, tz, angle2) =
+                    TransformList::get_normalized_vector_and_angle(tx, ty, tz, ta);
                 if (fx, fy, fz) == (tx, ty, tz) {
                     angle1.compute_squared_distance(&angle2)
                 } else {
                     let v1 = DirectionVector::new(fx, fy, fz);
                     let v2 = DirectionVector::new(tx, ty, tz);
                     let q1 = Quaternion::from_direction_and_angle(&v1, angle1.radians64());
                     let q2 = Quaternion::from_direction_and_angle(&v2, angle2.radians64());
                     q1.compute_squared_distance(&q2)
@@ -2243,29 +2202,38 @@ impl ComputeSquaredDistance for Transfor
             _ => Err(()),
         }
     }
 }
 
 impl ComputeSquaredDistance for TransformList {
     #[inline]
     fn compute_squared_distance(&self, other: &Self) -> Result<SquaredDistance, ()> {
-        let this = self.0.as_ref().map_or(&[][..], |l| l);
-        let other = other.0.as_ref().map_or(&[][..], |l| l);
+        let list1 = self.0.as_ref().map_or(&[][..], |l| l);
+        let list2 = other.0.as_ref().map_or(&[][..], |l| l);
 
-        this.iter().zip_longest(other).map(|it| {
+        let squared_dist: Result<SquaredDistance, _> = list1.iter().zip_longest(list2).map(|it| {
             match it {
                 EitherOrBoth::Both(this, other) => {
                     this.compute_squared_distance(other)
                 },
                 EitherOrBoth::Left(list) | EitherOrBoth::Right(list) => {
                     list.to_animated_zero()?.compute_squared_distance(list)
                 },
             }
-        }).sum()
+        }).sum();
+
+        // Roll back to matrix interpolation if there is any Err(()) in the transform lists, such
+        // as mismatched transform functions.
+        if let Err(_) = squared_dist {
+            let matrix1: ComputedMatrix = self.to_transform_3d_matrix(None).ok_or(())?.into();
+            let matrix2: ComputedMatrix = other.to_transform_3d_matrix(None).ok_or(())?.into();
+            return matrix1.compute_squared_distance(&matrix2);
+        }
+        squared_dist
     }
 }
 
 impl ToAnimatedZero for TransformList {
     #[inline]
     fn to_animated_zero(&self) -> Result<Self, ()> {
         match self.0 {
             None => Ok(TransformList(None)),
--- a/servo/components/style/properties/longhand/background.mako.rs
+++ b/servo/components/style/properties/longhand/background.mako.rs
@@ -64,17 +64,16 @@
     pub mod computed_value {
         pub use super::RepeatKeyword;
 
         #[derive(Clone, Debug, PartialEq)]
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
 
-    no_viewport_percentage!(SpecifiedValue);
 
     impl ToCss for computed_value::T {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match (self.0, self.1) {
                 (RepeatKeyword::Repeat, RepeatKeyword::NoRepeat) => dest.write_str("repeat-x"),
                 (RepeatKeyword::NoRepeat, RepeatKeyword::Repeat) => dest.write_str("repeat-y"),
                 (horizontal, vertical) => {
                     horizontal.to_css(dest)?;
--- a/servo/components/style/properties/longhand/border.mako.rs
+++ b/servo/components/style/properties/longhand/border.mako.rs
@@ -73,17 +73,16 @@
     <%helpers:longhand name="-moz-border-${side}-colors" animation_value_type="discrete"
                        spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-border-*-colors)"
                        products="gecko"
                        flags="APPLIES_TO_FIRST_LETTER"
                        ignored_when_colors_disabled="True">
         use std::fmt;
         use style_traits::ToCss;
         use values::specified::RGBAColor;
-        no_viewport_percentage!(SpecifiedValue);
 
         pub mod computed_value {
             use cssparser::RGBA;
             #[derive(Clone, Debug, PartialEq)]
             #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
             pub struct T(pub Option<Vec<RGBA>>);
         }
 
@@ -225,17 +224,16 @@
     flags="APPLIES_TO_FIRST_LETTER",
     boxed=True)}
 
 <%helpers:longhand name="border-image-repeat" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER"
                    spec="https://drafts.csswg.org/css-backgrounds/#border-image-repeat">
     use style_traits::ToCss;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::RepeatKeyword;
 
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         #[derive(Clone, Debug, PartialEq, ToCss)]
         pub struct T(pub RepeatKeyword, pub RepeatKeyword);
     }
--- a/servo/components/style/properties/longhand/box.mako.rs
+++ b/servo/components/style/properties/longhand/box.mako.rs
@@ -31,17 +31,16 @@
             values += """grid inline-grid ruby ruby-base ruby-base-container
                 ruby-text ruby-text-container contents flow-root -webkit-box
                 -webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid
                 -moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck
                 -moz-popup -moz-groupbox""".split()
     %>
     use values::computed::ComputedValueAsSpecified;
     use style_traits::ToCss;
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
 
         impl T {
             /// Returns whether this "display" value is the display of a flex or
             /// grid container.
             ///
@@ -217,17 +216,16 @@
                                   needs_conversion="True"
                                   animation_value_type="discrete"
                                   gecko_enum_prefix="StyleFloat"
                                   gecko_inexhaustive="True"
                                   gecko_ffi_name="mFloat"
                                   gecko_pref_ident="float_"
                                   flags="APPLIES_TO_FIRST_LETTER"
                                   spec="https://drafts.csswg.org/css-box/#propdef-float">
-    no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let ltr = context.style().writing_mode.is_bidi_ltr();
             // https://drafts.csswg.org/css-logical-props/#float-clear
             match *self {
@@ -256,17 +254,16 @@
                                   // https://drafts.csswg.org/css-logical-props/#float-clear
                                   extra_specified="inline-start inline-end"
                                   needs_conversion="True"
                                   gecko_inexhaustive="True"
                                   animation_value_type="discrete"
                                   gecko_enum_prefix="StyleClear"
                                   gecko_ffi_name="mBreakType"
                                   spec="https://www.w3.org/TR/CSS2/visuren.html#flow-control">
-    no_viewport_percentage!(SpecifiedValue);
     impl ToComputedValue for SpecifiedValue {
         type ComputedValue = computed_value::T;
 
         #[inline]
         fn to_computed_value(&self, context: &Context) -> computed_value::T {
             let ltr = context.style().writing_mode.is_bidi_ltr();
             // https://drafts.csswg.org/css-logical-props/#float-clear
             match *self {
@@ -322,17 +319,17 @@
                                         "baseline sub super top text-top middle bottom text-bottom",
                                         extra_gecko_values="-moz-middle-with-baseline") %>
     <% vertical_align_keywords = vertical_align.keyword.values_for(product) %>
 
     ${helpers.gecko_keyword_conversion(vertical_align.keyword)}
 
     /// The `vertical-align` value.
     #[allow(non_camel_case_types)]
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+    #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         % for keyword in vertical_align_keywords:
             ${to_rust_ident(keyword)},
         % endfor
         LengthOrPercentage(specified::LengthOrPercentage),
     }
 
@@ -566,17 +563,16 @@
                          -> Result<Self, ParseError<'i>> {
             if let Ok(name) = input.try(|input| KeyframesName::parse(context, input)) {
                 Ok(SpecifiedValue(Some(name)))
             } else {
                 input.expect_ident_matching("none").map(|_| SpecifiedValue(None)).map_err(|e| e.into())
             }
         }
     }
-    no_viewport_percentage!(SpecifiedValue);
 
     pub fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                          -> Result<SpecifiedValue,ParseError<'i>> {
         SpecifiedValue::parse(context, input)
     }
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
 </%helpers:vector_longhand>
@@ -636,17 +632,16 @@
             if number < 0.0 {
                 return Err(StyleParseError::UnspecifiedError.into());
             }
 
             Ok(SpecifiedValue::Number(number))
         }
     }
 
-    no_viewport_percentage!(SpecifiedValue);
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         get_initial_specified_value()
     }
 
     #[inline]
     pub fn get_initial_specified_value() -> SpecifiedValue {
@@ -837,17 +832,17 @@
     }
 
     /// Describes a single parsed
     /// [Transform Function](https://drafts.csswg.org/css-transforms/#typedef-transform-function).
     ///
     /// Multiple transform functions compose a transformation.
     ///
     /// Some transformations can be expressed by other more general functions.
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+    #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedOperation {
         /// Represents a 2D 2x3 matrix.
         Matrix(Matrix<Number>),
         /// Represents a 3D 4x4 matrix with percentage and length values.
         /// For `moz-transform`.
         PrefixedMatrix(Matrix<Number, LoPoNumber>),
         /// Represents a 3D 4x4 matrix.
@@ -986,17 +981,17 @@
                 SpecifiedOperation::AccumulateMatrix { ref from_list, ref to_list, count } => {
                     write!(dest, "accumulatematrix({}, {}, {})",
                            Css(from_list), Css(to_list), Css(count))
                 }
             }
         }
     }
 
-    #[derive(Clone, Debug, HasViewportPercentage, PartialEq)]
+    #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue(Vec<SpecifiedOperation>);
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
 
             if self.0.is_empty() {
                 return dest.write_str("none")
@@ -1700,17 +1695,16 @@
 <%helpers:longhand name="contain" animation_value_type="discrete" products="gecko" need_clone="True"
                    flags="FIXPOS_CB"
                    spec="https://drafts.csswg.org/css-contain/#contain-property">
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub type T = super::SpecifiedValue;
     }
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags SpecifiedValue: u8 {
@@ -1845,17 +1839,16 @@
 <%helpers:longhand name="will-change" products="gecko" animation_value_type="discrete"
                    spec="https://drafts.csswg.org/css-will-change/#will-change">
     use std::fmt;
     use style_traits::ToCss;
     use values::CustomIdent;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
@@ -1919,17 +1912,16 @@
                    animation_value_type="discrete"
                    spec="https://compat.spec.whatwg.org/#touch-action">
     use gecko_bindings::structs;
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     bitflags! {
         /// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants.
         pub flags SpecifiedValue: u8 {
--- a/servo/components/style/properties/longhand/color.mako.rs
+++ b/servo/components/style/properties/longhand/color.mako.rs
@@ -29,17 +29,16 @@
         fn from_computed_value(computed: &computed_value::T) -> Self {
             SpecifiedValue(Color::rgba(*computed).into())
         }
     }
 
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     #[derive(Clone, Debug, PartialEq, ToCss)]
     pub struct SpecifiedValue(pub Color);
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser;
         pub type T = cssparser::RGBA;
     }
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         RGBA::new(0, 0, 0, 255) // black
--- a/servo/components/style/properties/longhand/counters.mako.rs
+++ b/servo/components/style/properties/longhand/counters.mako.rs
@@ -18,17 +18,16 @@
 
     #[cfg(feature = "servo")]
     use super::list_style_type;
 
     pub use self::computed_value::T as SpecifiedValue;
     pub use self::computed_value::ContentItem;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser;
         use std::fmt;
         use style_traits::ToCss;
         #[cfg(feature = "gecko")]
         use values::specified::url::SpecifiedUrl;
 
@@ -291,17 +290,16 @@
         }
     }
 
     #[inline]
     pub fn get_initial_value() -> computed_value::T {
         computed_value::T(Vec::new())
     }
 
-    no_viewport_percentage!(SpecifiedValue);
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result
             where W: fmt::Write,
         {
             if self.0.is_empty() {
                 return dest.write_str("none");
             }
--- a/servo/components/style/properties/longhand/font.mako.rs
+++ b/servo/components/style/properties/longhand/font.mako.rs
@@ -71,17 +71,16 @@ macro_rules! impl_gecko_keyword_conversi
 <%helpers:longhand name="font-family" animation_value_type="discrete" need_index="True"  boxed="${product == 'gecko'}"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
     use properties::longhands::system_font::SystemFont;
     use self::computed_value::{FontFamily, FamilyName};
     use std::fmt;
     use style_traits::ToCss;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         use cssparser::{CssStringWriter, Parser, serialize_identifier};
         use std::fmt::{self, Write};
         use Atom;
         use style_traits::{ToCss, ParseError};
         pub use self::FontFamily as SingleComputedValue;
 
@@ -425,17 +424,16 @@ macro_rules! impl_gecko_keyword_conversi
                                flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
                                animation_value_type="discrete")}
 
 <%helpers:longhand name="font-weight" need_clone="True" animation_value_type="ComputedValue"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-weight">
     use properties::longhands::system_font::SystemFont;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     #[derive(Clone, Copy, Debug, Eq, PartialEq, ToCss)]
     pub enum SpecifiedValue {
         Normal,
         Bold,
         Bolder,
         Lighter,
@@ -592,17 +590,17 @@ macro_rules! impl_gecko_keyword_conversi
 </%helpers:longhand>
 
 <%helpers:longhand name="font-size" need_clone="True" animation_value_type="NonNegativeAu"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    allow_quirks="True" spec="https://drafts.csswg.org/css-fonts/#propdef-font-size">
     use app_units::Au;
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
-    use style_traits::{HasViewportPercentage, ToCss};
+    use style_traits::ToCss;
     use values::FONT_MEDIUM_PX;
     use values::computed::NonNegativeAu;
     use values::specified::{AllowQuirks, FontRelativeLength, LengthOrPercentage, NoCalcLength};
     use values::specified::length::FontBaseSize;
 
     impl ToCss for SpecifiedValue {
         fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
             match *self {
@@ -610,25 +608,16 @@ macro_rules! impl_gecko_keyword_conversi
                 SpecifiedValue::Keyword(kw, _) => kw.to_css(dest),
                 SpecifiedValue::Smaller => dest.write_str("smaller"),
                 SpecifiedValue::Larger => dest.write_str("larger"),
                 SpecifiedValue::System(sys) => sys.to_css(dest),
             }
         }
     }
 
-    impl HasViewportPercentage for SpecifiedValue {
-        fn has_viewport_percentage(&self) -> bool {
-            match *self {
-                SpecifiedValue::Length(ref lop) => lop.has_viewport_percentage(),
-                _ => false
-            }
-        }
-    }
-
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         Length(specified::LengthOrPercentage),
         /// A keyword value, along with a ratio.
         /// The ratio in any specified keyword value
         /// will be 1, but we cascade keywordness even
         /// after font-relative (percent and em) values
@@ -1063,17 +1052,16 @@ macro_rules! impl_gecko_keyword_conversi
 </%helpers:longhand>
 
 <%helpers:longhand products="gecko" name="font-size-adjust"
                    animation_value_type="longhands::font_size_adjust::computed_value::T"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-size-adjust">
     use properties::longhands::system_font::SystemFont;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Clone, Copy, Debug, PartialEq, ToCss)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum SpecifiedValue {
         None,
         Number(specified::Number),
         System(SystemFont),
     }
@@ -1185,17 +1173,16 @@ macro_rules! impl_gecko_keyword_conversi
 <%helpers:longhand products="gecko" name="font-synthesis" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-synthesis">
     use std::fmt;
     use style_traits::ToCss;
     use values::computed::ComputedValueAsSpecified;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
-    no_viewport_percentage!(SpecifiedValue);
 
     pub mod computed_value {
         pub use super::SpecifiedValue as T;
     }
 
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub struct SpecifiedValue {
@@ -1297,17 +1284,16 @@ macro_rules! impl_gecko_keyword_conversi
 <%helpers:longhand name="font-variant-alternates" products="gecko" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-alternates">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
     use values::CustomIdent;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     #[derive(Clone, Debug, PartialEq)]
     #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
     pub enum VariantAlternates {
         Stylistic(CustomIdent),
         Styleset(Box<[CustomIdent]>),
         CharacterVariant(Box<[CustomIdent]>),
         Swash(CustomIdent),
@@ -1507,17 +1493,16 @@ macro_rules! exclusive_value {
 
 <%helpers:longhand name="font-variant-east-asian" products="gecko" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-east-asian">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags VariantEastAsian: u16 {
             const NORMAL = 0,
             const JIS78 = 0x01,
             const JIS83 = 0x02,
             const JIS90 = 0x04,
@@ -1653,17 +1638,16 @@ macro_rules! exclusive_value {
 
 <%helpers:longhand name="font-variant-ligatures" products="gecko" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-ligatures">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags VariantLigatures: u16 {
             const NORMAL = 0,
             const NONE = 0x01,
             const COMMON_LIGATURES = 0x02,
             const NO_COMMON_LIGATURES = 0x04,
@@ -1813,17 +1797,16 @@ macro_rules! exclusive_value {
 
 <%helpers:longhand name="font-variant-numeric" products="gecko" animation_value_type="discrete"
                    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER"
                    spec="https://drafts.csswg.org/css-fonts/#propdef-font-variant-numeric">
     use properties::longhands::system_font::SystemFont;
     use std::fmt;
     use style_traits::ToCss;
 
-    no_viewport_percentage!(SpecifiedValue);
 
     bitflags! {
         #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
         pub flags VariantNumeric: u8 {
             const NORMAL = 0,
             const LINING_NUMS = 0x01,
             const OLDSTYLE_NUMS = 0x02,
             const PROPORTIONAL_NUMS = 0x04,
@@ -1976,17 +1959,16 @@ macro_rules! exclusive_value {
     use properties::longhands::system_font::SystemFont;
     use values::generics::FontSettings;
 
     #[derive(Clone, Debug, PartialEq, ToCss)]
     pub enum SpecifiedValue {
         Value(computed_value::T),
         System(SystemFont)
     }
-    no_viewport_percentage!(SpecifiedValue);
 
     <%self:simple_system_boilerplate name="font_feature_settings"></%self:simple_system_boilerplate>
 
     pub mod computed_value {
         use values::generics::{FontSettings, FontSettingTagInt};
         pub type T = FontSettings<FontSettingTagInt>;
     }
 
@@ -2018,17 +2000,16 @@ https://drafts.csswg.org/css-fonts-4/#lo
                    spec="${variation_spec}">
     use values::computed::ComputedValueAsSpecified;
     use values::generics::FontSettings;
 
     impl ComputedValueAsSpecified for SpecifiedValue {}
 
     pub type SpecifiedValue = computed_value::T;
 
-    no_viewport_percentage!(SpecifiedValue);
 
 
     pub mod computed_value {
         use values::generics::{FontSettings, FontSettingTagFloat};
         pub type T = FontSettings<FontSettingTagFloat>;