Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 02 Jun 2016 12:00:53 +0200
changeset 341090 58ac3bd12914f8d2d683176139e5630b4e32c2ca
parent 341089 1b67bc89fe9ab5278c735b251a66130ee82d862c (current diff)
parent 341071 34a8be4346a9231e472fc36b1d7c0531e0fbf7c5 (diff)
child 341091 b6ba2b9fbe8a89a569222914c6990a31cfbd9b56
push id1183
push userraliiev@mozilla.com
push dateMon, 05 Sep 2016 20:01:49 +0000
treeherdermozilla-release@3148731bed45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to fx-team
dom/browser-element/mochitest/browserElement_ActiveStateChangeOnChangingMutedOrVolume.js
dom/browser-element/mochitest/file_browserElement_ActiveStateChangeOnChangingMutedOrVolume.html
dom/browser-element/mochitest/test_browserElement_inproc_ActiveStateChangeOnChangingMutedOrVolume.html
dom/browser-element/mochitest/test_browserElement_oop_ActiveStateChangeOnChangingMutedOrVolume.html
dom/push/test/xpcshell/test_handler_service_parent.js
netwerk/dns/mdns/libmdns/MulticastDNSFallback.jsm
--- a/accessible/base/MarkupMap.h
+++ b/accessible/base/MarkupMap.h
@@ -29,16 +29,20 @@ MARKUPMAP(aside,
 MARKUPMAP(blockquote,
           New_HyperText,
           roles::SECTION)
 
 MARKUPMAP(dd,
           New_HTMLDefinition,
           roles::DEFINITION)
 
+MARKUPMAP(details,
+          New_HyperText,
+          roles::DETAILS)
+
 MARKUPMAP(div,
           nullptr,
           roles::SECTION)
 
 MARKUPMAP(dl,
           New_HTMLList,
           roles::DEFINITION_LIST)
 
@@ -308,16 +312,20 @@ MARKUPMAP(q,
           New_HyperText,
           0)
 
 MARKUPMAP(section,
           New_HyperText,
           roles::SECTION,
           Attr(xmlroles, region))
 
+MARKUPMAP(summary,
+          New_HTMLSummary,
+          roles::SUMMARY)
+
 MARKUPMAP(time,
           New_HyperText,
           0,
           Attr(xmlroles, time),
           AttrFromDOM(datetime, datetime))
 
 MARKUPMAP(td,
           New_HTMLTableHeaderCellIfScope,
--- a/accessible/base/Role.h
+++ b/accessible/base/Role.h
@@ -967,17 +967,27 @@ enum Role {
   RADIO_GROUP = 165,
 
   /**
    * A text container exposing brief amount of information. See related
    * TEXT_CONTAINER role.
    */
   TEXT = 166,
 
-  LAST_ROLE = TEXT
+  /**
+   * The html:details element.
+   */
+  DETAILS = 167,
+
+  /**
+   * The html:summary element.
+   */
+  SUMMARY = 168,
+
+  LAST_ROLE = SUMMARY
 };
 
 } // namespace role
 
 typedef enum mozilla::a11y::roles::Role role;
 
 } // namespace a11y
 } // namespace mozilla
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -1347,8 +1347,24 @@ ROLE(RADIO_GROUP,
 ROLE(TEXT,
      "text",
      ATK_ROLE_STATIC,
      NSAccessibilityGroupRole,
      USE_ROLE_STRING,
      IA2_ROLE_TEXT_FRAME,
      eNameFromSubtreeIfReqRule)
 
+ROLE(DETAILS,
+     "details",
+     ATK_ROLE_PANEL,
+     NSAccessibilityGroupRole,
+     ROLE_SYSTEM_GROUPING,
+     ROLE_SYSTEM_GROUPING,
+     eNoNameRule)
+
+ROLE(SUMMARY,
+     "summary",
+     ATK_ROLE_PUSH_BUTTON,
+     NSAccessibilityGroupRole,
+     ROLE_SYSTEM_PUSHBUTTON,
+     ROLE_SYSTEM_PUSHBUTTON,
+     eNameFromSubtreeRule)
+
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -196,16 +196,19 @@ static Accessible* New_HTMLLabel(nsICont
   { return new HTMLLabelAccessible(aContent, aContext->Document()); }
 
 static Accessible* New_HTMLOutput(nsIContent* aContent, Accessible* aContext)
   { return new HTMLOutputAccessible(aContent, aContext->Document()); }
 
 static Accessible* New_HTMLProgress(nsIContent* aContent, Accessible* aContext)
   { return new HTMLProgressMeterAccessible(aContent, aContext->Document()); }
 
+static Accessible* New_HTMLSummary(nsIContent* aContent, Accessible* aContext)
+  { return new HTMLSummaryAccessible(aContent, aContext->Document()); }
+
 static Accessible*
 New_HTMLTableAccessible(nsIContent* aContent, Accessible* aContext)
   { return new HTMLTableAccessible(aContent, aContext->Document()); }
 
 static Accessible*
 New_HTMLTableRowAccessible(nsIContent* aContent, Accessible* aContext)
   { return new HTMLTableRowAccessible(aContent, aContext->Document()); }
 
--- a/accessible/html/HTMLElementAccessibles.cpp
+++ b/accessible/html/HTMLElementAccessibles.cpp
@@ -9,16 +9,18 @@
 #include "nsAccUtils.h"
 #include "nsIPersistentProperties2.h"
 #include "nsTextEquivUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
 #include "mozilla/dom/HTMLLabelElement.h"
+#include "mozilla/dom/HTMLDetailsElement.h"
+#include "mozilla/dom/HTMLSummaryElement.h"
 
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLHRAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 role
@@ -111,8 +113,92 @@ Relation
 HTMLOutputAccessible::RelationByType(RelationType aType)
 {
   Relation rel = AccessibleWrap::RelationByType(aType);
   if (aType == RelationType::CONTROLLED_BY)
     rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::_for));
 
   return rel;
 }
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSummaryAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+HTMLSummaryAccessible::
+  HTMLSummaryAccessible(nsIContent* aContent, DocAccessible* aDoc) :
+  HyperTextAccessibleWrap(aContent, aDoc)
+{
+  mGenericTypes |= eButton;
+}
+
+uint8_t
+HTMLSummaryAccessible::ActionCount()
+{
+  return 1;
+}
+
+void
+HTMLSummaryAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
+{
+  if (aIndex != eAction_Click) {
+    return;
+  }
+
+  dom::HTMLSummaryElement* summary = dom::HTMLSummaryElement::FromContent(mContent);
+  if (!summary) {
+    return;
+  }
+
+  dom::HTMLDetailsElement* details = summary->GetDetails();
+  if (!details) {
+    return;
+  }
+
+  if (details->Open()) {
+    aName.AssignLiteral("collapse");
+  } else {
+    aName.AssignLiteral("expand");
+  }
+}
+
+bool
+HTMLSummaryAccessible::DoAction(uint8_t aIndex)
+{
+  if (aIndex != eAction_Click)
+    return false;
+
+  DoCommand();
+  return true;
+}
+
+uint64_t
+HTMLSummaryAccessible::NativeState()
+{
+  uint64_t state = HyperTextAccessibleWrap::NativeState();
+
+  dom::HTMLSummaryElement* summary = dom::HTMLSummaryElement::FromContent(mContent);
+  if (!summary) {
+    return state;
+  }
+
+  dom::HTMLDetailsElement* details = summary->GetDetails();
+  if (!details) {
+    return state;
+  }
+
+  if (details->Open()) {
+    state |= states::EXPANDED;
+  } else {
+    state |= states::COLLAPSED;
+  }
+
+  return state;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// HTMLSummaryAccessible: Widgets
+
+bool
+HTMLSummaryAccessible::IsWidget() const
+{
+  return true;
+}
--- a/accessible/html/HTMLElementAccessibles.h
+++ b/accessible/html/HTMLElementAccessibles.h
@@ -87,12 +87,35 @@ public:
 
   // Accessible
   virtual Relation RelationByType(RelationType aType) override;
 
 protected:
   virtual ~HTMLOutputAccessible() {}
 };
 
+/**
+ * Accessible for the HTML summary element.
+ */
+class HTMLSummaryAccessible : public HyperTextAccessibleWrap
+{
+
+public:
+  enum { eAction_Click = 0 };
+
+  HTMLSummaryAccessible(nsIContent* aContent, DocAccessible* aDoc);
+
+  // Accessible
+  virtual uint64_t NativeState() override;
+
+  // ActionAccessible
+  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;
+};
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/interfaces/nsIAccessibleRole.idl
+++ b/accessible/interfaces/nsIAccessibleRole.idl
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 /**
  * Defines cross platform (Gecko) roles.
  */
-[scriptable, uuid(da0c7824-147c-11e5-917c-60a44c717042)]
+[scriptable, uuid(05a9f33f-dcfd-4e7b-b825-138ba784c1f5)]
 interface nsIAccessibleRole : nsISupports
 {
   /**
    * Used when accessible hans't strong defined role.
    */
   const unsigned long ROLE_NOTHING = 0;
 
   /**
@@ -960,9 +960,21 @@ interface nsIAccessibleRole : nsISupport
    */
   const unsigned long ROLE_RADIO_GROUP = 165;
 
   /**
    * A text container exposing brief amount of information. See related
    * TEXT_CONTAINER role.
    */
   const unsigned long ROLE_TEXT = 166;
+
+  /**
+   * A text container exposing brief amount of information. See related
+   * DETAILS role.
+   */
+  const unsigned long ROLE_DETAILS = 167;
+
+  /**
+   * A text container exposing brief amount of information. See related
+   * SUMMARY role.
+   */
+  const unsigned long ROLE_SUMMARY = 168;
 };
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -939,16 +939,22 @@ ConvertToNSArray(nsTArray<ProxyAccessibl
       return @"AXApplicationAlert";
 
     case roles::SEPARATOR:
       return @"AXContentSeparator";
 
     case roles::PROPERTYPAGE:
       return @"AXTabPanel";
 
+    case roles::DETAILS:
+      return @"AXDetails";
+
+    case roles::SUMMARY:
+      return @"AXSummary";
+
     default:
       break;
   }
 
   return nil;
 }
 
 struct RoleDescrMap
--- a/accessible/tests/mochitest/elm/test_HTMLSpec.html
+++ b/accessible/tests/mochitest/elm/test_HTMLSpec.html
@@ -367,19 +367,45 @@
         children: [
           { role: ROLE_TEXT_LEAF }, // plain text
           { role: ROLE_TEXT_LEAF } // HTML:del text
         ]
       };
       testElm("del_container", obj);
 
       //////////////////////////////////////////////////////////////////////////
-      // HTML:details
+      // HTML:details with open state
+
+      obj = {
+        role: ROLE_DETAILS,
+        children: [
+          {
+            role: ROLE_SUMMARY,
+            states: STATE_EXPANDED,
+            actions: "collapse"
+         },
+         { role: ROLE_PARAGRAPH }
+        ]
+      };
+      testElm("details", obj);
 
-      ok(isAccessible("details"), "details element is not accessible");
+      //////////////////////////////////////////////////////////////////////////
+      // HTML:details with closed (default) state
+
+      obj = {
+        role: ROLE_DETAILS,
+        children: [
+          {
+            role: ROLE_SUMMARY,
+            states: STATE_COLLAPSED,
+            actions: "expand"
+         }
+        ]
+      };
+      testElm("details_closed", obj);
 
       //////////////////////////////////////////////////////////////////////////
       // HTML:dfn contained by paragraph
 
       obj = {
         role: ROLE_PARAGRAPH,
         textAttrs: {
           0: { "font-style": "italic" },
@@ -1398,16 +1424,21 @@
 
   <p id="del_container">normal<del>Removed</del></p>
 
   <details id="details" open="open">
     <summary>Information</summary>
     <p>If your browser supports this element, it should allow you to expand and collapse these details.</p>
   </details>
 
+  <details id="details_closed">
+    <summary>Information</summary>
+    <p>If your browser supports this element, it should allow you to expand and collapse these details.</p>
+  </details>
+
   <p id="dfn_container"><dfn id="def-internet">The Internet</dfn> is a global
     system of interconnected networks that use the Internet Protocol Suite (TCP/IP)
     to serve billions of users worldwide.</p>
 
   <dialog id="dialog" open="true">This is a dialog</dialog>
 
   <div id="div">div</div>
 
--- a/accessible/tests/mochitest/role.js
+++ b/accessible/tests/mochitest/role.js
@@ -14,16 +14,17 @@ const ROLE_CHECKBUTTON = nsIAccessibleRo
 const ROLE_CHECK_MENU_ITEM = nsIAccessibleRole.ROLE_CHECK_MENU_ITEM;
 const ROLE_CHROME_WINDOW = nsIAccessibleRole.ROLE_CHROME_WINDOW;
 const ROLE_COMBOBOX = nsIAccessibleRole.ROLE_COMBOBOX;
 const ROLE_COMBOBOX_LIST = nsIAccessibleRole.ROLE_COMBOBOX_LIST;
 const ROLE_COMBOBOX_OPTION = nsIAccessibleRole.ROLE_COMBOBOX_OPTION;
 const ROLE_COLUMNHEADER = nsIAccessibleRole.ROLE_COLUMNHEADER;
 const ROLE_DEFINITION = nsIAccessibleRole.ROLE_DEFINITION;
 const ROLE_DEFINITION_LIST = nsIAccessibleRole.ROLE_DEFINITION_LIST;
+const ROLE_DETAILS = nsIAccessibleRole.ROLE_DETAILS;
 const ROLE_DIAGRAM = nsIAccessibleRole.ROLE_DIAGRAM;
 const ROLE_DIALOG = nsIAccessibleRole.ROLE_DIALOG;
 const ROLE_DOCUMENT = nsIAccessibleRole.ROLE_DOCUMENT;
 const ROLE_EMBEDDED_OBJECT = nsIAccessibleRole.ROLE_EMBEDDED_OBJECT;
 const ROLE_ENTRY = nsIAccessibleRole.ROLE_ENTRY;
 const ROLE_EQUATION = nsIAccessibleRole.ROLE_EQUATION;
 const ROLE_FIGURE = nsIAccessibleRole.ROLE_FIGURE;
 const ROLE_FOOTER = nsIAccessibleRole.ROLE_FOOTER;
@@ -100,16 +101,17 @@ const ROLE_ROW = nsIAccessibleRole.ROLE_
 const ROLE_ROWHEADER = nsIAccessibleRole.ROLE_ROWHEADER;
 const ROLE_SCROLLBAR = nsIAccessibleRole.ROLE_SCROLLBAR;
 const ROLE_SECTION = nsIAccessibleRole.ROLE_SECTION;
 const ROLE_SEPARATOR = nsIAccessibleRole.ROLE_SEPARATOR;
 const ROLE_SLIDER = nsIAccessibleRole.ROLE_SLIDER;
 const ROLE_SPINBUTTON = nsIAccessibleRole.ROLE_SPINBUTTON;
 const ROLE_STATICTEXT = nsIAccessibleRole.ROLE_STATICTEXT;
 const ROLE_STATUSBAR = nsIAccessibleRole.ROLE_STATUSBAR;
+const ROLE_SUMMARY = nsIAccessibleRole.ROLE_SUMMARY;
 const ROLE_SWITCH = nsIAccessibleRole.ROLE_SWITCH;
 const ROLE_TABLE = nsIAccessibleRole.ROLE_TABLE;
 const ROLE_TERM = nsIAccessibleRole.ROLE_TERM;
 const ROLE_TEXT = nsIAccessibleRole.ROLE_TEXT;
 const ROLE_TEXT_CONTAINER = nsIAccessibleRole.ROLE_TEXT_CONTAINER;
 const ROLE_TEXT_LEAF = nsIAccessibleRole.ROLE_TEXT_LEAF;
 const ROLE_TOGGLE_BUTTON = nsIAccessibleRole.ROLE_TOGGLE_BUTTON;
 const ROLE_TOOLBAR = nsIAccessibleRole.ROLE_TOOLBAR;
--- a/b2g/components/PresentationRequestUIGlue.js
+++ b/b2g/components/PresentationRequestUIGlue.js
@@ -18,17 +18,21 @@ Cu.import("resource://gre/modules/Servic
 XPCOMUtils.defineLazyModuleGetter(this, "SystemAppProxy",
                                   "resource://gre/modules/SystemAppProxy.jsm");
 
 function PresentationRequestUIGlue() { }
 
 PresentationRequestUIGlue.prototype = {
 
   sendRequest: function(aUrl, aSessionId, aDevice) {
-    let localDevice = aDevice.QueryInterface(Ci.nsIPresentationLocalDevice);
+    let localDevice;
+    try {
+      localDevice = aDevice.QueryInterface(Ci.nsIPresentationLocalDevice);
+    } catch (e) {}
+
     if (localDevice) {
       return this.sendTo1UA(aUrl, aSessionId, localDevice.windowId);
     } else {
       return this.sendTo2UA(aUrl, aSessionId);
     }
   },
 
   // For 1-UA scenario
--- a/browser/base/content/test/general/browser_audioTabIcon.js
+++ b/browser/base/content/test/general/browser_audioTabIcon.js
@@ -1,19 +1,24 @@
 const PAGE = "https://example.com/browser/browser/base/content/test/general/file_mediaPlayback.html";
 
 function* wait_for_tab_playing_event(tab, expectPlaying) {
-  yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
-    if (event.detail.changed.indexOf("soundplaying") >= 0) {
-      is(tab.hasAttribute("soundplaying"), expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
-      is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
-      return true;
-    }
-    return false;
-  });
+  if (tab.soundPlaying == expectPlaying) {
+    ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
+    return true;
+  } else {
+    yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
+      if (event.detail.changed.indexOf("soundplaying") >= 0) {
+        is(tab.hasAttribute("soundplaying"), expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
+        is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
+        return true;
+      }
+      return false;
+    });
+  }
 }
 
 function* play(tab) {
   let browser = tab.linkedBrowser;
   yield ContentTask.spawn(browser, {}, function* () {
     let audio = content.document.querySelector("audio");
     audio.play();
   });
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -3,18 +3,18 @@
 
 // This test checks that a <select> with an <optgroup> opens and can be navigated
 // in a child process. This is different than single-process as a <menulist> is used
 // to implement the dropdown list.
 
 const XHTML_DTD = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
 
 const PAGECONTENT =
-  "<html xmlns='http://www.w3.org/1999/xhtml'>" + 
-  "<body onload='gChangeEvents = 0; document.body.firstChild.focus()'><select onchange='gChangeEvents++'>" +
+  "<html xmlns='http://www.w3.org/1999/xhtml'>" +
+  "<body onload='gChangeEvents = 0;gInputEvents = 0; document.body.firstChild.focus()'><select oninput='gInputEvents++' onchange='gChangeEvents++'>" +
   "  <optgroup label='First Group'>" +
   "    <option value='One'>One</option>" +
   "    <option value='Two'>Two</option>" +
   "  </optgroup>" +
   "  <option value='Three'>Three</option>" +
   "  <optgroup label='Second Group' disabled='true'>" +
   "    <option value='Four'>Four</option>" +
   "    <option value='Five'>Five</option>" +
@@ -69,16 +69,23 @@ function hideSelectPopup(selectPopup, wi
   }
   else {
     EventUtils.synthesizeKey("KEY_Enter", { code: "Enter" });
   }
 
   return popupHiddenPromise;
 }
 
+function getInputEvents()
+{
+  return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
+    return content.wrappedJSObject.gInputEvents;
+  });
+}
+
 function getChangeEvents()
 {
   return ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     return content.wrappedJSObject.gChangeEvents;
   });
 }
 
 function doSelectTests(contentType, dtd)
@@ -106,56 +113,62 @@ function doSelectTests(contentType, dtd)
   EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
   is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3");
   is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
 
   EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
 
   // On Windows, one can navigate on disabled menuitems
   let expectedIndex = isWindows ? 5 : 9;
-     
+
   is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(expectedIndex),
      "Skip optgroup header and disabled items select item 7");
   is(menulist.selectedIndex, isWindows ? 5 : 1, "Select or skip disabled item selectedIndex");
 
   for (let i = 0; i < 10; i++) {
     is(menulist.getItemAtIndex(i).disabled, i >= 4 && i <= 7, "item " + i + " disabled")
   }
 
   EventUtils.synthesizeKey("KEY_ArrowUp", { code: "ArrowUp" });
   is(menulist.menuBoxObject.activeChild, menulist.getItemAtIndex(3), "Select item 3 again");
   is(menulist.selectedIndex, isWindows ? 3 : 1, "Select item 3 selectedIndex");
 
+  is((yield getInputEvents()), 0, "Before closed - number of input events");
   is((yield getChangeEvents()), 0, "Before closed - number of change events");
 
   EventUtils.synthesizeKey("a", { accelKey: true });
   yield ContentTask.spawn(gBrowser.selectedBrowser, { isWindows }, function(args) {
     Assert.equal(String(content.getSelection()), args.isWindows ? "Text" : "",
       "Select all while popup is open");
   });
 
   yield hideSelectPopup(selectPopup);
 
   is(menulist.selectedIndex, 3, "Item 3 still selected");
+  is((yield getInputEvents()), 1, "After closed - number of input events");
   is((yield getChangeEvents()), 1, "After closed - number of change events");
 
   // Opening and closing the popup without changing the value should not fire a change event.
   yield openSelectPopup(selectPopup, true);
   yield hideSelectPopup(selectPopup, true);
+  is((yield getInputEvents()), 1, "Open and close with no change - number of input events");
   is((yield getChangeEvents()), 1, "Open and close with no change - number of change events");
   EventUtils.synthesizeKey("VK_TAB", { });
   EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+  is((yield getInputEvents()), 1, "Tab away from select with no change - number of input events");
   is((yield getChangeEvents()), 1, "Tab away from select with no change - number of change events");
 
   yield openSelectPopup(selectPopup, true);
   EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
   yield hideSelectPopup(selectPopup, true);
+  is((yield getInputEvents()), isWindows ? 2 : 1, "Open and close with change - number of input events");
   is((yield getChangeEvents()), isWindows ? 2 : 1, "Open and close with change - number of change events");
   EventUtils.synthesizeKey("VK_TAB", { });
   EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
+  is((yield getInputEvents()), isWindows ? 2 : 1, "Tab away from select with change - number of input events");
   is((yield getChangeEvents()), isWindows ? 2 : 1, "Tab away from select with change - number of change events");
 
   is(selectPopup.lastChild.previousSibling.label, "Seven", "Spaces collapsed");
   is(selectPopup.lastChild.label, "\xA0\xA0Eight\xA0\xA0", "Non-breaking spaces not collapsed");
 
   yield BrowserTestUtils.removeTab(tab);
 }
 
@@ -180,17 +193,17 @@ add_task(function*() {
   yield openSelectPopup(selectPopup, true, "#one");
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
     content.document.body.removeChild(content.document.getElementById("two"));
   });
 
   // Wait a bit just to make sure the popup won't close.
   yield new Promise(resolve => setTimeout(resolve, 1000));
-  
+
   is(selectPopup.state, "open", "Different popup did not affect open popup");
 
   yield hideSelectPopup(selectPopup);
 
   // Next, try it when the same <select> element than the one that is open is removed
   yield openSelectPopup(selectPopup, true, "#three");
 
   let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
--- a/browser/base/content/test/plugins/browser_private_clicktoplay.js
+++ b/browser/base/content/test/plugins/browser_private_clicktoplay.js
@@ -13,21 +13,24 @@ var gPrivateBrowser = null;
 
 function pageLoad(aEvent) {
   // The plugin events are async dispatched and can come after the load event
   // This just allows the events to fire before we then go on to test the states
   executeSoon(gNextTest);
   gNextTest = null;
 }
 
-function prepareTest(nextTest, url, window) {
+function prepareTest(nextTest, url, browser) {
   gNextTest = nextTest;
-  if (!window)
-    window = gTestBrowser.contentWindow;
-  window.location = url;
+  if (!browser)
+    browser = gTestBrowser;
+
+  ContentTask.spawn(browser, url, function(url){
+    content.location = url;
+  });
 }
 
 function finishTest() {
   clearAllPluginPermissions();
   gTestBrowser.removeEventListener("load", pageLoad, true);
   gBrowser.removeCurrentTab();
   if (gPrivateWindow) {
     gPrivateWindow.close();
@@ -38,17 +41,17 @@ function finishTest() {
 
 function createPrivateWindow(nextTest, url) {
   gPrivateWindow = OpenBrowserWindow({private: true});
   ok(!!gPrivateWindow, "should have created a private window.");
   whenDelayedStartupFinished(gPrivateWindow, function() {
     gPrivateBrowser = gPrivateWindow.getBrowser().selectedBrowser;
     gPrivateBrowser.addEventListener("load", pageLoad, true);
     gNextTest = function() {
-      prepareTest(nextTest, url, gPrivateBrowser.contentWindow);
+      prepareTest(nextTest, url, gPrivateBrowser);
     };
   });
 }
 
 function whenDelayedStartupFinished(aWindow, aCallback) {
   Services.obs.addObserver(function observer(aSubject, aTopic) {
     if (aWindow == aSubject) {
       Services.obs.removeObserver(observer, aTopic);
@@ -187,17 +190,17 @@ function test3c() {
     // Check the button status
     let promiseShown = BrowserTestUtils.waitForEvent(gPrivateWindow.PopupNotifications.panel,
                                                      "Shown");
     popupNotification.reshow();
     promiseShown.then(() => {
       let buttonContainer = gPrivateWindow.PopupNotifications.panel.firstChild._buttonContainer;
       ok(buttonContainer.hidden, "Test 3c, Activated plugin in a private window should not have visible buttons");
 
-      prepareTest(test3d, gHttpTestRoot + "plugin_two_types.html", gPrivateBrowser.contentWindow);
+      prepareTest(test3d, gHttpTestRoot + "plugin_two_types.html", gPrivateBrowser);
     });
   });
 }
 
 function test3d() {
   let popupNotification = gPrivateWindow.PopupNotifications.getNotification("click-to-play-plugins", gPrivateBrowser);
   ok(popupNotification, "Test 3d, Should have a click-to-play notification");
 
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -1141,16 +1141,17 @@ if (Services.prefs.getBoolPref("privacy.
       ContextualIdentityService.getIdentities().forEach(identity => {
         let bundle = doc.getElementById("bundle_browser");
         let label = bundle.getString(identity.label);
 
         let item = doc.createElementNS(kNSXUL, "toolbarbutton");
         item.setAttribute("label", label);
         item.setAttribute("usercontextid", identity.userContextId);
         item.setAttribute("class", "subviewbutton");
+        item.setAttribute("image", identity.icon);
 
         fragment.appendChild(item);
       });
 
       items.appendChild(fragment);
     }
   });
 }
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -46,50 +46,16 @@ function getMigrationBundle() {
   if (!gMigrationBundle) {
     gMigrationBundle = Services.strings.createBundle(
      "chrome://browser/locale/migration/migration.properties");
   }
   return gMigrationBundle;
 }
 
 /**
- * Figure out what is the default browser, and if there is a migrator
- * for it, return that migrator's internal name.
- * For the time being, the "internal name" of a migrator is its contract-id
- * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie),
- * but it will soon be exposed properly.
- */
-function getMigratorKeyForDefaultBrowser() {
-  // Canary uses the same description as Chrome so we can't distinguish them.
-  const APP_DESC_TO_KEY = {
-    "Internet Explorer":                 "ie",
-    "Safari":                            "safari",
-    "Firefox":                           "firefox",
-    "Google Chrome":                     "chrome",  // Windows, Linux
-    "Chrome":                            "chrome",  // OS X
-    "Chromium":                          "chromium", // Windows, OS X
-    "Chromium Web Browser":              "chromium", // Linux
-    "360\u5b89\u5168\u6d4f\u89c8\u5668": "360se",
-  };
-
-  let browserDesc = "";
-  try {
-    let browserDesc =
-      Cc["@mozilla.org/uriloader/external-protocol-service;1"].
-      getService(Ci.nsIExternalProtocolService).
-      getApplicationDescription("http");
-    return APP_DESC_TO_KEY[browserDesc] || "";
-  }
-  catch(ex) {
-    Cu.reportError("Could not detect default browser: " + ex);
-  }
-  return "";
-}
-
-/**
  * Shared prototype for migrators, implementing nsIBrowserProfileMigrator.
  *
  * To implement a migrator:
  * 1. Import this module.
  * 2. Create the prototype for the migrator, extending MigratorPrototype.
  *    Namely: MosaicMigrator.prototype = Object.create(MigratorPrototype);
  * 3. Set classDescription, contractID and classID for your migrator, and set
  *    NSGetFactory appropriately.
@@ -531,16 +497,50 @@ this.MigrationUtils = Object.freeze({
       this._migrators.set(aKey, migrator);
     }
 
     try {
       return migrator && migrator.sourceExists ? migrator : null;
     } catch (ex) { Cu.reportError(ex); return null }
   },
 
+  /**
+   * Figure out what is the default browser, and if there is a migrator
+   * for it, return that migrator's internal name.
+   * For the time being, the "internal name" of a migrator is its contract-id
+   * trailer (e.g. ie for @mozilla.org/profile/migrator;1?app=browser&type=ie),
+   * but it will soon be exposed properly.
+   */
+  getMigratorKeyForDefaultBrowser() {
+    // Canary uses the same description as Chrome so we can't distinguish them.
+    const APP_DESC_TO_KEY = {
+      "Internet Explorer":                 "ie",
+      "Safari":                            "safari",
+      "Firefox":                           "firefox",
+      "Google Chrome":                     "chrome",  // Windows, Linux
+      "Chrome":                            "chrome",  // OS X
+      "Chromium":                          "chromium", // Windows, OS X
+      "Chromium Web Browser":              "chromium", // Linux
+      "360\u5b89\u5168\u6d4f\u89c8\u5668": "360se",
+    };
+
+    let browserDesc = "";
+    try {
+      let browserDesc =
+        Cc["@mozilla.org/uriloader/external-protocol-service;1"].
+        getService(Ci.nsIExternalProtocolService).
+        getApplicationDescription("http");
+      return APP_DESC_TO_KEY[browserDesc] || "";
+    }
+    catch(ex) {
+      Cu.reportError("Could not detect default browser: " + ex);
+    }
+    return "";
+  },
+
   // Whether or not we're in the process of startup migration
   get isStartupMigration() {
     return gProfileStartup != null;
   },
 
   /**
    * In the case of startup migration, this is set to the nsIProfileStartup
    * instance passed to ProfileMigrator's migrate.
@@ -671,17 +671,17 @@ this.MigrationUtils = Object.freeze({
         this.finishMigration();
         throw new Error("startMigration was asked to open auto-migrate from " +
                         "a non-existent source: " + aMigratorKey);
       }
       migratorKey = aMigratorKey;
       skipSourcePage = true;
     }
     else {
-      let defaultBrowserKey = getMigratorKeyForDefaultBrowser();
+      let defaultBrowserKey = this.getMigratorKeyForDefaultBrowser();
       if (defaultBrowserKey) {
         migrator = this.getMigrator(defaultBrowserKey);
         if (migrator)
           migratorKey = defaultBrowserKey;
       }
     }
 
     if (!migrator) {
--- a/browser/components/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -29,16 +29,17 @@ var MigrationWizard = {
     os.addObserver(this, "Migration:ItemError", false);
     os.addObserver(this, "Migration:Ended", false);
 
     this._wiz = document.documentElement;
 
     let args = window.arguments;
     let entryPointId = args[0] || MigrationUtils.MIGRATION_ENTRYPOINT_UNKNOWN;
     Services.telemetry.getHistogramById("FX_MIGRATION_ENTRY_POINT").add(entryPointId);
+    this.isInitialMigration = entryPointId == MigrationUtils.MIGRATION_ENTRYPOINT_FIRSTRUN;
 
     if (args.length > 1) {
       this._source = args[1];
       this._migrator = args[2] instanceof kIMig ?  args[2] : null;
       this._autoMigrate = args[3].QueryInterface(kIPStartup);
       this._skipImportSourcePage = args[4];
       if (this._migrator && args[5]) {
         let sourceProfiles = this._migrator.sourceProfiles;
@@ -80,31 +81,42 @@ var MigrationWizard = {
       document.getElementById("closeSourceBrowser").style.visibility = visibility;
     }
     this._wiz.canRewind = false;
 
     var selectedMigrator = null;
 
     // Figure out what source apps are are available to import from:
     var group = document.getElementById("importSourceGroup");
+    var availableMigratorCount = 0;
     for (var i = 0; i < group.childNodes.length; ++i) {
       var migratorKey = group.childNodes[i].id;
       if (migratorKey != "nothing") {
         var migrator = MigrationUtils.getMigrator(migratorKey);
         if (migrator) {
           // Save this as the first selectable item, if we don't already have
           // one, or if it is the migrator that was passed to us.
           if (!selectedMigrator || this._source == migratorKey)
             selectedMigrator = group.childNodes[i];
+          availableMigratorCount++;
         } else {
           // Hide this option
           group.childNodes[i].hidden = true;
         }
       }
     }
+    if (this.isInitialMigration) {
+      Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_BROWSER_COUNT")
+        .add(availableMigratorCount);
+      let defaultBrowser = MigrationUtils.getMigratorKeyForDefaultBrowser();
+      // This will record 0 for unknown default browser IDs.
+      defaultBrowser = MigrationUtils.getSourceIdForTelemetry(defaultBrowser);
+      Services.telemetry.getHistogramById("FX_STARTUP_MIGRATION_EXISTING_DEFAULT_BROWSER")
+        .add(defaultBrowser);
+    }
 
     group.addEventListener("command", toggleCloseBrowserWarning);
 
     if (selectedMigrator) {
       group.selectedItem = selectedMigrator;
       toggleCloseBrowserWarning();
     } else {
       // We didn't find a migrator, notify the user
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -117,16 +117,21 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
                                   "resource:///modules/ContentCrashHandlers.jsm");
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
                                     "resource:///modules/ContentCrashHandlers.jsm");
+  XPCOMUtils.defineLazyModuleGetter(this, "CrashSubmit",
+                                    "resource://gre/modules/CrashSubmit.jsm");
+  XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+                                    "resource://gre/modules/PluralForm.jsm");
+
 }
 
 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
   return Services.strings.createBundle('chrome://branding/locale/brand.properties');
 });
 
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings.createBundle('chrome://browser/locale/browser.properties');
@@ -821,16 +826,70 @@ BrowserGlue.prototype = {
       let acceptableAge = Services.prefs.getIntPref("app.update.checkInstallTime.days") * millisecondsIn24Hours;
 
       if (buildDate + acceptableAge < today) {
         Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService).checkForBackgroundUpdates();
       }
     }
   },
 
+  checkForPendingCrashReports: function() {
+    // We don't process crash reports older than 28 days, so don't bother submitting them
+    const PENDING_CRASH_REPORT_DAYS = 28;
+    if (AppConstants.MOZ_CRASHREPORTER) {
+      let dateLimit = new Date();
+      dateLimit.setDate(dateLimit.getDate() - PENDING_CRASH_REPORT_DAYS);
+      CrashSubmit.pendingIDsAsync(dateLimit).then(
+        function onSuccess(ids) {
+          let count = ids.length;
+          if (count) {
+            let win = RecentWindow.getMostRecentBrowserWindow();
+            let nb =  win.document.getElementById("global-notificationbox");
+            let notification = nb.getNotificationWithValue("pending-crash-reports");
+            if (notification) {
+              return;
+            }
+            let buttons = [
+              {
+                label: win.gNavigatorBundle.getString("pendingCrashReports.submitAll"),
+                callback: function() {
+                  ids.forEach(function(id) {
+                    CrashSubmit.submit(id, {extraExtraKeyVals: {"SubmittedFromInfobar": true}});
+                  });
+                }
+              },
+              {
+                label: win.gNavigatorBundle.getString("pendingCrashReports.ignoreAll"),
+                callback: function() {
+                  ids.forEach(function(id) {
+                    CrashSubmit.ignore(id);
+                  });
+                }
+              },
+              {
+                label: win.gNavigatorBundle.getString("pendingCrashReports.viewAll"),
+                callback: function() {
+                  win.openUILinkIn("about:crashes", "tab");
+                }
+              }
+            ];
+            nb.appendNotification(PluralForm.get(count,
+                                                 win.gNavigatorBundle.getString("pendingCrashReports.label")).replace("#1", count),
+                                  "pending-crash-reports",
+                                  "chrome://browser/skin/tab-crashed.svg",
+                                  nb.PRIORITY_INFO_HIGH, buttons);
+          }
+        },
+        function onError(err) {
+          Cu.reportError(err);
+        }
+      );
+    }
+  },
+
   _onSafeModeRestart: function BG_onSafeModeRestart() {
     // prompt the user to confirm
     let strings = gBrowserBundle;
     let promptTitle = strings.GetStringFromName("safeModeRestartPromptTitle");
     let promptMessage = strings.GetStringFromName("safeModeRestartPromptMessage");
     let restartText = strings.GetStringFromName("safeModeRestartButton");
     let buttonFlags = (Services.prompt.BUTTON_POS_0 *
                        Services.prompt.BUTTON_TITLE_IS_STRING) +
@@ -1071,16 +1130,20 @@ BrowserGlue.prototype = {
         if (removalSuccessful && uninstalledValue == "True") {
           this._resetProfileNotification("uninstall");
         }
       }
     }
 
     this._checkForOldBuildUpdates();
 
+    if ("release" != AppConstants.MOZ_UPDATE_CHANNEL) {
+      this.checkForPendingCrashReports();
+    }
+
     this._firstWindowTelemetry(aWindow);
     this._firstWindowLoaded();
   },
 
   /**
    * Application shutdown handler.
    */
   _onQuitApplicationGranted: function () {
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/linux64/asan-tc.manifest
@@ -0,0 +1,27 @@
+[
+{
+"version": "gcc 4.8.5 + PR64905",
+"size": 80160264,
+"digest": "c1a9dc9da289b8528874d16300b9d13a997cec99195bb0bc46ff665216d8535d6d6cb5af6b4b1f2749af6815dab12e703fdb3849014e5c23a70eff351a0baf4e",
+"algorithm": "sha512",
+"filename": "gcc.tar.xz",
+"unpack": true
+},
+{
+"version": "clang 3.9.0/r262971",
+"size": 117866656,
+"visibility": "public",
+"digest": "5529b4549e39838faf0f276fb5d1d9cf9972295a8fb2451e25b0490e4a94822bff9646d723cc3137bfc2eb7e7bcff8a25a331b881ec6ca0b73e29fd7dbb972f5",
+"algorithm": "sha512",
+"filename": "clang.tar.xz",
+ "unpack": true
+ },
+{
+"size": 12072532,
+"digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
+"algorithm": "sha512",
+"filename": "gtk3.tar.xz",
+"setup": "setup.sh",
+"unpack": true
+}
+]
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -744,16 +744,24 @@ certErrorDetailsHSTS.label = HTTP Strict
 certErrorDetailsKeyPinning.label = HTTP Public Key Pinning: %S
 certErrorDetailsCertChain.label = Certificate chain:
 
 # LOCALIZATION NOTE (tabgroups.migration.anonGroup):
 # %S is the group number/ID
 tabgroups.migration.anonGroup = Group %S
 tabgroups.migration.tabGroupBookmarkFolderName = Bookmarked Tab Groups
 
+# LOCALIZATION NOTE (pendingCrashReports.label): Semi-colon list of plural forms
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of pending crash reports
+pendingCrashReports.label = You have an unsubmitted crash report;You have #1 unsubmitted crash reports
+pendingCrashReports.viewAll = View
+pendingCrashReports.submitAll = Submit
+pendingCrashReports.ignoreAll = Ignore
+
 decoder.noCodecs.button = Learn how
 decoder.noCodecs.accesskey = L
 decoder.noCodecs.message = To play video, you may need to install Microsoft’s Media Feature Pack.
 decoder.noCodecsVista.message = To play video, you may need to install Microsoft’s Platform Update Supplement for Windows Vista.
 decoder.noCodecsXP.message = To play video, you may need to enable Adobe’s Primetime Content Decryption Module.
 decoder.noCodecsLinux.message = To play video, you may need to install the required video codecs.
 decoder.noHWAcceleration.message = To improve video quality, you may need to install Microsoft’s Media Feature Pack.
 decoder.noHWAccelerationVista.message = To improve video quality, you may need to install Microsoft’s Platform Update Supplement for Windows Vista.
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -59,16 +59,22 @@
   content: "";
   display: -moz-box;
   height: 2px;
   margin-top: -2px;
   position: relative;
   z-index: 2; /* navbar is at 1 */
 }
 
+@media (-moz-mac-yosemite-theme) {
+  #navigator-toolbox::after {
+    background-image: linear-gradient(to top, hsla(0,0%,0%,.1), hsla(0,0%,0%,.1) 1px, hsla(0,0%,100%,0) 1px, hsla(0,0%,100%,0) 2px, transparent 3px);
+  }
+}
+
 #navigator-toolbox toolbarbutton:-moz-lwtheme {
   color: inherit;
   text-shadow: inherit;
 }
 
 #main-window {
   -moz-appearance: none;
   background-color: #eeeeee;
@@ -143,16 +149,20 @@ toolbarseparator {
   background: url(chrome://browser/skin/Toolbar-background-noise.png) hsl(0,0%,83%);
 }
 
 /* remove noise texture on Yosemite */
 @media (-moz-mac-yosemite-theme) {
   #navigator-toolbox > toolbar:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
     background-image: none;
   }
+
+  #navigator-toolbox > toolbar:-moz-window-inactive:not(#TabsToolbar):not(#nav-bar):not(:-moz-lwtheme) {
+    background-color: hsl(0,0%,95%);
+  }
 }
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar) {
   overflow: -moz-hidden-unscrollable;
   max-height: 4em;
   transition: min-height 170ms ease-out, max-height 170ms ease-out;
 }
 
@@ -185,16 +195,20 @@ toolbarseparator {
   }
 }
 
 /* remove noise texture on Yosemite */
 @media (-moz-mac-yosemite-theme) {
   #nav-bar {
     background: linear-gradient(hsl(0,0%,93%), hsl(0,0%,83%));
   }
+
+  #nav-bar:-moz-window-inactive {
+    background: linear-gradient(hsl(0,0%,97%), hsl(0,0%,95%));
+  }
 }
 
 /* Draw the bottom border of the tabs toolbar when it's not using
    -moz-appearance: toolbar. */
 #main-window:-moz-any([sizemode="fullscreen"],[customize-entered]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #main-window:not([tabsintitlebar]) #TabsToolbar:not([collapsed="true"]) + #nav-bar,
 #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-lwtheme {
   border-top: 1px solid hsla(0,0%,0%,.3);
@@ -211,16 +225,20 @@ toolbarseparator {
   #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:not(:-moz-lwtheme) {
     border-top: 1px solid hsla(0,0%,0%,.2);
     background-clip: padding-box;
     margin-top: calc(-1 * var(--navbar-tab-toolbar-highlight-overlap));
     /* Position the toolbar above the bottom of background tabs */
     position: relative;
     z-index: 1;
   }
+
+  #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-window-inactive:not(:-moz-lwtheme) {
+    border-top-color: hsla(0,0%,0%,.05);
+  }
 }
 
 #nav-bar-customization-target {
   padding: 4px;
 }
 
 #PersonalToolbar {
   padding: 0 4px 4px;
@@ -2478,16 +2496,100 @@ toolbarbutton.chevron > .toolbarbutton-m
     list-style-image: url("chrome://browser/skin/tabbrowser/connecting@2x.png");
   }
 
   .tab-throbber[progress] {
     list-style-image: url("chrome://global/skin/icons/loading@2x.png");
   }
 }
 
+@media (-moz-mac-yosemite-theme) {
+  /* image preloading hack from shared/tabs.inc.css */
+  #tabbrowser-tabs::before {
+    background-image:
+      url(chrome://browser/skin/yosemite/tab-selected-end-inactive.svg),
+      url(chrome://browser/skin/yosemite/tab-selected-start-inactive.svg),
+      url(chrome://browser/skin/yosemite/tab-stroke-start-inactive.png),
+      url(chrome://browser/skin/yosemite/tab-active-middle-inactive.png),
+      url(chrome://browser/skin/yosemite/tab-stroke-end-inactive.png),
+      url(chrome://browser/skin/tabbrowser/tab-selected-end.svg),
+      url(chrome://browser/skin/tabbrowser/tab-selected-start.svg),
+      url(chrome://browser/skin/tabbrowser/tab-stroke-end.png),
+      url(chrome://browser/skin/tabbrowser/tab-active-middle.png),
+      url(chrome://browser/skin/tabbrowser/tab-stroke-start.png),
+      url(chrome://browser/skin/tabbrowser/tab-background-end.png),
+      url(chrome://browser/skin/tabbrowser/tab-background-middle.png),
+      url(chrome://browser/skin/tabbrowser/tab-background-start.png);
+  }
+
+  .tab-background-middle[visuallyselected=true]:-moz-window-inactive {
+    background-image: url(chrome://browser/skin/yosemite/tab-active-middle-inactive.png),
+                      @fgTabTextureYosemiteInactive@,
+                      none;
+  }
+
+  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+    background-image: url(chrome://browser/skin/yosemite/tab-stroke-start-inactive.png);
+  }
+
+  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+    background-image: url(chrome://browser/skin/yosemite/tab-stroke-end-inactive.png);
+  }
+
+  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+    background-image: url(chrome://browser/skin/yosemite/tab-selected-start-inactive.svg);
+    background-size: 100% 100%;
+  }
+
+  .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr):not(:-moz-lwtheme)::before,
+  .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl):not(:-moz-lwtheme)::before {
+    background-image: url(chrome://browser/skin/yosemite/tab-selected-end-inactive.svg);
+    background-size: 100% 100%;
+  }
+
+  @media (min-resolution: 2dppx) {
+    /* image preloading hack from shared/tabs.inc.css */
+    #tabbrowser-tabs::before {
+      background-image:
+        url(chrome://browser/skin/yosemite/tab-selected-end-inactive.svg),
+        url(chrome://browser/skin/yosemite/tab-selected-start-inactive.svg),
+        url(chrome://browser/skin/yosemite/tab-stroke-start-inactive@2x.png),
+        url(chrome://browser/skin/yosemite/tab-active-middle-inactive@2x.png),
+        url(chrome://browser/skin/yosemite/tab-stroke-end-inactive@2x.png),
+        url(chrome://browser/skin/tabbrowser/tab-selected-end.svg),
+        url(chrome://browser/skin/tabbrowser/tab-selected-start.svg),
+        url(chrome://browser/skin/tabbrowser/tab-stroke-end@2x.png),
+        url(chrome://browser/skin/tabbrowser/tab-active-middle@2x.png),
+        url(chrome://browser/skin/tabbrowser/tab-stroke-start@2x.png),
+        url(chrome://browser/skin/tabbrowser/tab-background-end@2x.png),
+        url(chrome://browser/skin/tabbrowser/tab-background-middle@2x.png),
+        url(chrome://browser/skin/tabbrowser/tab-background-start@2x.png);
+    }
+
+    .tab-background-middle[visuallyselected=true]:-moz-window-inactive {
+      background-image: url(chrome://browser/skin/yosemite/tab-active-middle-inactive@2x.png),
+                        @fgTabTextureYosemiteInactive@,
+                        none;
+    }
+
+    .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+    .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+      background-image: url(chrome://browser/skin/yosemite/tab-stroke-start-inactive@2x.png);
+    }
+
+    .tab-background-end[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(ltr)::after,
+    .tab-background-start[visuallyselected=true]:-moz-window-inactive:-moz-locale-dir(rtl)::after {
+      background-image: url(chrome://browser/skin/yosemite/tab-stroke-end-inactive@2x.png);
+    }
+  }
+}
+
 .tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([visuallyselected="true"]) {
   opacity: .9;
 }
 
 /*
  * Force the overlay to create a new stacking context so it always appears on
  * top of the icon.
  */
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -226,16 +226,24 @@ browser.jar:
   skin/classic/browser/yosemite/menuPanel-help.png                     (menuPanel-help-yosemite.png)
   skin/classic/browser/yosemite/menuPanel-help@2x.png                  (menuPanel-help-yosemite@2x.png)
   skin/classic/browser/yosemite/menuPanel-small.png                    (menuPanel-small-yosemite.png)
   skin/classic/browser/yosemite/menuPanel-small@2x.png                 (menuPanel-small-yosemite@2x.png)
   skin/classic/browser/yosemite/reload-stop-go.png                     (reload-stop-go-yosemite.png)
   skin/classic/browser/yosemite/reload-stop-go@2x.png                  (reload-stop-go-yosemite@2x.png)
   skin/classic/browser/yosemite/sync-horizontalbar.png                 (sync-horizontalbar-yosemite.png)
   skin/classic/browser/yosemite/sync-horizontalbar@2x.png              (sync-horizontalbar-yosemite@2x.png)
+  skin/classic/browser/yosemite/tab-selected-end-inactive.svg          (tabbrowser/tab-selected-end-yosemite-inactive.svg)
+  skin/classic/browser/yosemite/tab-selected-start-inactive.svg        (tabbrowser/tab-selected-start-yosemite-inactive.svg)
+  skin/classic/browser/yosemite/tab-active-middle-inactive.png         (tabbrowser/tab-active-middle-yosemite-inactive.png)
+  skin/classic/browser/yosemite/tab-active-middle-inactive@2x.png      (tabbrowser/tab-active-middle-yosemite-inactive@2x.png)
+  skin/classic/browser/yosemite/tab-stroke-end-inactive.png            (tabbrowser/tab-stroke-end-yosemite-inactive.png)
+  skin/classic/browser/yosemite/tab-stroke-end-inactive@2x.png         (tabbrowser/tab-stroke-end-yosemite-inactive@2x.png)
+  skin/classic/browser/yosemite/tab-stroke-start-inactive.png          (tabbrowser/tab-stroke-start-yosemite-inactive.png)
+  skin/classic/browser/yosemite/tab-stroke-start-inactive@2x.png       (tabbrowser/tab-stroke-start-yosemite-inactive@2x.png)
 #ifdef E10S_TESTING_ONLY
   skin/classic/browser/e10s-64@2x.png (../shared/e10s-64@2x.png)
 #endif
 
 [extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
 % override chrome://browser/skin/feeds/audioFeedIcon.png                   chrome://browser/skin/feeds/feedIcon.png
 % override chrome://browser/skin/feeds/audioFeedIcon16.png                 chrome://browser/skin/feeds/feedIcon16.png
 % override chrome://browser/skin/feeds/videoFeedIcon.png                   chrome://browser/skin/feeds/feedIcon.png
@@ -254,10 +262,10 @@ browser.jar:
 % override chrome://browser/skin/menuPanel-help.png                        chrome://browser/skin/yosemite/menuPanel-help.png                       os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-help@2x.png                     chrome://browser/skin/yosemite/menuPanel-help@2x.png                    os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-small.png                       chrome://browser/skin/yosemite/menuPanel-small.png                      os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-small@2x.png                    chrome://browser/skin/yosemite/menuPanel-small@2x.png                   os=Darwin osversion>=10.10
 % override chrome://browser/skin/preferences/checkbox.png                  chrome://browser/skin/yosemite/preferences/checkbox.png                 os=Darwin osversion>=10.10
 % override chrome://browser/skin/preferences/checkbox@2x.png               chrome://browser/skin/yosemite/preferences/checkbox@2x.png              os=Darwin osversion>=10.10
 % override chrome://browser/skin/reload-stop-go.png                        chrome://browser/skin/yosemite/reload-stop-go.png                       os=Darwin osversion>=10.10
 % override chrome://browser/skin/reload-stop-go@2x.png                     chrome://browser/skin/yosemite/reload-stop-go@2x.png                    os=Darwin osversion>=10.10
-% override chrome://browser/skin/sync-horizontalbar.png                    chrome://browser/skin/yosemite/sync-horizontalbar.png                   os=Darwin osversion>=10.10
-% override chrome://browser/skin/sync-horizontalbar@2x.png                 chrome://browser/skin/yosemite/sync-horizontalbar@2x.png                os=Darwin osversion>=10.10
+% override chrome://browser/skin/sync-horizontalbar.png                    chrome://browser/skin/yosemite/sync-horizontalbar.png               os=Darwin osversion>=10.10
+% override chrome://browser/skin/sync-horizontalbar@2x.png                 chrome://browser/skin/yosemite/sync-horizontalbar@2x.png            os=Darwin osversion>=10.10
--- a/browser/themes/osx/shared.inc
+++ b/browser/themes/osx/shared.inc
@@ -1,12 +1,13 @@
 %include ../../../toolkit/themes/osx/global/shared.inc
 %include ../shared/browser.inc
 
 %filter substitution
 
-%define fgTabTexture linear-gradient(transparent 2px, hsla(0,0%,100%,.6) 2px, hsla(0,0%,100%,.6) 3px, hsl(0,0%,99%) 3px, hsl(0,0%,93%))
+%define fgTabTexture linear-gradient(transparent 2px, hsl(0,0%,99%) 2px, hsl(0,0%,93%))
+%define fgTabTextureYosemiteInactive linear-gradient(transparent 2px, hsl(0,0%,99%) 2px, hsl(0,0%,97%))
 %define toolbarColorLWT rgba(253,253,253,0.45)
 %define fgTabTextureLWT linear-gradient(transparent 2px, rgba(254,254,254,.72) 2px, @toolbarColorLWT@)
 %define fgTabBackgroundColor transparent
 %define hudButton -moz-appearance: none; color: #434343; border-radius: 4px; border: 1px solid #b5b5b5; background: linear-gradient(#fff, #f2f2f2); box-shadow: inset 0 1px rgba(255,255,255,.8), inset 0 0 1px rgba(255,255, 255,.25), 0 1px rgba(255,255,255,.3); background-clip: padding-box; background-origin: padding-box; padding: 2px 6px;
 %define hudButtonPressed box-shadow: inset 0 1px 4px -3px #000, 0 1px rgba(255,255,255,.3);
 %define hudButtonFocused box-shadow: 0 0 1px -moz-mac-focusring inset, 0 0 4px 1px -moz-mac-focusring, 0 0 2px 1px -moz-mac-focusring;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9f74c4e7654d18ca9b811c778ebf8586ba525dcd
GIT binary patch
literal 78
zc%17D@N?(olHy`uVBq!ia0vp^Y(Ol}!2%@nWJ)FgDN#=s$B+ufWCzwPj}P@yOhy-Y
bdzctXJvg10Jp088RKVcr>gTe~DWM4fDHRb@
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..50961db7ec08f990d349ab94ebe4296cddd72368
GIT binary patch
literal 89
zc%17D@N?(olHy`uVBq!ia0vp^JV0#6!2%?=o-;ZHq!c_|978G?lNFd(B)0ER{kI%M
kzTk2@s3F0+I6;Jg;kAw`r|rIrB|x<dp00i_>zopr0EOolmjD0&
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/tabbrowser/tab-selected-end-yosemite-inactive.svg
@@ -0,0 +1,28 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="30px" height="31px" preserveAspectRatio="none">
+  <defs>
+    <style>
+      #tab-background-fill {
+        background-color: transparent;
+        background-image: linear-gradient(transparent, transparent 2px, hsl(0,0%,99%) 2px, hsl(0,0%,97%));
+        background-repeat: no-repeat;
+        height: 100%;
+        width: 100%;
+      }
+    </style>
+
+    <clipPath id="tab-curve-clip-path-end" clipPathUnits="objectBoundingBox">
+      <path d="m 0,0.0625 -0.05,0 0,0.938 1,0 0,-0.028 C 0.67917542,0.95840561 0.56569036,0.81970962 0.51599998,0.5625 0.48279998,0.3905 0.465,0.0659 0,0.0625 z"/>
+    </clipPath>
+
+    <clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
+      <path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
+    </clipPath>
+  </defs>
+
+  <foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-end)">
+    <div id="tab-background-fill" xmlns="http://www.w3.org/1999/xhtml"></div>
+  </foreignObject>
+</svg>
new file mode 100644
--- /dev/null
+++ b/browser/themes/osx/tabbrowser/tab-selected-start-yosemite-inactive.svg
@@ -0,0 +1,28 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="30px" height="31px" preserveAspectRatio="none">
+  <defs>
+    <style>
+      #tab-background-fill {
+        background-color: transparent;
+        background-image: linear-gradient(transparent, transparent 2px, hsl(0,0%,99%) 2px, hsl(0,0%,97%));
+        background-repeat: no-repeat;
+        height: 100%;
+        width: 100%;
+      }
+    </style>
+
+    <clipPath id="tab-curve-clip-path-start" clipPathUnits="objectBoundingBox">
+      <path d="m 1,0.0625 0.05,0 0,0.938 -1,0 0,-0.028 C 0.32082458,0.95840561 0.4353096,0.81970962 0.48499998,0.5625 0.51819998,0.3905 0.535,0.0659 1,0.0625 z"/>
+    </clipPath>
+
+    <clipPath id="tab-hover-clip-path" clipPathUnits="objectBoundingBox">
+      <path d="M 0,0.2 0,1 1,1, 1,0.2 z"/>
+    </clipPath>
+  </defs>
+
+  <foreignObject width="30" height="31" clip-path="url(#tab-curve-clip-path-start)">
+    <div id="tab-background-fill" xmlns="http://www.w3.org/1999/xhtml"></div>
+  </foreignObject>
+</svg>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4206f0c7dca0188d886a6a10f4a9ba0a6c7767dc
GIT binary patch
literal 339
zc$@)K0j&OsP)<h;3K|Lk000e1NJLTq0015U0018d1ONa4T4lA20003RNkl<Zcmbu<
zGed?^6bJBswr$(CZQGolJ-6ms!=ArIV_wphZNK5vS9s3-%~wYkV%*^hXV}34`cQ#9
z)UIfLYB;J`&zp%0^@TOG<!x6dU9uuiN}}3>Vks1s(1d)XOpa8zo$ufUL-3IMU1hu<
zjJVqkZ%cf<p~Ky7u$K>OaFOFg4EyQ8b)tp`z<&Bj6m-G07Ap|<yOW70l)yu(ong=h
z*J2b*z_k(p8*rW3!v$QV{om!P;2Md59=MhxU<R&TKREECHpvZu8o1`-1g7geOlZ2M
zVqpxf`vg~kPf2GlU&DhvBIK}N=-|cwQJwFq2BS3{U<O^NLs@p>DE+Z_SK9C2%vm&J
l)c&b}0w|5jDVLH+`wyzbPouYMMS}nU002ovPDHLkV1j2in@#`#
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..21ac892703cffc3bb16e4c63e7735984b598f23c
GIT binary patch
literal 718
zc$@*w0x|uGP)<h;3K|Lk000e1NJLTq002Ay002G+1ONa4-951K0007&Nkl<Zcmd_u
z1B{(P5CGtL#@V*5n&)@6`J6On!^Tb#)s4!q4(gyb;@UROH{E~g#`tH8nQWYVYr%gQ
z=^|4h1ri|+A|V8P!N3Rqo=Kt#exOs;MK5~Cog?-a0}r~cG##v7F|Q{7dj(=3Kvnqn
zd^>oiVa2?JEW+a<SUuc`77W5@Ove(e#@;Xdxa(q*yM*vK@B@xOf6NvYa47t(j-DdI
zqd)^Zfd<ULHuO$<PSmGnQ>Pn%FEAcEl~MN=3px!<oU|5m(LU+kw1~Q#lcrG@oxY<~
z2Cc(V^lnk;M9>3o-~#k4n@t-Fu+t-sihMywaKjp`zuQS8ZQ!QO*tbrZ-_iWt9W!F5
z4aLe&mmaK^GsP?;gLHNo`RNi`(c%2C=~<V66M#Q(A)VDe{BX#z8UaTFe>`uXYoUbB
zE^H{Evx*(|-&Q8zAmEQHHBz=tfS2<GoFZeClj-trkg#>GnlE5q;E(;v^a_;#UtS>P
zlmd8NC~JDfFUk|No*%Z-^zsA#I9eg#FbVLjg#wO~0FUMSWe8fsKgy9yN(5}-hxNNE
zByA$MswIu`_^i}yIY&h_J!yjZVeO3;0mlM=Oq9;elk=8}J6J8}{iuE49g->M##yqh
zOir7vBZIW4{3QI2oT;ZDAsu_0(Y|z+obJx~V5j>?6#tJ<<CEUhY@#*X-QEABhv+!4
z@X}#ue>L#+hdUDVoOC$a$KCrY{W1xU0$)BlQ2F70J-B8eU5b>9fl%-VD;PlsS~Y1s
z&O$eOKiDt!?qBE#17CrfMH;~Xdcrm8;Q?5#;OHH3-mE;=d#>_o<+c37AteTaRiEVr
z*n%Fx|HuJRp&YcZ{E1;a7Go*aV;2sIQl`M40U(9}#4OR73;+NC07*qoM6N<$f^w8q
Ao&W#<
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2acbac438c6aea4c4f3c4b0a2efb859a5f8945d7
GIT binary patch
literal 339
zc$@)K0j&OsP)<h;3K|Lk000e1NJLTq0015U0018d1ONa4T4lA20003RNkl<ZcmciC
zLt@+k6bA5bY}ajU+qP{xtJ%lc#K{R}AKxOI7@OEhH?`fqUSa;P_+8-x{wE3axDN-o
z!X2IvK#fP?{<egzg6pbxP2XCn6LwPKfi?WNl<Fykyy+onNe(+1oqYBRshFHe41r9E
z0ne++6AWrU-<M@%0?VU=0y2QbI2=vR;8A!zO$U>9G=|JzdbE}RCXvPavkREi-4P@O
zlVvdmY>!uhz~*-D1SV->0I9*Gn~DP4-Q_5-IUNLmNtPePFQ$2pUre3XiwH28Z3(0W
zi^kpebs|{Q3a^tw(tyF_<a1O^P7pv0IkdQ|bef5MRYR^2K(=^yC+}$R!1}hIG6g*)
lsVRl>sgg1%=v@&6`3>EePmwbnyd?kt002ovPDHLkV1ikcku(4R
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a12fc39a7b10829cffefb0f736eebd8a545ef19f
GIT binary patch
literal 716
zc$@*u0yF)IP)<h;3K|Lk000e1NJLTq002Ay002G+1ONa4-951K0007$Nkl<ZcmeI$
zGnjQ(6vpv)Y};OA+qUhQ>ne4+^i5F3slqO9;wCn_*tW*==I&VKch>pz+x_#u2ls5i
z5*LLa8c9e;4)RfiVh9A53PKFhQ2_a$VVO8&{Sg_gxuWC2kymCuT={Zq&vEFYk3Lu;
z5xFLqSAIpuGgIGf)A!F|O*jTQChBQ?aputdduD?pY@{%g35;MU&bR<%nCM#l+e<V-
zr$1Q1SnJ>2Tu5=n6O+eYe{_MR41t0$lt6q9=81Bdxlj@Yf!x${?4g5I3{=hD(MNNG
zW1%J(`H)*owvRzjlm)r0m0os1Rhs!S?K!oxArQ!W%ba8YRAoZ0>){5&p(@gRF!mhG
zg0d{ggCp#NvQRVs>}@jw%2Hwey^T<p2l;%3VNe!j=3I6|T{`5kF(yG>0pyD*s0)Kk
z)v^xi;vu*7GY;x9ATN)?28EFC+o3K5GFHPvsEdYN)yYVxONQLv4;y4bUhaYoiXh+A
zL0vGUr<%D?7h~6}l>&Km5H`qxyxk3T0g#x|gBqxdfLzkVbXz_icMUSwmM8qih%M7Y
zam8T^)Fnc0>}Dp^WkcSaV<418LfWg@2Xz@Rd!-y3R+cekKd<QD=uDWEWupyqQgOwP
zJKD2S5b`0<OtQ;{+k9yY9opBMVb&Z!I#{VY4Q809`{_ET8K5}{=9uh9Yuwbk&A~|j
z?ezKUEjpN@wcpQxm=DJNLx&zZm<EdkA_$>~L@bh#1%Zi{Ueo=}Z>P`k*1S9>8jKKx
zBHTYB5$#O5zx&1YA07Uvhb;`Y@M;@F9!`3#=GCF+hMrr0YiQ5qPnzg}g<Ee%u+ksc
yqVI3R%Q@CD5{9MWey(tbE1YB>8<@*5Sne0?D*?YIK~4t%0000<MNUMnLSTZFxl?ig
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -1445,16 +1445,21 @@ toolbaritem[overflowedItem=true],
   -moz-appearance: none;
   margin-inline-end: 3px;
 }
 
 menuitem[checked="true"].subviewbutton > .menu-iconic-left {
   visibility: hidden;
 }
 
+#PanelUI-containersItems > .subviewbutton > .toolbarbutton-icon {
+  width: 16px;
+  height: 16px;
+}
+
 .panel-mainview[panelid=customizationui-widget-panel],
 #customizationui-widget-multiview > .panel-viewcontainer,
 #customizationui-widget-multiview > .panel-viewcontainer > .panel-viewstack,
 #PanelUI-panicView > .panel-subview-body,
 #PanelUI-panicView {
   overflow: visible;
 }
 
--- a/build/buildconfig.py
+++ b/build/buildconfig.py
@@ -1,17 +1,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import imp
 import os
 import sys
 
-path = os.path.dirname(__file__)
+path = os.path.dirname(sys.executable)
 while not os.path.exists(os.path.join(path, 'config.status')):
     parent = os.path.normpath(os.path.join(path, os.pardir))
     if parent == path:
         raise Exception("Can't find config.status")
     path = parent
 
 path = os.path.join(path, 'config.status')
 config = imp.load_module('_buildconfig', open(path), path, ('', 'r', imp.PY_SOURCE))
--- a/build/gecko_templates.mozbuild
+++ b/build/gecko_templates.mozbuild
@@ -66,18 +66,16 @@ def GeckoBinary(linkage='dependent', msv
             LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS']
             if mozglue == 'program':
                 USE_LIBS += ['mozglue']
                 if CONFIG['MOZ_GLUE_IN_PROGRAM']:
                     if CONFIG['GNU_CC']:
                         LDFLAGS += ['-rdynamic']
                     if CONFIG['MOZ_MEMORY']:
                         USE_LIBS += ['memory']
-                    if CONFIG['MOZ_LINKER']:
-                        OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
             elif mozglue == 'library':
                 if not CONFIG['MOZ_GLUE_IN_PROGRAM']:
                     USE_LIBS += ['mozglue']
             else:
                 error('`mozglue` must be "program" or "library"')
 
     if not CONFIG['JS_STANDALONE']:
         USE_LIBS += [
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -228,21 +228,18 @@ def bootstrap(topsrcdir, mozilla_dir=Non
     # Global build system and mach state is stored in a central directory. By
     # default, this is ~/.mozbuild. However, it can be defined via an
     # environment variable. We detect first run (by lack of this directory
     # existing) and notify the user that it will be created. The logic for
     # creation is much simpler for the "advanced" environment variable use
     # case. For default behavior, we educate users and give them an opportunity
     # to react. We always exit after creating the directory because users don't
     # like surprises.
-    try:
-        import mach.main
-    except ImportError:
-        sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
-        import mach.main
+    sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
+    import mach.main
 
     def telemetry_handler(context, data):
         # We have not opted-in to telemetry
         if 'BUILD_SYSTEM_TELEMETRY' not in os.environ:
             return
 
         telemetry_dir = os.path.join(get_state_dir()[0], 'telemetry')
         try:
--- a/build/virtualenv_packages.txt
+++ b/build/virtualenv_packages.txt
@@ -18,17 +18,16 @@ macholib.pth:python/macholib
 mock.pth:python/mock-1.0.0
 mozilla.pth:build
 mozilla.pth:config
 mozilla.pth:xpcom/typelib/xpt/tools
 mozilla.pth:dom/bindings
 mozilla.pth:dom/bindings/parser
 mozilla.pth:layout/tools/reftest
 moztreedocs.pth:tools/docs
-copy:build/buildconfig.py
 packages.txt:testing/mozbase/packages.txt
 objdir:build
 gyp.pth:media/webrtc/trunk/tools/gyp/pylib
 pyasn1.pth:python/pyasn1
 pyasn1_modules.pth:python/pyasn1-modules
 bitstring.pth:python/bitstring
 redo.pth:python/redo
 requests.pth:python/requests
--- a/dom/animation/AnimationEffectTiming.cpp
+++ b/dom/animation/AnimationEffectTiming.cpp
@@ -129,20 +129,24 @@ AnimationEffectTiming::SetDirection(cons
   PostSpecifiedTimingUpdated(mEffect);
 }
 
 void
 AnimationEffectTiming::SetEasing(JSContext* aCx,
                                  const nsAString& aEasing,
                                  ErrorResult& aRv)
 {
+  nsIDocument* document = AnimationUtils::GetCurrentRealmDocument(aCx);
+  if (!document) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
   Maybe<ComputedTimingFunction> newFunction =
-    TimingParams::ParseEasing(aEasing,
-                              AnimationUtils::GetCurrentRealmDocument(aCx),
-                              aRv);
+    TimingParams::ParseEasing(aEasing, document, aRv);
   if (aRv.Failed()) {
     return;
   }
 
   if (mTiming.mFunction == newFunction) {
     return;
   }
 
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -514,45 +514,36 @@ EffectCompositor::UpdateCascadeResults(E
 }
 
 /* static */ Maybe<NonOwningAnimationTarget>
 EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame)
 {
   // Always return the same object to benefit from return-value optimization.
   Maybe<NonOwningAnimationTarget> result;
 
+  CSSPseudoElementType pseudoType =
+    aFrame->StyleContext()->GetPseudoType();
+
+  if (pseudoType != CSSPseudoElementType::NotPseudo &&
+      pseudoType != CSSPseudoElementType::before &&
+      pseudoType != CSSPseudoElementType::after) {
+    return result;
+  }
+
   nsIContent* content = aFrame->GetContent();
   if (!content) {
     return result;
   }
 
-  CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
-
-  if (aFrame->IsGeneratedContentFrame()) {
-    nsIFrame* parent = aFrame->GetParent();
-    if (parent->IsGeneratedContentFrame()) {
-      return result;
-    }
-    nsIAtom* name = content->NodeInfo()->NameAtom();
-    if (name == nsGkAtoms::mozgeneratedcontentbefore) {
-      pseudoType = CSSPseudoElementType::before;
-    } else if (name == nsGkAtoms::mozgeneratedcontentafter) {
-      pseudoType = CSSPseudoElementType::after;
-    } else {
-      return result;
-    }
+  if (pseudoType == CSSPseudoElementType::before ||
+      pseudoType == CSSPseudoElementType::after) {
     content = content->GetParent();
     if (!content) {
       return result;
     }
-  } else {
-    if (nsLayoutUtils::GetStyleFrame(content) != aFrame) {
-      // The effects associated with an element are for its primary frame.
-      return result;
-    }
   }
 
   if (!content->IsElement()) {
     return result;
   }
 
   result.emplace(content->AsElement(), pseudoType);
 
--- a/dom/animation/EffectSet.cpp
+++ b/dom/animation/EffectSet.cpp
@@ -44,52 +44,28 @@ EffectSet::GetEffectSet(dom::Element* aE
 {
   nsIAtom* propName = GetEffectSetPropertyAtom(aPseudoType);
   return static_cast<EffectSet*>(aElement->GetProperty(propName));
 }
 
 /* static */ EffectSet*
 EffectSet::GetEffectSet(const nsIFrame* aFrame)
 {
-  nsIContent* content = aFrame->GetContent();
-  if (!content) {
+  Maybe<NonOwningAnimationTarget> target =
+    EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame);
+
+  if (!target) {
     return nullptr;
   }
 
-  nsIAtom* propName;
-  if (aFrame->IsGeneratedContentFrame()) {
-    nsIFrame* parent = aFrame->GetParent();
-    if (parent->IsGeneratedContentFrame()) {
-      return nullptr;
-    }
-    nsIAtom* name = content->NodeInfo()->NameAtom();
-    if (name == nsGkAtoms::mozgeneratedcontentbefore) {
-      propName = nsGkAtoms::animationEffectsForBeforeProperty;
-    } else if (name == nsGkAtoms::mozgeneratedcontentafter) {
-      propName = nsGkAtoms::animationEffectsForAfterProperty;
-    } else {
-      return nullptr;
-    }
-    content = content->GetParent();
-    if (!content) {
-      return nullptr;
-    }
-  } else {
-    if (nsLayoutUtils::GetStyleFrame(content) != aFrame) {
-      // The effects associated with an element are for its primary frame.
-      return nullptr;
-    }
-    propName = nsGkAtoms::animationEffectsProperty;
-  }
-
-  if (!content->MayHaveAnimations()) {
+  if (!target->mElement->MayHaveAnimations()) {
     return nullptr;
   }
 
-  return static_cast<EffectSet*>(content->GetProperty(propName));
+  return GetEffectSet(target->mElement, target->mPseudoType);
 }
 
 /* static */ EffectSet*
 EffectSet::GetOrCreateEffectSet(dom::Element* aElement,
                                 CSSPseudoElementType aPseudoType)
 {
   EffectSet* effectSet = GetEffectSet(aElement, aPseudoType);
   if (effectSet) {
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -81,8 +81,9 @@ skip-if = buildapp == 'mulet'
 skip-if = buildapp == 'mulet'
 [mozilla/test_deferred_start.html]
 skip-if = (toolkit == 'gonk' && debug)
 [mozilla/test_disabled_properties.html]
 [mozilla/test_hide_and_show.html]
 [mozilla/test_partial_keyframes.html]
 [style/test_animation-seeking-with-current-time.html]
 [style/test_animation-seeking-with-start-time.html]
+[sandbox/test_set-easing.html]
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/sandbox/test_set-easing.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<head>
+<meta charset=utf-8>
+<title>Tests AnimationTimingFunction::SetEasing in sandbox</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/SpawnTask.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+
+<script>
+"use strict";
+
+add_task(function* set_easing() {
+  const div = document.createElement("div");
+  document.body.appendChild(div);
+  div.animate({ opacity: [0, 1] }, 100000 );
+
+  const contentScript = function() {
+    doesThrow(() => {
+      document.getAnimations()[0].effect.timing.easing = "linear";
+    }, "AnimationTimingFunction::SetEasing should throw in sandbox.");
+  };
+
+  const sandbox = new SpecialPowers.Cu.Sandbox(window);
+  sandbox.importFunction(document, "document");
+  sandbox.importFunction(SimpleTest.doesThrow, "doesThrow");
+  SpecialPowers.Cu.evalInSandbox(`(${contentScript.toSource()})()`, sandbox);
+});
+
+</script>
+</body>
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -220,18 +220,18 @@ AudioChannelAgent::NotifyStartedPlaying(
   service->RegisterAudioChannelAgent(this,
     static_cast<AudioChannelService::AudibleState>(aAudible));
 
   AudioPlaybackConfig config = service->GetMediaConfig(mWindow,
                                                        mAudioChannelType);
 
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, NotifyStartedPlaying, this = %p, "
-          "mute = %d, volume = %f, suspend = %d\n", this,
-          config.mMuted, config.mVolume, config.mSuspend));
+          "audible = %d, mute = %d, volume = %f, suspend = %d\n", this,
+          aAudible, config.mMuted, config.mVolume, config.mSuspend));
 
   aConfig->SetConfig(config.mVolume, config.mMuted, config.mSuspend);
   mIsRegToService = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 AudioChannelAgent::NotifyStoppedPlaying()
@@ -249,29 +249,31 @@ AudioChannelAgent::NotifyStoppedPlaying(
     service->UnregisterAudioChannelAgent(this);
   }
 
   mIsRegToService = false;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-AudioChannelAgent::NotifyStartedAudible(bool aAudible)
+AudioChannelAgent::NotifyStartedAudible(bool aAudible, uint32_t aReason)
 {
   MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelAgent, NotifyStartedAudible, this = %p, "
-          "audible = %d\n", this, aAudible));
+          "audible = %d, reason = %d\n", this, aAudible, aReason));
 
   RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
   if (NS_WARN_IF(!service)) {
     return NS_ERROR_FAILURE;
   }
 
   service->AudioAudibleChanged(
-    this, static_cast<AudioChannelService::AudibleState>(aAudible));
+    this,
+    static_cast<AudioChannelService::AudibleState>(aAudible),
+    static_cast<AudioChannelService::AudibleChangedReasons>(aReason));
   return NS_OK;
 }
 
 already_AddRefed<nsIAudioChannelAgentCallback>
 AudioChannelAgent::GetCallback()
 {
   nsCOMPtr<nsIAudioChannelAgentCallback> callback = mCallback;
   if (!callback) {
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -100,42 +100,62 @@ bool
 IsParentProcess()
 {
   return XRE_GetProcessType() == GeckoProcessType_Default;
 }
 
 class AudioPlaybackRunnable final : public Runnable
 {
 public:
-  AudioPlaybackRunnable(nsPIDOMWindowOuter* aWindow, bool aActive)
+  AudioPlaybackRunnable(nsPIDOMWindowOuter* aWindow, bool aActive,
+                        AudioChannelService::AudibleChangedReasons aReason)
     : mWindow(aWindow)
     , mActive(aActive)
+    , mReason(aReason)
   {}
 
  NS_IMETHOD Run()
  {
     nsCOMPtr<nsIObserverService> observerService =
       services::GetObserverService();
     if (NS_WARN_IF(!observerService)) {
       return NS_ERROR_FAILURE;
     }
 
+    nsAutoString state;
+    GetActiveState(state);
+
     observerService->NotifyObservers(ToSupports(mWindow),
                                      "audio-playback",
-                                     mActive ? MOZ_UTF16("active")
-                                             : MOZ_UTF16("inactive"));
+                                     state.get());
 
     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
-           ("AudioPlaybackRunnable, active = %d\n", mActive));
+           ("AudioPlaybackRunnable, active = %d, reason = %d\n",
+            mActive, mReason));
+
     return NS_OK;
   }
 
 private:
+  void GetActiveState(nsAString& astate)
+  {
+    if (mActive) {
+      CopyASCIItoUTF16("active", astate);
+    } else {
+      if(mReason == AudioChannelService::AudibleChangedReasons::ePauseStateChanged) {
+        CopyASCIItoUTF16("inactive-pause", astate);
+      } else {
+        CopyASCIItoUTF16("inactive-nonaudible", astate);
+      }
+    }
+  }
+
   nsCOMPtr<nsPIDOMWindowOuter> mWindow;
   bool mActive;
+  AudioChannelService::AudibleChangedReasons mReason;
 };
 
 } // anonymous namespace
 
 StaticRefPtr<AudioChannelService> gAudioChannelService;
 
 // Mappings from 'mozaudiochannel' attribute strings to an enumeration.
 static const nsAttrValue::EnumTable kMozAudioChannelAttributeTable[] = {
@@ -367,24 +387,25 @@ AudioChannelService::GetMediaConfig(nsPI
     // If there is no parent, or we are the toplevel we don't continue.
   } while (window && window != aWindow);
 
   return config;
 }
 
 void
 AudioChannelService::AudioAudibleChanged(AudioChannelAgent* aAgent,
-                                         AudibleState aAudible)
+                                         AudibleState aAudible,
+                                         AudibleChangedReasons aReason)
 {
   MOZ_ASSERT(aAgent);
 
   uint64_t windowID = aAgent->WindowID();
   AudioChannelWindow* winData = GetWindowData(windowID);
   if (winData) {
-    winData->AudioAudibleChanged(aAgent, aAudible);
+    winData->AudioAudibleChanged(aAgent, aAudible, aReason);
   }
 }
 
 bool
 AudioChannelService::TelephonyChannelIsActive()
 {
   nsTObserverArray<nsAutoPtr<AudioChannelWindow>>::ForwardIterator windowsIter(mWindows);
   while (windowsIter.HasMore()) {
@@ -1188,28 +1209,32 @@ AudioChannelService::AudioChannelWindow:
                                                      AudibleState aAudible)
 {
   MOZ_ASSERT(aAgent);
 
   RequestAudioFocus(aAgent);
   AppendAgentAndIncreaseAgentsNum(aAgent);
   AudioCapturedChanged(aAgent, AudioCaptureState::eCapturing);
   if (aAudible) {
-    AudioAudibleChanged(aAgent, AudibleState::eAudible);
+    AudioAudibleChanged(aAgent,
+                        AudibleState::eAudible,
+                        AudibleChangedReasons::eDataAudibleChanged);
   }
 }
 
 void
 AudioChannelService::AudioChannelWindow::RemoveAgent(AudioChannelAgent* aAgent)
 {
   MOZ_ASSERT(aAgent);
 
   RemoveAgentAndReduceAgentsNum(aAgent);
   AudioCapturedChanged(aAgent, AudioCaptureState::eNotCapturing);
-  AudioAudibleChanged(aAgent, AudibleState::eNotAudible);
+  AudioAudibleChanged(aAgent,
+                      AudibleState::eNotAudible,
+                      AudibleChangedReasons::ePauseStateChanged);
 }
 
 void
 AudioChannelService::AudioChannelWindow::AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent)
 {
   MOZ_ASSERT(aAgent);
   MOZ_ASSERT(!mAgents.Contains(aAgent));
 
@@ -1253,52 +1278,55 @@ AudioChannelService::AudioChannelWindow:
 
   if (mIsAudioCaptured) {
     aAgent->WindowAudioCaptureChanged(aAgent->InnerWindowID(), aCapture);
   }
 }
 
 void
 AudioChannelService::AudioChannelWindow::AudioAudibleChanged(AudioChannelAgent* aAgent,
-                                                             AudibleState aAudible)
+                                                             AudibleState aAudible,
+                                                             AudibleChangedReasons aReason)
 {
   MOZ_ASSERT(aAgent);
 
   if (aAudible) {
-    AppendAudibleAgentIfNotContained(aAgent);
+    AppendAudibleAgentIfNotContained(aAgent, aReason);
   } else {
-    RemoveAudibleAgentIfContained(aAgent);
+    RemoveAudibleAgentIfContained(aAgent, aReason);
   }
 
   NotifyAudioCompetingChanged(aAgent, aAudible);
 }
 
 void
-AudioChannelService::AudioChannelWindow::AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent)
+AudioChannelService::AudioChannelWindow::AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
+                                                                          AudibleChangedReasons aReason)
 {
   MOZ_ASSERT(aAgent);
   MOZ_ASSERT(mAgents.Contains(aAgent));
 
   if (!mAudibleAgents.Contains(aAgent)) {
     mAudibleAgents.AppendElement(aAgent);
     if (IsFirstAudibleAgent()) {
-      NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eAudible);
+      NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eAudible, aReason);
     }
   }
 }
 
 void
-AudioChannelService::AudioChannelWindow::RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent)
+AudioChannelService::AudioChannelWindow::RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent,
+                                                                       AudibleChangedReasons aReason)
 {
   MOZ_ASSERT(aAgent);
 
   if (mAudibleAgents.Contains(aAgent)) {
     mAudibleAgents.RemoveElement(aAgent);
     if (IsLastAudibleAgent()) {
-      NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eNotAudible);
+      NotifyAudioAudibleChanged(aAgent->Window(), AudibleState::eNotAudible, aReason);
     }
   }
 }
 
 bool
 AudioChannelService::AudioChannelWindow::IsFirstAudibleAgent() const
 {
   return (mAudibleAgents.Length() == 1);
@@ -1307,20 +1335,21 @@ AudioChannelService::AudioChannelWindow:
 bool
 AudioChannelService::AudioChannelWindow::IsLastAudibleAgent() const
 {
   return mAudibleAgents.IsEmpty();
 }
 
 void
 AudioChannelService::AudioChannelWindow::NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow,
-                                                                   AudibleState aAudible)
+                                                                   AudibleState aAudible,
+                                                                   AudibleChangedReasons aReason)
 {
   RefPtr<AudioPlaybackRunnable> runnable =
-    new AudioPlaybackRunnable(aWindow, aAudible);
+    new AudioPlaybackRunnable(aWindow, aAudible, aReason);
   nsresult rv = NS_DispatchToCurrentThread(runnable);
   NS_WARN_IF(NS_FAILED(rv));
 }
 
 void
 AudioChannelService::AudioChannelWindow::NotifyChannelActive(uint64_t aWindowID,
                                                              AudioChannel aChannel,
                                                              bool aActive)
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -73,16 +73,22 @@ public:
     eNotAudible = false
   };
 
   enum AudioCaptureState : bool {
     eCapturing = true,
     eNotCapturing = false
   };
 
+  enum AudibleChangedReasons : uint32_t {
+    eVolumeChanged = 0,
+    eDataAudibleChanged = 1,
+    ePauseStateChanged = 2
+  };
+
   /**
    * Returns the AudioChannelServce singleton.
    * If AudioChannelServce is not exist, create and return new one.
    * Only to be called from main thread.
    */
   static already_AddRefed<AudioChannelService> GetOrCreate();
 
   static bool IsAudioChannelMutedByDefault();
@@ -117,17 +123,19 @@ public:
   AudioPlaybackConfig GetMediaConfig(nsPIDOMWindowOuter* aWindow,
                                      uint32_t aAudioChannel) const;
 
   /**
    * Called this method when the audible state of the audio playback changed,
    * it would dispatch the playback event to observers which want to know the
    * actual audible state of the window.
    */
-  void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible);
+  void AudioAudibleChanged(AudioChannelAgent* aAgent,
+                           AudibleState aAudible,
+                           AudibleChangedReasons aReason);
 
   /* Methods for the BrowserElementAudioChannel */
   float GetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
 
   void SetAudioChannelVolume(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel,
                              float aVolume);
 
   bool GetAudioChannelMuted(nsPIDOMWindowOuter* aWindow, AudioChannel aChannel);
@@ -245,17 +253,19 @@ private:
       , mIsAudioCaptured(false)
       , mOwningAudioFocus(!AudioChannelService::IsEnableAudioCompeting())
     {
       // Workaround for bug1183033, system channel type can always playback.
       mChannels[(int16_t)AudioChannel::System].mMuted = false;
     }
 
     void AudioFocusChanged(AudioChannelAgent* aNewPlayingAgent, bool aActive);
-    void AudioAudibleChanged(AudioChannelAgent* aAgent, AudibleState aAudible);
+    void AudioAudibleChanged(AudioChannelAgent* aAgent,
+                             AudibleState aAudible,
+                             AudibleChangedReasons aReason);
 
     void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible);
     void RemoveAgent(AudioChannelAgent* aAgent);
 
     uint64_t mWindowID;
     bool mIsAudioCaptured;
     AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS];
 
@@ -266,27 +276,31 @@ private:
     // Owning audio focus when the window starts playing audible sound, and
     // lose audio focus when other windows starts playing.
     bool mOwningAudioFocus;
 
   private:
     void AudioCapturedChanged(AudioChannelAgent* aAgent,
                               AudioCaptureState aCapture);
 
-    void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent);
-    void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent);
+    void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
+                                          AudibleChangedReasons aReason);
+    void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent,
+                                       AudibleChangedReasons aReason);
 
     void AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent);
     void RemoveAgentAndReduceAgentsNum(AudioChannelAgent* aAgent);
 
     bool IsFirstAudibleAgent() const;
     bool IsLastAudibleAgent() const;
 
     void NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow,
-                                   AudibleState aAudible);
+                                   AudibleState aAudible,
+                                   AudibleChangedReasons aReason);
+
     void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel,
                              bool aActive);
 
     void RequestAudioFocus(AudioChannelAgent* aAgent);
     void NotifyAudioCompetingChanged(AudioChannelAgent* aAgent, bool aActive);
 
     uint32_t GetCompetingBehavior(AudioChannelAgent* aAgent,
                                   int32_t aIncomingChannelType,
--- a/dom/audiochannel/nsIAudioChannelAgent.idl
+++ b/dom/audiochannel/nsIAudioChannelAgent.idl
@@ -178,10 +178,10 @@ interface nsIAudioChannelAgent : nsISupp
 
   /**
    * Notify agent that we already start producing audible data.
    *
    * Note : sometime audio might become silent during playing, this method is used to
    * notify the actually audible state to other services which want to know
    * about that, ex. tab sound indicator.
    */
-  void notifyStartedAudible(in bool audible);
+  void notifyStartedAudible(in bool audible, in uint32_t reason);
 };
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -338,51 +338,40 @@ Blob::SetMutable(bool aMutable)
 
 JSObject*
 Blob::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return BlobBinding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */ already_AddRefed<Blob>
-Blob::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
+Blob::Constructor(const GlobalObject& aGlobal,
+                  const Optional<Sequence<BlobPart>>& aData,
+                  const BlobPropertyBag& aBag,
+                  ErrorResult& aRv)
 {
   RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl();
 
-  impl->InitializeBlob(aRv);
+  if (aData.WasPassed()) {
+    impl->InitializeBlob(aGlobal.Context(), aData.Value(), aBag.mType,
+                         aBag.mEndings == EndingTypes::Native, aRv);
+  } else {
+    impl->InitializeBlob(aRv);
+  }
+
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   MOZ_ASSERT(!impl->IsFile());
 
   RefPtr<Blob> blob = Blob::Create(aGlobal.GetAsSupports(), impl);
   return blob.forget();
 }
 
-/* static */ already_AddRefed<Blob>
-Blob::Constructor(
-        const GlobalObject& aGlobal,
-        const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
-        const BlobPropertyBag& aBag,
-        ErrorResult& aRv)
-{
-  RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl();
-
-  impl->InitializeBlob(aGlobal.Context(), aData, aBag.mType,
-                       aBag.mEndings == EndingTypes::Native, aRv);
-  if (aRv.Failed()) {
-    return nullptr;
-  }
-  MOZ_ASSERT(!impl->IsFile());
-
-  RefPtr<Blob> blob = Blob::Create(aGlobal.GetAsSupports(), impl);
-  return blob.forget();
-}
-
 int64_t
 Blob::GetFileId()
 {
   return mImpl->GetFileId();
 }
 
 bool
 Blob::IsMemoryFile() const
@@ -538,22 +527,21 @@ ParseSize(int64_t aSize, int64_t& aStart
   }
   else {
     aStart = newStartOffset.value();
     aEnd = newEndOffset.value();
   }
 }
 
 /* static */ already_AddRefed<File>
-File::Constructor(
-        const GlobalObject& aGlobal,
-        const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
-        const nsAString& aName,
-        const FilePropertyBag& aBag,
-        ErrorResult& aRv)
+File::Constructor(const GlobalObject& aGlobal,
+                  const Sequence<BlobPart>& aData,
+                  const nsAString& aName,
+                  const FilePropertyBag& aBag,
+                  ErrorResult& aRv)
 {
   RefPtr<MultipartBlobImpl> impl = new MultipartBlobImpl(aName);
 
   impl->InitializeBlob(aGlobal.Context(), aData, aBag.mType, false, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   MOZ_ASSERT(impl->IsFile());
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -37,32 +37,34 @@ class nsIInputStream;
 namespace mozilla {
 namespace dom {
 
 struct BlobPropertyBag;
 struct ChromeFilePropertyBag;
 struct FilePropertyBag;
 class BlobImpl;
 class File;
-class OwningArrayBufferOrArrayBufferViewOrBlobOrString;
+class OwningArrayBufferOrArrayBufferViewOrBlobOrUSVString;
 
 class Blob : public nsIDOMBlob
            , public nsIXHRSendable
            , public nsIMutable
            , public nsSupportsWeakReference
            , public nsWrapperCache
 {
 public:
   NS_DECL_NSIDOMBLOB
   NS_DECL_NSIXHRSENDABLE
   NS_DECL_NSIMUTABLE
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Blob, nsIDOMBlob)
 
+  typedef OwningArrayBufferOrArrayBufferViewOrBlobOrUSVString BlobPart;
+
   // This creates a Blob or a File based on the type of BlobImpl.
   static Blob*
   Create(nsISupports* aParent, BlobImpl* aImpl);
 
   static already_AddRefed<Blob>
   Create(nsISupports* aParent, const nsAString& aContentType,
          uint64_t aLength);
 
@@ -117,22 +119,18 @@ public:
     return mParent;
   }
 
   bool
   IsMemoryFile() const;
 
   // Blob constructor
   static already_AddRefed<Blob>
-  Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
-
-  // Blob constructor
-  static already_AddRefed<Blob>
   Constructor(const GlobalObject& aGlobal,
-              const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
+              const Optional<Sequence<BlobPart>>& aData,
               const BlobPropertyBag& aBag,
               ErrorResult& aRv);
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   uint64_t GetSize(ErrorResult& aRv);
 
@@ -199,17 +197,17 @@ public:
   // WebIDL methods
 
   virtual JSObject* WrapObject(JSContext *cx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // File constructor
   static already_AddRefed<File>
   Constructor(const GlobalObject& aGlobal,
-              const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
+              const Sequence<BlobPart>& aData,
               const nsAString& aName,
               const FilePropertyBag& aBag,
               ErrorResult& aRv);
 
   // File constructor - ChromeOnly
   static already_AddRefed<File>
   Constructor(const GlobalObject& aGlobal,
               Blob& aData,
--- a/dom/base/MultipartBlobImpl.cpp
+++ b/dom/base/MultipartBlobImpl.cpp
@@ -167,36 +167,35 @@ MultipartBlobImpl::CreateSlice(uint64_t 
 void
 MultipartBlobImpl::InitializeBlob(ErrorResult& aRv)
 {
   SetLengthAndModifiedDate(aRv);
   NS_WARN_IF(aRv.Failed());
 }
 
 void
-MultipartBlobImpl::InitializeBlob(
-       JSContext* aCx,
-       const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
-       const nsAString& aContentType,
-       bool aNativeEOL,
-       ErrorResult& aRv)
+MultipartBlobImpl::InitializeBlob(JSContext* aCx,
+                                  const Sequence<Blob::BlobPart>& aData,
+                                  const nsAString& aContentType,
+                                  bool aNativeEOL,
+                                  ErrorResult& aRv)
 {
   mContentType = aContentType;
   BlobSet blobSet;
 
   for (uint32_t i = 0, len = aData.Length(); i < len; ++i) {
-    const OwningArrayBufferOrArrayBufferViewOrBlobOrString& data = aData[i];
+    const Blob::BlobPart& data = aData[i];
 
     if (data.IsBlob()) {
       RefPtr<Blob> blob = data.GetAsBlob().get();
       blobSet.AppendBlobImpl(blob->Impl());
     }
 
-    else if (data.IsString()) {
-      aRv = blobSet.AppendString(data.GetAsString(), aNativeEOL, aCx);
+    else if (data.IsUSVString()) {
+      aRv = blobSet.AppendString(data.GetAsUSVString(), aNativeEOL, aCx);
       if (aRv.Failed()) {
         return;
       }
     }
 
     else if (data.IsArrayBuffer()) {
       const ArrayBuffer& buffer = data.GetAsArrayBuffer();
       buffer.ComputeLengthAndData();
--- a/dom/base/MultipartBlobImpl.h
+++ b/dom/base/MultipartBlobImpl.h
@@ -48,22 +48,21 @@ public:
   MultipartBlobImpl()
     : BlobImplBase(EmptyString(), UINT64_MAX),
       mIsFromNsIFile(false)
   {
   }
 
   void InitializeBlob(ErrorResult& aRv);
 
-  void InitializeBlob(
-       JSContext* aCx,
-       const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
-       const nsAString& aContentType,
-       bool aNativeEOL,
-       ErrorResult& aRv);
+  void InitializeBlob(JSContext* aCx,
+                      const Sequence<Blob::BlobPart>& aData,
+                      const nsAString& aContentType,
+                      bool aNativeEOL,
+                      ErrorResult& aRv);
 
   void InitializeChromeFile(Blob& aData,
                             const ChromeFilePropertyBag& aBag,
                             ErrorResult& aRv);
 
   void InitializeChromeFile(nsPIDOMWindowInner* aWindow,
                             const nsAString& aData,
                             const ChromeFilePropertyBag& aBag,
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -81,16 +81,17 @@ public:
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIREQUEST
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
   using nsIEventTarget::Dispatch;
 
   explicit WebSocketImpl(WebSocket* aWebSocket)
   : mWebSocket(aWebSocket)
+  , mIsServerSide(false)
   , mOnCloseScheduled(false)
   , mFailed(false)
   , mDisconnectingOrDisconnected(false)
   , mCloseEventWasClean(false)
   , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL)
   , mScriptLine(0)
   , mScriptColumn(0)
   , mInnerWindowID(0)
@@ -113,25 +114,28 @@ public:
   {
     MOZ_ASSERT(IsTargetThread());
   }
 
   bool IsTargetThread() const;
 
   void Init(JSContext* aCx,
             nsIPrincipal* aPrincipal,
+            bool aIsServerSide,
             const nsAString& aURL,
             nsTArray<nsString>& aProtocolArray,
             const nsACString& aScriptFile,
             uint32_t aScriptLine,
             uint32_t aScriptColumn,
             ErrorResult& aRv,
             bool* aConnectionFailed);
 
   void AsyncOpen(nsIPrincipal* aPrincipal, uint64_t aInnerWindowID,
+                 nsITransportProvider* aTransportProvider,
+                 const nsACString& aNegotiatedExtensions,
                  ErrorResult& aRv);
 
   nsresult ParseURL(const nsAString& aURL);
   nsresult InitializeConnection(nsIPrincipal* aPrincipal);
 
   // These methods when called can release the WebSocket object
   void FailConnection(uint16_t reasonCode,
                       const nsACString& aReasonString = EmptyCString());
@@ -164,16 +168,19 @@ public:
   void UnregisterFeature();
 
   nsresult CancelInternal();
 
   RefPtr<WebSocket> mWebSocket;
 
   nsCOMPtr<nsIWebSocketChannel> mChannel;
 
+  bool mIsServerSide; // True if we're implementing the server side of a
+                      // websocket connection
+
   bool mSecure; // if true it is using SSL and the wss scheme,
                 // otherwise it is using the ws scheme with no SSL
 
   bool mOnCloseScheduled;
   bool mFailed;
   bool mDisconnectingOrDisconnected;
 
   // Set attributes of DOM 'onclose' message
@@ -946,32 +953,44 @@ WebSocket::WrapObject(JSContext* cx, JS:
 
 // Constructor:
 already_AddRefed<WebSocket>
 WebSocket::Constructor(const GlobalObject& aGlobal,
                        const nsAString& aUrl,
                        ErrorResult& aRv)
 {
   Sequence<nsString> protocols;
-  return WebSocket::Constructor(aGlobal, aUrl, protocols, aRv);
+  return WebSocket::ConstructorCommon(aGlobal, aUrl, protocols, nullptr,
+                                      EmptyCString(), aRv);
 }
 
 already_AddRefed<WebSocket>
 WebSocket::Constructor(const GlobalObject& aGlobal,
                        const nsAString& aUrl,
                        const nsAString& aProtocol,
                        ErrorResult& aRv)
 {
   Sequence<nsString> protocols;
   if (!protocols.AppendElement(aProtocol, fallible)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
 
-  return WebSocket::Constructor(aGlobal, aUrl, protocols, aRv);
+  return WebSocket::ConstructorCommon(aGlobal, aUrl, protocols, nullptr,
+                                      EmptyCString(), aRv);
+}
+
+already_AddRefed<WebSocket>
+WebSocket::Constructor(const GlobalObject& aGlobal,
+                       const nsAString& aUrl,
+                       const Sequence<nsString>& aProtocols,
+                       ErrorResult& aRv)
+{
+  return WebSocket::ConstructorCommon(aGlobal, aUrl, aProtocols, nullptr,
+                                      EmptyCString(), aRv);
 }
 
 namespace {
 
 // This class is used to clear any exception.
 class MOZ_STACK_CLASS ClearException
 {
 public:
@@ -1022,24 +1041,26 @@ protected:
   virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) = 0;
 
   virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) = 0;
 };
 
 class InitRunnable final : public WebSocketMainThreadRunnable
 {
 public:
-  InitRunnable(WebSocketImpl* aImpl, const nsAString& aURL,
+  InitRunnable(WebSocketImpl* aImpl, bool aIsServerSide,
+               const nsAString& aURL,
                nsTArray<nsString>& aProtocolArray,
                const nsACString& aScriptFile, uint32_t aScriptLine,
                uint32_t aScriptColumn,
                ErrorResult& aRv, bool* aConnectionFailed)
     : WebSocketMainThreadRunnable(aImpl->mWorkerPrivate,
                                   NS_LITERAL_CSTRING("WebSocket :: init"))
     , mImpl(aImpl)
+    , mIsServerSide(aIsServerSide)
     , mURL(aURL)
     , mProtocolArray(aProtocolArray)
     , mScriptFile(aScriptFile)
     , mScriptLine(aScriptLine)
     , mScriptColumn(aScriptColumn)
     , mRv(aRv)
     , mConnectionFailed(aConnectionFailed)
   {
@@ -1065,35 +1086,37 @@ protected:
     }
 
     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
     if (!principal) {
       mRv.Throw(NS_ERROR_FAILURE);
       return true;
     }
 
-    mImpl->Init(jsapi.cx(), principal, mURL, mProtocolArray, mScriptFile,
-                mScriptLine, mScriptColumn, mRv, mConnectionFailed);
+    mImpl->Init(jsapi.cx(), principal, mIsServerSide, mURL, mProtocolArray,
+                mScriptFile, mScriptLine, mScriptColumn, mRv,
+                mConnectionFailed);
     return true;
   }
 
   virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
 
-    mImpl->Init(nullptr, aTopLevelWorkerPrivate->GetPrincipal(), mURL,
-                mProtocolArray, mScriptFile, mScriptLine, mScriptColumn, mRv,
-                mConnectionFailed);
+    mImpl->Init(nullptr, aTopLevelWorkerPrivate->GetPrincipal(), mIsServerSide,
+                mURL, mProtocolArray, mScriptFile, mScriptLine, mScriptColumn,
+                mRv, mConnectionFailed);
     return true;
   }
 
   // Raw pointer. This worker runs synchronously.
   WebSocketImpl* mImpl;
 
+  bool mIsServerSide;
   const nsAString& mURL;
   nsTArray<nsString>& mProtocolArray;
   nsCString mScriptFile;
   uint32_t mScriptLine;
   uint32_t mScriptColumn;
   ErrorResult& mRv;
   bool* mConnectionFailed;
 };
@@ -1135,44 +1158,48 @@ protected:
     if (topWindow) {
       topInner = topWindow->GetCurrentInnerWindow();
     }
 
     if (topInner) {
       windowID = topInner->WindowID();
     }
 
-    mImpl->AsyncOpen(principal, windowID, mRv);
+    mImpl->AsyncOpen(principal, windowID, nullptr, EmptyCString(), mRv);
     return true;
   }
 
   virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
 
-    mImpl->AsyncOpen(aTopLevelWorkerPrivate->GetPrincipal(), 0, mRv);
+    mImpl->AsyncOpen(aTopLevelWorkerPrivate->GetPrincipal(), 0, nullptr,
+                     EmptyCString(), mRv);
     return true;
   }
 
 private:
   // Raw pointer. This worker runs synchronously.
   WebSocketImpl* mImpl;
 
   ErrorResult& mRv;
 };
 
 } // namespace
 
 already_AddRefed<WebSocket>
-WebSocket::Constructor(const GlobalObject& aGlobal,
-                       const nsAString& aUrl,
-                       const Sequence<nsString>& aProtocols,
-                       ErrorResult& aRv)
+WebSocket::ConstructorCommon(const GlobalObject& aGlobal,
+                             const nsAString& aUrl,
+                             const Sequence<nsString>& aProtocols,
+                             nsITransportProvider* aTransportProvider,
+                             const nsACString& aNegotiatedExtensions,
+                             ErrorResult& aRv)
 {
+  MOZ_ASSERT_IF(!aTransportProvider, aNegotiatedExtensions.IsEmpty());
   nsCOMPtr<nsIPrincipal> principal;
   nsCOMPtr<nsPIDOMWindowInner> ownerWindow;
 
   if (NS_IsMainThread()) {
     nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
       do_QueryInterface(aGlobal.GetAsSupports());
     if (!scriptPrincipal) {
       aRv.Throw(NS_ERROR_FAILURE);
@@ -1224,37 +1251,38 @@ WebSocket::Constructor(const GlobalObjec
   }
 
   RefPtr<WebSocket> webSocket = new WebSocket(ownerWindow);
   RefPtr<WebSocketImpl> kungfuDeathGrip = webSocket->mImpl;
 
   bool connectionFailed = true;
 
   if (NS_IsMainThread()) {
-    webSocket->mImpl->Init(aGlobal.Context(), principal, aUrl, protocolArray,
-                           EmptyCString(), 0, 0, aRv, &connectionFailed);
+    webSocket->mImpl->Init(aGlobal.Context(), principal, !!aTransportProvider,
+                           aUrl, protocolArray, EmptyCString(),
+                           0, 0, aRv, &connectionFailed);
   } else {
     // In workers we have to keep the worker alive using a feature in order to
     // dispatch messages correctly.
     if (!webSocket->mImpl->RegisterFeature()) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
 
     unsigned lineno, column;
     JS::AutoFilename file;
     if (!JS::DescribeScriptedCaller(aGlobal.Context(), &file, &lineno,
                                     &column)) {
       NS_WARNING("Failed to get line number and filename in workers.");
     }
 
     RefPtr<InitRunnable> runnable =
-      new InitRunnable(webSocket->mImpl, aUrl, protocolArray,
-                       nsDependentCString(file.get()), lineno, column, aRv,
-                       &connectionFailed);
+      new InitRunnable(webSocket->mImpl, !!aTransportProvider, aUrl,
+                       protocolArray, nsDependentCString(file.get()), lineno,
+                       column, aRv, &connectionFailed);
     runnable->Dispatch(aRv);
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   // It can be that we have been already disconnected because the WebSocket is
@@ -1322,18 +1350,21 @@ WebSocket::Constructor(const GlobalObjec
     if (topWindow) {
       topInner = topWindow->GetCurrentInnerWindow();
     }
 
     if (topInner) {
       windowID = topInner->WindowID();
     }
 
-    webSocket->mImpl->AsyncOpen(principal, windowID, aRv);
+    webSocket->mImpl->AsyncOpen(principal, windowID, aTransportProvider,
+                                aNegotiatedExtensions, aRv);
   } else {
+    MOZ_ASSERT(!aTransportProvider && aNegotiatedExtensions.IsEmpty(),
+               "not yet implemented");
     RefPtr<AsyncOpenRunnable> runnable =
       new AsyncOpenRunnable(webSocket->mImpl, aRv);
     runnable->Dispatch(aRv);
   }
 
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
@@ -1420,16 +1451,17 @@ WebSocket::DisconnectFromOwner()
 
 //-----------------------------------------------------------------------------
 // WebSocketImpl:: initialization
 //-----------------------------------------------------------------------------
 
 void
 WebSocketImpl::Init(JSContext* aCx,
                     nsIPrincipal* aPrincipal,
+                    bool aIsServerSide,
                     const nsAString& aURL,
                     nsTArray<nsString>& aProtocolArray,
                     const nsACString& aScriptFile,
                     uint32_t aScriptLine,
                     uint32_t aScriptColumn,
                     ErrorResult& aRv,
                     bool* aConnectionFailed)
 {
@@ -1479,77 +1511,82 @@ WebSocketImpl::Init(JSContext* aCx,
     JS::AutoFilename file;
     if (JS::DescribeScriptedCaller(aCx, &file, &lineno, &column)) {
       mScriptFile = file.get();
       mScriptLine = lineno;
       mScriptColumn = column;
     }
   }
 
+  mIsServerSide = aIsServerSide;
+
   // If we don't have aCx, we are window-less, so we don't have a
   // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in
   // DedicateWorkers created by JSM.
   if (aCx) {
     mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx);
   }
 
   // parses the url
   aRv = ParseURL(PromiseFlatString(aURL));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
-  nsCOMPtr<nsIURI> uri;
-  {
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
-
-    // We crash here because we are sure that mURI is a valid URI, so either we
-    // are OOM'ing or something else bad is happening.
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      MOZ_CRASH();
-    }
-  }
-
-  // Check content policy.
-  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   nsCOMPtr<nsIDocument> originDoc = mWebSocket->GetDocumentIfCurrent();
   if (!originDoc) {
     nsresult rv = mWebSocket->CheckInnerWindowCorrectness();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aRv.Throw(rv);
       return;
     }
   }
-
   mOriginDocument = do_GetWeakReference(originDoc);
-  aRv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
-                                  uri,
-                                  aPrincipal,
-                                  originDoc,
-                                  EmptyCString(),
-                                  nullptr,
-                                  &shouldLoad,
-                                  nsContentUtils::GetContentPolicy(),
-                                  nsContentUtils::GetSecurityManager());
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
-
-  if (NS_CP_REJECTED(shouldLoad)) {
-    // Disallowed by content policy.
-    aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
-    return;
+
+  if (!mIsServerSide) {
+    nsCOMPtr<nsIURI> uri;
+    {
+      nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
+
+      // We crash here because we are sure that mURI is a valid URI, so either we
+      // are OOM'ing or something else bad is happening.
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        MOZ_CRASH();
+      }
+    }
+
+    // Check content policy.
+    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+    aRv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
+                                    uri,
+                                    aPrincipal,
+                                    originDoc,
+                                    EmptyCString(),
+                                    nullptr,
+                                    &shouldLoad,
+                                    nsContentUtils::GetContentPolicy(),
+                                    nsContentUtils::GetSecurityManager());
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+
+    if (NS_CP_REJECTED(shouldLoad)) {
+      // Disallowed by content policy.
+      aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
+      return;
+    }
   }
 
   // Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
   // In such a case we have to upgrade ws: to wss: and also update mSecure
   // to reflect that upgrade. Please note that we can not upgrade from ws:
   // to wss: before performing content policy checks because CSP needs to
   // send reports in case the scheme is about to be upgraded.
-  if (!mSecure && originDoc && originDoc->GetUpgradeInsecureRequests(false)) {
+  if (!mIsServerSide && !mSecure && originDoc &&
+      originDoc->GetUpgradeInsecureRequests(false)) {
     // let's use the old specification before the upgrade for logging
     NS_ConvertUTF8toUTF16 reportSpec(mURI);
 
     // upgrade the request from ws:// to wss:// and mark as secure
     mURI.ReplaceSubstring("ws://", "wss://");
     if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
       return;
     }
@@ -1562,17 +1599,17 @@ WebSocketImpl::Init(JSContext* aCx,
                         EmptyString(), // aScriptSample
                         0, // aLineNumber
                         0, // aColumnNumber
                         nsIScriptError::warningFlag, "CSP",
                         mInnerWindowID);
   }
 
   // Don't allow https:// to open ws://
-  if (!mSecure &&
+  if (!mIsServerSide && !mSecure &&
       !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
                             false)) {
     // Confirmed we are opening plain ws:// and want to prevent this from a
     // secure context (e.g. https).
     nsCOMPtr<nsIPrincipal> principal;
     nsCOMPtr<nsIURI> originURI;
     if (mWorkerPrivate) {
       // For workers, retrieve the URI from the WorkerPrivate
@@ -1699,31 +1736,43 @@ WebSocketImpl::Init(JSContext* aCx,
     *aConnectionFailed = true;
   } else {
     *aConnectionFailed = false;
   }
 }
 
 void
 WebSocketImpl::AsyncOpen(nsIPrincipal* aPrincipal, uint64_t aInnerWindowID,
+                         nsITransportProvider* aTransportProvider,
+                         const nsACString& aNegotiatedExtensions,
                          ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
+  MOZ_ASSERT_IF(!aTransportProvider, aNegotiatedExtensions.IsEmpty());
 
   nsCString asciiOrigin;
   aRv = nsContentUtils::GetASCIIOrigin(aPrincipal, asciiOrigin);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
+  if (aTransportProvider) {
+    aRv = mChannel->SetServerParameters(aTransportProvider, aNegotiatedExtensions);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+  }
+
   ToLowerCase(asciiOrigin);
 
   nsCOMPtr<nsIURI> uri;
-  aRv = NS_NewURI(getter_AddRefs(uri), mURI);
-  MOZ_ASSERT(!aRv.Failed());
+  if (!aTransportProvider) {
+    aRv = NS_NewURI(getter_AddRefs(uri), mURI);
+    MOZ_ASSERT(!aRv.Failed());
+  }
 
   aRv = mChannel->AsyncOpen(uri, asciiOrigin, aInnerWindowID, this, nullptr);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   mInnerWindowID = aInnerWindowID;
 }
@@ -1979,16 +2028,23 @@ WebSocket::CreateAndDispatchCloseEvent(b
 }
 
 nsresult
 WebSocketImpl::ParseURL(const nsAString& aURL)
 {
   AssertIsOnMainThread();
   NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
 
+  if (mIsServerSide) {
+    mWebSocket->mURI = aURL;
+    CopyUTF16toUTF8(mWebSocket->mURI, mURI);
+
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
 
   nsCOMPtr<nsIURL> parsedURL = do_QueryInterface(uri, &rv);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
 
   bool hasRef;
--- a/dom/base/WebSocket.h
+++ b/dom/base/WebSocket.h
@@ -19,16 +19,17 @@
 #include "nsISupportsUtils.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 #define DEFAULT_WS_SCHEME_PORT  80
 #define DEFAULT_WSS_SCHEME_PORT 443
 
 class nsIInputStream;
+class nsITransportProvider;
 
 namespace mozilla {
 namespace dom {
 
 class Blob;
 
 class WebSocketImpl;
 
@@ -77,16 +78,23 @@ public: // WebIDL interface:
                                                  const nsAString& aProtocol,
                                                  ErrorResult& rv);
 
   static already_AddRefed<WebSocket> Constructor(const GlobalObject& aGlobal,
                                                  const nsAString& aUrl,
                                                  const Sequence<nsString>& aProtocols,
                                                  ErrorResult& rv);
 
+  static already_AddRefed<WebSocket> ConstructorCommon(const GlobalObject& aGlobal,
+                                                       const nsAString& aUrl,
+                                                       const Sequence<nsString>& aProtocols,
+                                                       nsITransportProvider* aTransportProvider,
+                                                       const nsACString& aNegotiatedExtensions,
+                                                       ErrorResult& rv);
+
   // webIDL: readonly attribute DOMString url
   void GetUrl(nsAString& aResult);
 
   // webIDL: readonly attribute unsigned short readyState;
   uint16_t ReadyState();
 
   // webIDL: readonly attribute unsigned long bufferedAmount;
   uint32_t BufferedAmount() const;
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -64,18 +64,16 @@
 #  undef SendMessage
 # endif
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
-static const size_t kMinTelemetryMessageSize = 8192;
-
 nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
                                              nsFrameMessageManager* aParentManager,
                                              /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
  : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
    mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
    mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),
    mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
    mOwnsCallback(!!(aFlags & mozilla::dom::ipc::MM_OWNSCALLBACK)),
@@ -700,16 +698,32 @@ nsFrameMessageManager::SendRpcMessage(co
                                       JSContext* aCx,
                                       uint8_t aArgc,
                                       JS::MutableHandle<JS::Value> aRetval)
 {
   return SendMessage(aMessageName, aJSON, aObjects, aPrincipal, aCx, aArgc,
                      aRetval, false);
 }
 
+static void
+RecordMessageSize(size_t aDataLength, const nsAString& aMessageName)
+{
+  static const size_t kMinTelemetryMessageSize = 8192;
+
+  if (aDataLength < kMinTelemetryMessageSize) {
+    return;
+  }
+
+  NS_ConvertUTF16toUTF8 messageName(aMessageName);
+  messageName.StripChars("0123456789");
+
+  Telemetry::Accumulate(Telemetry::MESSAGE_MANAGER_MESSAGE_SIZE2, messageName,
+                        aDataLength);
+}
+
 nsresult
 nsFrameMessageManager::SendMessage(const nsAString& aMessageName,
                                    JS::Handle<JS::Value> aJSON,
                                    JS::Handle<JS::Value> aObjects,
                                    nsIPrincipal* aPrincipal,
                                    JSContext* aCx,
                                    uint8_t aArgc,
                                    JS::MutableHandle<JS::Value> aRetval,
@@ -727,21 +741,17 @@ nsFrameMessageManager::SendMessage(const
     return NS_ERROR_UNEXPECTED;
   }
 
   StructuredCloneData data;
   if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, JS::UndefinedHandleValue, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
-  if (data.DataLength() >= kMinTelemetryMessageSize) {
-    Telemetry::Accumulate(Telemetry::MESSAGE_MANAGER_MESSAGE_SIZE,
-                          NS_ConvertUTF16toUTF8(aMessageName),
-                          data.DataLength());
-  }
+  RecordMessageSize(data.DataLength(), aMessageName);
 
   JS::Rooted<JSObject*> objects(aCx);
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
 
   nsTArray<StructuredCloneData> retval;
 
@@ -813,21 +823,17 @@ nsFrameMessageManager::DispatchAsyncMess
                                             JSContext* aCx,
                                             uint8_t aArgc)
 {
   StructuredCloneData data;
   if (aArgc >= 2 && !GetParamsForMessage(aCx, aJSON, aTransfers, data)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
-  if (data.DataLength() >= kMinTelemetryMessageSize) {
-    Telemetry::Accumulate(Telemetry::MESSAGE_MANAGER_MESSAGE_SIZE,
-                          NS_ConvertUTF16toUTF8(aMessageName),
-                          data.DataLength());
-  }
+  RecordMessageSize(data.DataLength(), aMessageName);
 
   JS::Rooted<JSObject*> objects(aCx);
   if (aArgc >= 3 && aObjects.isObject()) {
     objects = &aObjects.toObject();
   }
 
   return DispatchAsyncMessageInternal(aCx, aMessageName, data, objects,
                                       aPrincipal);
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -945,16 +945,17 @@ GK_ATOM(onupgradeneeded, "onupgradeneede
 GK_ATOM(onussdreceived, "onussdreceived")
 GK_ATOM(onversionchange, "onversionchange")
 GK_ATOM(onvoicechange, "onvoicechange")
 GK_ATOM(onvoiceschanged, "onvoiceschanged")
 GK_ATOM(onwebkitAnimationEnd, "onwebkitAnimationEnd")
 GK_ATOM(onwebkitAnimationIteration, "onwebkitAnimationIteration")
 GK_ATOM(onwebkitAnimationStart, "onwebkitAnimationStart")
 GK_ATOM(onwebkitTransitionEnd, "onwebkitTransitionEnd")
+GK_ATOM(onwebsocket, "onwebsocket")
 GK_ATOM(onwheel, "onwheel")
 GK_ATOM(open, "open")
 GK_ATOM(optgroup, "optgroup")
 GK_ATOM(optimum, "optimum")
 GK_ATOM(option, "option")
 GK_ATOM(_or, "or")
 GK_ATOM(order, "order")
 GK_ATOM(ordinal, "ordinal")
@@ -1303,16 +1304,17 @@ GK_ATOM(viewport_maximum_scale, "viewpor
 GK_ATOM(viewport_minimum_scale, "viewport-minimum-scale")
 GK_ATOM(viewport_user_scalable, "viewport-user-scalable")
 GK_ATOM(viewport_width, "viewport-width")
 GK_ATOM(visibility, "visibility")
 GK_ATOM(visuallyselected, "visuallyselected")
 GK_ATOM(vlink, "vlink")
 GK_ATOM(vspace, "vspace")
 GK_ATOM(wbr, "wbr")
+GK_ATOM(webkitdirectory, "webkitdirectory")
 GK_ATOM(when, "when")
 GK_ATOM(where, "where")
 GK_ATOM(widget, "widget")
 GK_ATOM(width, "width")
 GK_ATOM(window, "window")
 GK_ATOM(headerWindowTarget, "window-target")
 GK_ATOM(windowtype, "windowtype")
 GK_ATOM(withParam, "with-param")
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -2318,16 +2318,25 @@ nsScriptLoader::OnStreamComplete(nsIIncr
   } else {
     nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
 
     bool enforceSRI = false;
     loadInfo->GetEnforceSRI(&enforceSRI);
     if (enforceSRI) {
       MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
              ("nsScriptLoader::OnStreamComplete, required SRI not found"));
+      nsCOMPtr<nsIContentSecurityPolicy> csp;
+      loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
+      nsAutoCString violationURISpec;
+      mDocument->GetDocumentURI()->GetAsciiSpec(violationURISpec);
+      uint32_t lineNo = request->mElement ? request->mElement->GetScriptLineNumber() : 0;
+      csp->LogViolationDetails(
+        nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
+        NS_ConvertUTF8toUTF16(violationURISpec),
+        EmptyString(), lineNo, EmptyString(), EmptyString());
       rv = NS_ERROR_SRI_CORRUPT;
     }
   }
 
   if (NS_SUCCEEDED(rv)) {
     rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString);
   }
 
--- a/dom/base/test/test_audioNotification.html
+++ b/dom/base/test/test_audioNotification.html
@@ -37,17 +37,17 @@ var tests = [
   },
 
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     audio.pause();
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/base/test/test_audioNotificationSilent_audioFile.html
+++ b/dom/base/test/test_audioNotificationSilent_audioFile.html
@@ -46,17 +46,17 @@ function audioPlayingStart() {
 
   info("Audio playing start");
   audio.play();
 }
 
 function audioBecomeSilentDuringPlaying() {
   info("Audio would become silent during playing");
 
-  expectedPlaybackActive = 'inactive';
+  expectedPlaybackActive = 'inactive-nonaudible';
   expectedPlaying = true;
 }
 
 function finish() {
   observerService.removeObserver(observer, "audio-playback");
   ok(true, "Observer removed");
 
   SimpleTest.finish();
--- a/dom/base/test/test_audioNotificationSilent_webAudio.html
+++ b/dom/base/test/test_audioNotificationSilent_webAudio.html
@@ -54,17 +54,17 @@ function audioPlayingStart() {
 
   info("Audio playing start");
   playOscillatorNode();
 }
 
 function audioBecomeSilentDuringPlaying() {
   info("Audio would become silent during playing");
 
-  expectedPlaybackActive = 'inactive';
+  expectedPlaybackActive = 'inactive-pause';
   expectedPlaying = "running";
 }
 
 function finish() {
   observerService.removeObserver(observer, "audio-playback");
   ok(true, "Observer removed");
 
   SimpleTest.finish();
--- a/dom/base/test/test_audioNotificationStopOnNavigation.html
+++ b/dom/base/test/test_audioNotificationStopOnNavigation.html
@@ -37,17 +37,17 @@ var tests = [
   },
 
   function() {
     expectedNotification = 'active';
     iframe.src = "file_audioLoop.html";
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     iframe.src = "data:text/html,page without audio";
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/base/test/test_audioNotificationStream.html
+++ b/dom/base/test/test_audioNotificationStream.html
@@ -37,17 +37,17 @@ var tests = [
   },
 
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     audio.pause();
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/base/test/test_audioNotificationWithEarlyPlay.html
+++ b/dom/base/test/test_audioNotificationWithEarlyPlay.html
@@ -39,17 +39,17 @@ var tests = [
 
   function() {
     expectedNotification = 'active';
     audio.src = "audio.ogg";
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     audio.pause();
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/base/test/test_noAudioNotificationOnMutedElement.html
+++ b/dom/base/test/test_noAudioNotificationOnMutedElement.html
@@ -45,38 +45,38 @@ var tests = [
 
   // Verify that muting and unmuting dispatches the events as expected.
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-nonaudible';
     audio.muted = true;
   },
 
   function() {
     expectedNotification = 'active';
     audio.muted = false;
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     audio.pause();
   },
 
   // Verify that no events are dispatched when muted.
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-nonaudible';
     audio.muted = true;
   },
 
   function() {
     expectedNotification = null;
     audio.onpause  = function() {
       // Yield to the event loop a few times to make sure that audio-playback is not dispatched.
       SimpleTest.executeSoon(function() {
--- a/dom/base/test/test_noAudioNotificationOnMutedOrVolume0Element.html
+++ b/dom/base/test/test_noAudioNotificationOnMutedOrVolume0Element.html
@@ -45,17 +45,17 @@ var tests = [
 
   // Verify that unmuting when the volume is 0 doesn't dispatch the events.
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-nonaudible';
     audio.muted = true;
   },
 
   function() {
     expectedNotification = null;
     audio.volume = 0;
     // Yield to the event loop a few times to make sure that audio-playback is not dispatched.
     SimpleTest.executeSoon(function() {
@@ -81,28 +81,28 @@ var tests = [
   },
 
   function() {
     expectedNotification = 'active';
     audio.volume = 0.5;
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     audio.pause();
   },
 
   // Verify that raising the volume when muted doesn't dispatch the events.
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-nonaudible';
     audio.muted = true;
   },
 
   function() {
     expectedNotification = null;
     audio.volume = 0;
     // Yield to the event loop a few times to make sure that audio-playback is not dispatched.
     SimpleTest.executeSoon(function() {
@@ -128,17 +128,17 @@ var tests = [
   },
 
   function() {
     expectedNotification = 'active';
     audio.muted = false;
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     audio.pause();
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/base/test/test_noAudioNotificationOnVolume0Element.html
+++ b/dom/base/test/test_noAudioNotificationOnVolume0Element.html
@@ -45,38 +45,38 @@ var tests = [
 
   // Verify that muting and unmuting dispatches the events as expected.
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-nonaudible';
     audio.volume = 0;
   },
 
   function() {
     expectedNotification = 'active';
     audio.volume = 1;
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     audio.pause();
   },
 
   // Verify that no events are dispatched when volume is set to 0..
   function() {
     expectedNotification = 'active';
     audio.play();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-nonaudible';
     audio.volume = 0;
   },
 
   function() {
     expectedNotification = null;
     audio.onpause  = function() {
       // Yield to the event loop a few times to make sure that audio-playback is not dispatched.
       SimpleTest.executeSoon(function() {
--- a/dom/base/test/test_pluginAudioNotification.html
+++ b/dom/base/test/test_pluginAudioNotification.html
@@ -79,17 +79,17 @@ var tests = [
     iframe.contentWindow.toggleMuteState(true);
     ok(iframe.contentWindow.pluginMuted(), "Plugin should be muted");
     iframe.contentWindow.toggleMuteState(false);
     ok(!iframe.contentWindow.pluginMuted(), "Plugin should not be muted");
     runTest();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     iframe.contentWindow.stopAudio();
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/base/test/test_webaudioNotification.html
+++ b/dom/base/test/test_webaudioNotification.html
@@ -37,27 +37,27 @@ var tests = [
   },
 
   function() {
     iframe.src = "file_webaudioLoop.html";
     expectedNotification = 'active';
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     iframe.contentWindow.suspendAC();
   },
 
   function() {
     expectedNotification = 'active';
     iframe.contentWindow.resumeAC();
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     iframe.contentWindow.closeAC();
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/base/test/test_webaudioNotificationStopOnNavigation.html
+++ b/dom/base/test/test_webaudioNotificationStopOnNavigation.html
@@ -37,17 +37,17 @@ var tests = [
   },
 
   function() {
     expectedNotification = 'active';
     iframe.src = "file_webaudioLoop2.html";
   },
 
   function() {
-    expectedNotification = 'inactive';
+    expectedNotification = 'inactive-pause';
     iframe.src = "data:text/html,page without audio";
   },
 
   function() {
     observerService.removeObserver(observer, "audio-playback");
     ok(true, "Observer removed");
     runTest();
   }
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -21,16 +21,18 @@
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocShell.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
 #include "nsIXPConnect.h"
 #include "nsUTF8Utils.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Snprintf.h"
 #include "nsGlobalWindow.h"
 
 #include "mozilla/dom/ScriptSettings.h"
@@ -51,16 +53,18 @@
 #include "mozilla/jsipc/CrossProcessObjectWrappers.h"
 #include "nsDOMClassInfo.h"
 #include "ipc/ErrorIPCUtils.h"
 #include "mozilla/UseCounter.h"
 
 namespace mozilla {
 namespace dom {
 
+using namespace workers;
+
 const JSErrorFormatString ErrorFormatString[] = {
 #define MSG_DEF(_name, _argc, _exn, _str) \
   { #_name, _str, _argc, _exn },
 #include "mozilla/dom/Errors.msg"
 #undef MSG_DEF
 };
 
 #define MSG_DEF(_name, _argc, _exn, _str) \
@@ -2496,32 +2500,30 @@ ConvertJSValueToByteString(JSContext* cx
   JS_EncodeStringToBuffer(cx, s, result.BeginWriting(), length);
 
   return true;
 }
 
 bool
 IsInPrivilegedApp(JSContext* aCx, JSObject* aObj)
 {
-  using mozilla::dom::workers::GetWorkerPrivateFromContext;
   if (!NS_IsMainThread()) {
     return GetWorkerPrivateFromContext(aCx)->IsInPrivilegedApp();
   }
 
   nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aObj);
   uint16_t appStatus = principal->GetAppStatus();
   return (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
           appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) ||
           Preferences::GetBool("dom.ignore_webidl_scope_checks", false);
 }
 
 bool
 IsInCertifiedApp(JSContext* aCx, JSObject* aObj)
 {
-  using mozilla::dom::workers::GetWorkerPrivateFromContext;
   if (!NS_IsMainThread()) {
     return GetWorkerPrivateFromContext(aCx)->IsInCertifiedApp();
   }
 
   nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aObj);
   return principal->GetAppStatus() == nsIPrincipal::APP_STATUS_CERTIFIED ||
          Preferences::GetBool("dom.ignore_webidl_scope_checks", false);
 }
@@ -3280,38 +3282,159 @@ SetDocumentAndPageUseCounter(JSContext* 
                              UseCounter aUseCounter)
 {
   nsGlobalWindow* win = xpc::WindowGlobalOrNull(js::UncheckedUnwrap(aObject));
   if (win && win->GetDocument()) {
     win->GetDocument()->SetDocumentAndPageUseCounter(aUseCounter);
   }
 }
 
+namespace {
+
+// This runnable is used to write a deprecation message from a worker to the
+// console running on the main-thread.
+class DeprecationWarningRunnable final : public Runnable
+                                       , public WorkerFeature
+{
+  WorkerPrivate* mWorkerPrivate;
+  nsIDocument::DeprecatedOperations mOperation;
+
+public:
+  DeprecationWarningRunnable(WorkerPrivate* aWorkerPrivate,
+                             nsIDocument::DeprecatedOperations aOperation)
+    : mWorkerPrivate(aWorkerPrivate)
+    , mOperation(aOperation)
+  {
+    MOZ_ASSERT(aWorkerPrivate);
+  }
+
+  void
+  Dispatch()
+  {
+    if (NS_WARN_IF(!mWorkerPrivate->AddFeature(this))) {
+      return;
+    }
+
+    if (NS_WARN_IF(NS_FAILED(NS_DispatchToMainThread(this)))) {
+      mWorkerPrivate->RemoveFeature(this);
+      return;
+    }
+  }
+
+  virtual bool
+  Notify(Status aStatus) override
+  {
+    // We don't care about the notification. We just want to keep the
+    // mWorkerPrivate alive.
+    return true;
+  }
+
+private:
+
+  NS_IMETHOD
+  Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    // Walk up to our containing page
+    WorkerPrivate* wp = mWorkerPrivate;
+    while (wp->GetParent()) {
+      wp = wp->GetParent();
+    }
+
+    nsPIDOMWindowInner* window = wp->GetWindow();
+    if (window && window->GetExtantDoc()) {
+      window->GetExtantDoc()->WarnOnceAbout(mOperation);
+    }
+
+    ReleaseWorker();
+    return NS_OK;
+  }
+
+  void
+  ReleaseWorker()
+  {
+    class ReleaseRunnable final : public WorkerRunnable
+    {
+      RefPtr<DeprecationWarningRunnable> mRunnable;
+
+    public:
+      ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
+                      DeprecationWarningRunnable* aRunnable)
+        : WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+        , mRunnable(aRunnable)
+      {}
+
+      virtual bool
+      WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+      {
+        MOZ_ASSERT(aWorkerPrivate);
+        aWorkerPrivate->AssertIsOnWorkerThread();
+
+        aWorkerPrivate->RemoveFeature(mRunnable);
+        return true;
+      }
+
+      virtual bool
+      PreDispatch(WorkerPrivate* aWorkerPrivate) override
+      {
+        AssertIsOnMainThread();
+        return true;
+      }
+
+      virtual void
+      PostDispatch(WorkerPrivate* aWorkerPrivate,
+                   bool aDispatchResult) override
+      {
+      }
+    };
+
+    RefPtr<ReleaseRunnable> runnable =
+      new ReleaseRunnable(mWorkerPrivate, this);
+    NS_WARN_IF(!runnable->Dispatch());
+  }
+};
+
+} // anonymous namespace
+
 void
 DeprecationWarning(JSContext* aCx, JSObject* aObject,
                    nsIDocument::DeprecatedOperations aOperation)
 {
   GlobalObject global(aCx, aObject);
   if (global.Failed()) {
     NS_ERROR("Could not create global for DeprecationWarning");
     return;
   }
 
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
-  if (window && window->GetExtantDoc()) {
-    window->GetExtantDoc()->WarnOnceAbout(aOperation);
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(global.GetAsSupports());
+    if (window && window->GetExtantDoc()) {
+      window->GetExtantDoc()->WarnOnceAbout(aOperation);
+    }
+
+    return;
   }
+
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
+  if (!workerPrivate) {
+    return;
+  }
+
+  RefPtr<DeprecationWarningRunnable> runnable =
+    new DeprecationWarningRunnable(workerPrivate, aOperation);
+  runnable->Dispatch();
 }
 
 namespace binding_detail {
 JSObject*
 UnprivilegedJunkScopeOrWorkerGlobal()
 {
   if (NS_IsMainThread()) {
     return xpc::UnprivilegedJunkScope();
   }
 
-  return workers::GetCurrentThreadWorkerGlobal();
+  return GetCurrentThreadWorkerGlobal();
 }
 } // namespace binding_detail
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1890,16 +1890,29 @@ struct FakeString {
   }
 
   void Rebind(const nsString::char_type* aData, nsString::size_type aLength) {
     MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
     mData = const_cast<nsString::char_type*>(aData);
     mLength = aLength;
   }
 
+  // Share aString's string buffer, if it has one; otherwise, make this string
+  // depend upon aString's data.  aString should outlive this instance of
+  // FakeString.
+  void ShareOrDependUpon(const nsAString& aString) {
+    RefPtr<nsStringBuffer> sharedBuffer = nsStringBuffer::FromString(aString);
+    if (!sharedBuffer) {
+      Rebind(aString.Data(), aString.Length());
+    } else {
+      AssignFromStringBuffer(sharedBuffer.forget());
+      mLength = aString.Length();
+    }
+  }
+
   void Truncate() {
     MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
     mData = nsString::char_traits::sEmptyBuffer;
     mLength = 0;
   }
 
   void SetIsVoid(bool aValue) {
     MOZ_ASSERT(aValue,
@@ -1924,23 +1937,22 @@ struct FakeString {
   }
 
   // Reserve space to write aLength chars, not including null-terminator.
   bool SetLength(nsString::size_type aLength, mozilla::fallible_t const&) {
     // Use mInlineStorage for small strings.
     if (aLength < sInlineCapacity) {
       SetData(mInlineStorage);
     } else {
-      nsStringBuffer *buf = nsStringBuffer::Alloc((aLength + 1) * sizeof(nsString::char_type)).take();
+      RefPtr<nsStringBuffer> buf = nsStringBuffer::Alloc((aLength + 1) * sizeof(nsString::char_type));
       if (MOZ_UNLIKELY(!buf)) {
         return false;
       }
 
-      SetData(static_cast<nsString::char_type*>(buf->Data()));
-      mFlags = nsString::F_SHARED | nsString::F_TERMINATED;
+      AssignFromStringBuffer(buf.forget());
     }
     mLength = aLength;
     mData[mLength] = char16_t(0);
     return true;
   }
 
   // If this ever changes, change the corresponding code in the
   // Optional<nsAString> specialization as well.
@@ -1966,16 +1978,20 @@ private:
 
   FakeString(const FakeString& other) = delete;
   void operator=(const FakeString& other) = delete;
 
   void SetData(nsString::char_type* aData) {
     MOZ_ASSERT(mFlags == nsString::F_TERMINATED);
     mData = const_cast<nsString::char_type*>(aData);
   }
+  void AssignFromStringBuffer(already_AddRefed<nsStringBuffer> aBuffer) {
+    SetData(static_cast<nsString::char_type*>(aBuffer.take()->Data()));
+    mFlags = nsString::F_SHARED | nsString::F_TERMINATED;
+  }
 
   friend class NonNull<nsAString>;
 
   // A class to use for our static asserts to ensure our object layout
   // matches that of nsString.
   class StringAsserter;
   friend class StringAsserter;
 
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -198,17 +198,17 @@ GetCurrentJSStack(int32_t aMaxDepth)
 {
   // is there a current context available?
   JSContext* cx = nsContentUtils::GetCurrentJSContextForThread();
 
   if (!cx || !js::GetContextCompartment(cx)) {
     return nullptr;
   }
 
-  return exceptions::CreateStack(cx, aMaxDepth);
+  return dom::exceptions::CreateStack(cx, aMaxDepth);
 }
 
 AutoForceSetExceptionOnContext::AutoForceSetExceptionOnContext(JSContext* aCx)
   : mCx(aCx)
 {
   mOldValue = JS::ContextOptionsRef(mCx).autoJSAPIOwnsErrorReporting();
   JS::ContextOptionsRef(mCx).setAutoJSAPIOwnsErrorReporting(true);
 }
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_ActiveStateChange.js
@@ -0,0 +1,110 @@
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+var fileURL = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_ActiveStateChange.html';
+var generator = runTests();
+var testFrame;
+var ac;
+
+function assert(aVal, aMessage) {
+  return (!aVal) ? error(aMessage) : 0;
+}
+
+function error(aMessage) {
+  ok(false, "Error : " + aMessage);
+  finish();
+}
+
+function continueTest() {
+  try {
+    generator.next();
+  } catch (e if e instanceof StopIteration) {
+    error("Stop test because of exception!");
+  }
+}
+
+function finish() {
+  document.body.removeChild(testFrame);
+  SimpleTest.finish();
+}
+
+function setCommand(aArg) {
+  assert(!!ac, "Audio channel doesn't exist!");
+  info("# Command = " + aArg);
+
+  testFrame.src = fileURL + '#' + aArg;
+  var expectedActive = false;
+  switch (aArg) {
+    case 'play':
+      expectedActive = true;
+      break;
+    case 'pause':
+      expectedActive = false;
+      break;
+    default :
+      error("Undefined command!");
+  }
+
+  ac.onactivestatechanged = () => {
+    ac.onactivestatechanged = null;
+    ac.isActive().onsuccess = (e) => {
+      is(expectedActive, e.target.result,
+         "Correct active state = " + expectedActive);
+      continueTest();
+    }
+  };
+}
+
+function runTests() {
+  setCommand('play');
+  yield undefined;
+
+  setCommand('pause');
+  yield undefined;
+
+  finish();
+  yield undefined;
+}
+
+function setupTestFrame() {
+  testFrame = document.createElement('iframe');
+  testFrame.setAttribute('mozbrowser', 'true');
+  testFrame.setAttribute('mozapp', 'http://example.org/manifest.webapp');
+  testFrame.src = fileURL;
+
+  function loadend() {
+    testFrame.removeEventListener('mozbrowserloadend', loadend);
+    ok("allowedAudioChannels" in testFrame, "allowedAudioChannels exist");
+    var channels = testFrame.allowedAudioChannels;
+    is(channels.length, 1, "1 audio channel by default");
+
+    ac = channels[0];
+
+    ok(ac instanceof BrowserElementAudioChannel, "Correct class");
+    ok("isActive" in ac, "isActive exists");
+    ok("onactivestatechanged" in ac, "onactivestatechanged exists");
+
+    generator.next();
+  }
+
+  function alertError(e) {
+    testFrame.removeEventListener('mozbrowsershowmodalprompt', alertError);
+    var message = e.detail.message
+    error(message);
+  }
+
+  testFrame.addEventListener('mozbrowserloadend', loadend);
+  testFrame.addEventListener('mozbrowsershowmodalprompt', alertError);
+  document.body.appendChild(testFrame);
+}
+
+addEventListener('testready', function() {
+  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
+                            function() {
+    SimpleTest.executeSoon(setupTestFrame);
+  });
+});
+
deleted file mode 100644
--- a/dom/browser-element/mochitest/browserElement_ActiveStateChangeOnChangingMutedOrVolume.js
+++ /dev/null
@@ -1,126 +0,0 @@
-"use strict";
-
-SimpleTest.waitForExplicitFinish();
-browserElementTestHelpers.setEnabledPref(true);
-browserElementTestHelpers.addPermission();
-
-var fileURL = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_ActiveStateChangeOnChangingMutedOrVolume.html';
-var generator = runTests();
-var testFrame;
-var ac;
-
-function assert(aVal, aMessage) {
-  return (!aVal) ? error(aMessage) : 0;
-}
-
-function error(aMessage) {
-  ok(false, "Error : " + aMessage);
-  finish();
-}
-
-function continueTest() {
-  try {
-    generator.next();
-  } catch (e if e instanceof StopIteration) {
-    error("Stop test because of exception!");
-  }
-}
-
-function finish() {
-  document.body.removeChild(testFrame);
-  SimpleTest.finish();
-}
-
-function setCommand(aArg) {
-  assert(!!ac, "Audio channel doesn't exist!");
-  info("# Command = " + aArg);
-
-  testFrame.src = fileURL + '#' + aArg;
-  var expectedActive = false;
-  switch (aArg) {
-    case 'play':
-    case 'unmute':
-    case 'volume-1':
-      expectedActive = true;
-      break;
-    case 'pause':
-    case 'mute':
-    case 'volume-0':
-      expectedActive = false;
-      break;
-    default :
-      error("Undefined command!");
-  }
-
-  ac.onactivestatechanged = () => {
-    ac.onactivestatechanged = null;
-    ac.isActive().onsuccess = (e) => {
-      is(expectedActive, e.target.result,
-         "Correct active state = " + expectedActive);
-      continueTest();
-    }
-  };
-}
-
-function runTests() {
-  setCommand('play');
-  yield undefined;
-
-  setCommand('mute');
-  yield undefined;
-
-  setCommand('unmute');
-  yield undefined;
-
-  setCommand('volume-0');
-  yield undefined;
-
-  setCommand('volume-1');
-  yield undefined;
-
-  setCommand('pause');
-  yield undefined;
-
-  finish();
-  yield undefined;
-}
-
-function setupTestFrame() {
-  testFrame = document.createElement('iframe');
-  testFrame.setAttribute('mozbrowser', 'true');
-  testFrame.setAttribute('mozapp', 'http://example.org/manifest.webapp');
-  testFrame.src = fileURL;
-
-  function loadend() {
-    testFrame.removeEventListener('mozbrowserloadend', loadend);
-    ok("allowedAudioChannels" in testFrame, "allowedAudioChannels exist");
-    var channels = testFrame.allowedAudioChannels;
-    is(channels.length, 1, "1 audio channel by default");
-
-    ac = channels[0];
-
-    ok(ac instanceof BrowserElementAudioChannel, "Correct class");
-    ok("isActive" in ac, "isActive exists");
-    ok("onactivestatechanged" in ac, "onactivestatechanged exists");
-
-    generator.next();
-  }
-
-  function alertError(e) {
-    testFrame.removeEventListener('mozbrowsershowmodalprompt', alertError);
-    var message = e.detail.message
-    error(message);
-  }
-
-  testFrame.addEventListener('mozbrowserloadend', loadend);
-  testFrame.addEventListener('mozbrowsershowmodalprompt', alertError);
-  document.body.appendChild(testFrame);
-}
-
-addEventListener('testready', function() {
-  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
-                            function() {
-    SimpleTest.executeSoon(setupTestFrame);
-  });
-});
-
--- a/dom/browser-element/mochitest/browserElement_AudioPlayback.js
+++ b/dom/browser-element/mochitest/browserElement_AudioPlayback.js
@@ -42,25 +42,25 @@ function runTest() {
   // an audio element and play it.
   iframe.addEventListener('mozbrowserloadend', () => {
     let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
     mm.loadFrameScript('data:,(' + playAudioScript.toString() + ')();', false);
   });
 
   // Two events should come in, when the audio starts, and stops playing.
   // The first one should have a detail of 'active' and the second one
-  // should have a detail of 'inactive'.
+  // should have a detail of 'inactive-pause'.
   let expectedNextData = 'active';
   iframe.addEventListener('mozbrowseraudioplaybackchange', (e) => {
     is(e.detail, expectedNextData, 'Audio detail should be correct')
     is(e.target, iframe, 'event target should be the first iframe')
-    if (e.detail === 'inactive') {
+    if (e.detail === 'inactive-pause') {
       SimpleTest.finish();
     }
-    expectedNextData = 'inactive';
+    expectedNextData = 'inactive-pause';
   });
 
   // Make sure an event only goes to the first iframe.
   iframe2.addEventListener('mozbrowseraudioplaybackchange', (e) => {
     ok(false,
        'mozbrowseraudioplaybackchange should dispatch to the correct browser');
   });
 
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_ActiveStateChange.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<script type="application/javascript;version=1.7">
+var audio = new Audio();
+audio.src = "audio.ogg";
+audio.loop = true;
+
+function runCommands()
+{
+  switch(location.hash) {
+    case '#play':
+      audio.play();
+      break;
+    case '#pause':
+      audio.pause();
+      break;
+    default :
+      alert("Undefined command!");
+  }
+}
+window.addEventListener('hashchange', runCommands);
+</script>
+</body>
+</html>
\ No newline at end of file
deleted file mode 100644
--- a/dom/browser-element/mochitest/file_browserElement_ActiveStateChangeOnChangingMutedOrVolume.html
+++ /dev/null
@@ -1,37 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<body>
-<script type="application/javascript;version=1.7">
-var audio = new Audio();
-audio.src = "audio.ogg";
-audio.loop = true;
-
-function runCommands()
-{
-  switch(location.hash) {
-    case '#play':
-      audio.play();
-      break;
-    case '#mute':
-      audio.muted = true;
-      break;
-    case '#unmute':
-      audio.muted = false;
-      break;
-    case '#volume-0':
-      audio.volume = 0.0;
-      break;
-    case '#volume-1':
-      audio.volume = 1.0;
-      break;
-    case '#pause':
-      audio.pause();
-      break;
-    default :
-      alert("Undefined command!");
-  }
-}
-window.addEventListener('hashchange', runCommands);
-</script>
-</body>
-</html>
\ No newline at end of file
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -125,10 +125,10 @@ disabled = bug 924771
 [test_browserElement_oop_AudioChannel.html]
 tags = audiochannel
 [test_browserElement_oop_AudioChannel_nested.html]
 tags = audiochannel
 [test_browserElement_oop_SetNFCFocus.html]
 [test_browserElement_oop_getWebManifest.html]
 [test_browserElement_oop_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
-[test_browserElement_oop_ActiveStateChangeOnChangingMutedOrVolume.html]
+[test_browserElement_oop_ActiveStateChange.html]
 tags = audiochannel
\ No newline at end of file
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -1,16 +1,16 @@
 [DEFAULT]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
 support-files =
   audio.ogg
   ../../../dom/media/test/short-video.ogv
   async.js
   browserElementTestHelpers.js
-  browserElement_ActiveStateChangeOnChangingMutedOrVolume.js
+  browserElement_ActiveStateChange.js
   browserElement_Alert.js
   browserElement_AlertInFrame.js
   browserElement_AllowEmbedAppsInNestedOOIframe.js
   browserElement_AppFramePermission.js
   browserElement_AppWindowNamespace.js
   browserElement_AudioChannelSeeking.js
   browserElement_AudioChannelMutedByDefault.js
   browserElement_AudioPlayback.js
@@ -80,17 +80,17 @@ support-files =
   browserElement_VisibilityChange.js
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_GetContentDimensions.js
   browserElement_AudioChannel.js
   browserElement_AudioChannel_nested.js
-  file_browserElement_ActiveStateChangeOnChangingMutedOrVolume.html
+  file_browserElement_ActiveStateChange.html
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AllowEmbedAppsInNestedOOIframe.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
   file_browserElement_AudioChannelSeeking.html
   file_browserElement_AudioChannel_nested.html
   file_browserElement_AudioChannelMutedByDefault.html
@@ -262,10 +262,10 @@ disabled = bug 774100
 [test_browserElement_inproc_GetContentDimensions.html]
 [test_browserElement_inproc_AudioChannel.html]
 tags = audiochannel
 [test_browserElement_inproc_AudioChannel_nested.html]
 tags = audiochannel
 [test_browserElement_inproc_SetNFCFocus.html]
 [test_browserElement_inproc_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
-[test_browserElement_inproc_ActiveStateChangeOnChangingMutedOrVolume.html]
+[test_browserElement_inproc_ActiveStateChange.html]
 tags = audiochannel
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_ActiveStateChange.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test ActiveStateChangeOnChangingMutedOrVolume</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_ActiveStateChange.js">
+</script>
+</body>
+</html>
deleted file mode 100644
--- a/dom/browser-element/mochitest/test_browserElement_inproc_ActiveStateChangeOnChangingMutedOrVolume.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test ActiveStateChangeOnChangingMutedOrVolume</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<script type="application/javascript;version=1.7" src="browserElement_ActiveStateChangeOnChangingMutedOrVolume.js">
-</script>
-</body>
-</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_ActiveStateChange.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test ActiveStateChangeOnChangingMutedOrVolume</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript;version=1.7" src="browserElement_ActiveStateChange.js">
+</script>
+</body>
+</html>
deleted file mode 100644
--- a/dom/browser-element/mochitest/test_browserElement_oop_ActiveStateChangeOnChangingMutedOrVolume.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test ActiveStateChangeOnChangingMutedOrVolume</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<script type="application/javascript;version=1.7" src="browserElement_ActiveStateChangeOnChangingMutedOrVolume.js">
-</script>
-</body>
-</html>
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -276,17 +276,17 @@ TypeUtils::ToResponse(const CacheRespons
 
   ir->InitChannelInfo(aIn.channelInfo());
   if (aIn.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
     UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(aIn.principalInfo().get_PrincipalInfo()));
     ir->SetPrincipalInfo(Move(info));
   }
 
   nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
-  ir->SetBody(stream);
+  ir->SetBody(stream, InternalResponse::UNKNOWN_BODY_SIZE);
 
   switch (aIn.type())
   {
     case ResponseType::Basic:
       ir = ir->BasicResponse();
       break;
     case ResponseType::Cors:
       ir = ir->CORSResponse();
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/reftest/clip-multiple-move-1-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<canvas id="canvas" width="100" height="100"></canvas>
+<script>
+
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 100);
+
+ctx.beginPath();
+ctx.rect(30, 30, 40, 40);
+ctx.clip();
+
+ctx.fillStyle = '#f00';
+
+ctx.fillRect(0, 0, 100, 100);
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/reftest/clip-multiple-move-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<canvas id="canvas" width="100" height="100"></canvas>
+
+<script>
+
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 100);
+
+ctx.beginPath();
+ctx.moveTo(100, 30);
+ctx.moveTo(30, 30);
+ctx.lineTo(30, 70);
+ctx.lineTo(70, 70);
+ctx.lineTo(70, 30);
+ctx.closePath();
+ctx.clip();
+
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 100);
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/reftest/clip-multiple-move-2-ref.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<canvas id="canvas" width="150" height="150"></canvas>
+<script>
+
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 150, 150);
+
+</script>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/reftest/clip-multiple-move-2.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<canvas id="canvas" width="150" height="150"></canvas>
+
+<script>
+
+var canvas = document.getElementById('canvas');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 150, 150);
+
+ctx.beginPath();
+ctx.moveTo(0, 0);
+ctx.moveTo(0, -1);
+ctx.lineTo(0, 150);
+ctx.lineTo(150, 150);
+
+// The coordinate '149.99999' makes skia use GrConvexPolyEffect to handle the points.
+// The result should be same as '150'. So this test checks if the GrConvexPolyEffect
+// works well.
+ctx.lineTo(149.99999, -1);
+
+ctx.closePath();
+ctx.clip();
+
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 150, 150);
+
+</script>
+</body></html>
--- a/dom/canvas/test/reftest/reftest.list
+++ b/dom/canvas/test/reftest/reftest.list
@@ -143,16 +143,20 @@ fuzzy(9,40000) skip-if(!(Android||B2G)) 
 skip-if(!winWidget) pref(webgl.disable-angle,true)  == webgl-color-test.html?native-gl  wrapper.html?colors-no-alpha.png
 
 
 # Non-WebGL Reftests!
 
 # Do we correctly handle multiple clip paths?
 != clip-multiple-paths.html clip-multiple-paths-badref.html
 
+# Bug 1255062
+== clip-multiple-move-1.html clip-multiple-move-1-ref.html
+== clip-multiple-move-2.html clip-multiple-move-2-ref.html
+
 # Bug 815648
 == stroketext-shadow.html stroketext-shadow-ref.html
 
 # focus rings
 pref(canvas.focusring.enabled,true) skip-if(B2G) skip-if(cocoaWidget) skip-if(winWidget) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html
 pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(cocoaWidget) skip-if(Android) skip-if(winWidget) fuzzy-if(gtkWidget,64,410) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html
 
 # Check that captureStream() displays in a local video element
--- a/dom/events/JSEventHandler.cpp
+++ b/dom/events/JSEventHandler.cpp
@@ -138,17 +138,17 @@ JSEventHandler::HandleEvent(nsIDOMEvent*
     Optional<uint32_t> lineNumber;
     Optional<uint32_t> columnNumber;
     Optional<JS::Handle<JS::Value>> error;
 
     NS_ENSURE_TRUE(aEvent, NS_ERROR_UNEXPECTED);
     ErrorEvent* scriptEvent = aEvent->InternalDOMEvent()->AsErrorEvent();
     if (scriptEvent) {
       scriptEvent->GetMessage(errorMsg);
-      msgOrEvent.SetAsString().Rebind(errorMsg.Data(), errorMsg.Length());
+      msgOrEvent.SetAsString().ShareOrDependUpon(errorMsg);
 
       scriptEvent->GetFilename(file);
       fileName = &file;
 
       lineNumber.Construct();
       lineNumber.Value() = scriptEvent->Lineno();
 
       columnNumber.Construct();
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -444,67 +444,80 @@ WorkerFetchResolver::OnResponseEnd()
       NS_WARNING("Failed to dispatch WorkerFetchResponseEndControlRunnable");
     }
   }
 }
 
 namespace {
 nsresult
 ExtractFromArrayBuffer(const ArrayBuffer& aBuffer,
-                       nsIInputStream** aStream)
+                       nsIInputStream** aStream,
+                       uint64_t& aContentLength)
 {
   aBuffer.ComputeLengthAndData();
+  aContentLength = aBuffer.Length();
   //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
   return NS_NewByteInputStream(aStream,
                                reinterpret_cast<char*>(aBuffer.Data()),
                                aBuffer.Length(), NS_ASSIGNMENT_COPY);
 }
 
 nsresult
 ExtractFromArrayBufferView(const ArrayBufferView& aBuffer,
-                           nsIInputStream** aStream)
+                           nsIInputStream** aStream,
+                           uint64_t& aContentLength)
 {
   aBuffer.ComputeLengthAndData();
+  aContentLength = aBuffer.Length();
   //XXXnsm reinterpret_cast<> is used in DOMParser, should be ok.
   return NS_NewByteInputStream(aStream,
                                reinterpret_cast<char*>(aBuffer.Data()),
                                aBuffer.Length(), NS_ASSIGNMENT_COPY);
 }
 
 nsresult
-ExtractFromBlob(const Blob& aBlob, nsIInputStream** aStream,
-                nsCString& aContentType)
+ExtractFromBlob(const Blob& aBlob,
+                nsIInputStream** aStream,
+                nsCString& aContentType,
+                uint64_t& aContentLength)
 {
   RefPtr<BlobImpl> impl = aBlob.Impl();
   ErrorResult rv;
+  aContentLength = impl->GetSize(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
   impl->GetInternalStream(aStream, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   nsAutoString type;
   impl->GetType(type);
   aContentType = NS_ConvertUTF16toUTF8(type);
   return NS_OK;
 }
 
 nsresult
-ExtractFromFormData(FormData& aFormData, nsIInputStream** aStream,
-                    nsCString& aContentType)
+ExtractFromFormData(FormData& aFormData,
+                    nsIInputStream** aStream,
+                    nsCString& aContentType,
+                    uint64_t& aContentLength)
 {
-  uint64_t unusedContentLength;
   nsAutoCString unusedCharset;
-  return aFormData.GetSendInfo(aStream, &unusedContentLength,
+  return aFormData.GetSendInfo(aStream, &aContentLength,
                                aContentType, unusedCharset);
 }
 
 nsresult
 ExtractFromUSVString(const nsString& aStr,
                      nsIInputStream** aStream,
-                     nsCString& aContentType)
+                     nsCString& aContentType,
+                     uint64_t& aContentLength)
 {
   nsCOMPtr<nsIUnicodeEncoder> encoder = EncodingUtils::EncoderForEncoding("UTF-8");
   if (!encoder) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   int32_t destBufferLen;
   nsresult rv = encoder->GetMaxLength(aStr.get(), aStr.Length(), &destBufferLen);
@@ -524,90 +537,106 @@ ExtractFromUSVString(const nsString& aSt
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   MOZ_ASSERT(outLen <= destBufferLen);
   encoded.SetLength(outLen);
 
   aContentType = NS_LITERAL_CSTRING("text/plain;charset=UTF-8");
+  aContentLength = outLen;
 
   return NS_NewCStringInputStream(aStream, encoded);
 }
 
 nsresult
 ExtractFromURLSearchParams(const URLSearchParams& aParams,
                            nsIInputStream** aStream,
-                           nsCString& aContentType)
+                           nsCString& aContentType,
+                           uint64_t& aContentLength)
 {
   nsAutoString serialized;
   aParams.Stringify(serialized);
   aContentType = NS_LITERAL_CSTRING("application/x-www-form-urlencoded;charset=UTF-8");
+  aContentLength = serialized.Length();
   return NS_NewCStringInputStream(aStream, NS_ConvertUTF16toUTF8(serialized));
 }
 } // namespace
 
 nsresult
 ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType)
+                          nsCString& aContentType,
+                          uint64_t& aContentLength)
 {
   MOZ_ASSERT(aStream);
 
   if (aBodyInit.IsArrayBuffer()) {
     const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
-    return ExtractFromArrayBuffer(buf, aStream);
-  } else if (aBodyInit.IsArrayBufferView()) {
+    return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsArrayBufferView()) {
     const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
-    return ExtractFromArrayBufferView(buf, aStream);
-  } else if (aBodyInit.IsBlob()) {
+    return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsBlob()) {
     const Blob& blob = aBodyInit.GetAsBlob();
-    return ExtractFromBlob(blob, aStream, aContentType);
-  } else if (aBodyInit.IsFormData()) {
+    return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsFormData()) {
     FormData& form = aBodyInit.GetAsFormData();
-    return ExtractFromFormData(form, aStream, aContentType);
-  } else if (aBodyInit.IsUSVString()) {
+    return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsUSVString()) {
     nsAutoString str;
     str.Assign(aBodyInit.GetAsUSVString());
-    return ExtractFromUSVString(str, aStream, aContentType);
-  } else if (aBodyInit.IsURLSearchParams()) {
+    return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsURLSearchParams()) {
     URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
-    return ExtractFromURLSearchParams(params, aStream, aContentType);
+    return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
   }
 
   NS_NOTREACHED("Should never reach here");
   return NS_ERROR_FAILURE;
 }
 
 nsresult
 ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType)
+                          nsCString& aContentType,
+                          uint64_t& aContentLength)
 {
   MOZ_ASSERT(aStream);
+  MOZ_ASSERT(!*aStream);
 
   if (aBodyInit.IsArrayBuffer()) {
     const ArrayBuffer& buf = aBodyInit.GetAsArrayBuffer();
-    return ExtractFromArrayBuffer(buf, aStream);
-  } else if (aBodyInit.IsArrayBufferView()) {
+    return ExtractFromArrayBuffer(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsArrayBufferView()) {
     const ArrayBufferView& buf = aBodyInit.GetAsArrayBufferView();
-    return ExtractFromArrayBufferView(buf, aStream);
-  } else if (aBodyInit.IsBlob()) {
+    return ExtractFromArrayBufferView(buf, aStream, aContentLength);
+  }
+  if (aBodyInit.IsBlob()) {
     const Blob& blob = aBodyInit.GetAsBlob();
-    return ExtractFromBlob(blob, aStream, aContentType);
-  } else if (aBodyInit.IsFormData()) {
+    return ExtractFromBlob(blob, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsFormData()) {
     FormData& form = aBodyInit.GetAsFormData();
-    return ExtractFromFormData(form, aStream, aContentType);
-  } else if (aBodyInit.IsUSVString()) {
+    return ExtractFromFormData(form, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsUSVString()) {
     nsAutoString str;
     str.Assign(aBodyInit.GetAsUSVString());
-    return ExtractFromUSVString(str, aStream, aContentType);
-  } else if (aBodyInit.IsURLSearchParams()) {
+    return ExtractFromUSVString(str, aStream, aContentType, aContentLength);
+  }
+  if (aBodyInit.IsURLSearchParams()) {
     URLSearchParams& params = aBodyInit.GetAsURLSearchParams();
-    return ExtractFromURLSearchParams(params, aStream, aContentType);
+    return ExtractFromURLSearchParams(params, aStream, aContentType, aContentLength);
   }
 
   NS_NOTREACHED("Should never reach here");
   return NS_ERROR_FAILURE;
 }
 
 namespace {
 /*
--- a/dom/fetch/Fetch.h
+++ b/dom/fetch/Fetch.h
@@ -45,25 +45,27 @@ UpdateRequestReferrer(nsIGlobalObject* a
 /*
  * Creates an nsIInputStream based on the fetch specifications 'extract a byte
  * stream algorithm' - http://fetch.spec.whatwg.org/#concept-bodyinit-extract.
  * Stores content type in out param aContentType.
  */
 nsresult
 ExtractByteStreamFromBody(const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType);
+                          nsCString& aContentType,
+                          uint64_t& aContentLength);
 
 /*
  * Non-owning version.
  */
 nsresult
 ExtractByteStreamFromBody(const ArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& aBodyInit,
                           nsIInputStream** aStream,
-                          nsCString& aContentType);
+                          nsCString& aContentType,
+                          uint64_t& aContentLength);
 
 template <class Derived> class FetchBodyFeature;
 
 /*
  * FetchBody's body consumption uses nsIInputStreamPump to read from the
  * underlying stream to a block of memory, which is then adopted by
  * ContinueConsumeBody() and converted to the right type based on the JS
  * function called.
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -483,16 +483,20 @@ FetchDriver::OnStartRequest(nsIRequest* 
   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
 
   // On a successful redirect we perform the following substeps of HTTP Fetch,
   // step 5, "redirect status", step 11.
 
   bool foundOpaqueRedirect = false;
 
+  int64_t contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
+  rv = channel->GetContentLength(&contentLength);
+  MOZ_ASSERT_IF(NS_FAILED(rv), contentLength == InternalResponse::UNKNOWN_BODY_SIZE);
+
   if (httpChannel) {
     uint32_t responseStatus;
     httpChannel->GetResponseStatus(&responseStatus);
 
     if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) {
       if (mRequest->GetRedirectMode() == RequestRedirect::Error) {
         FailWithNetworkError();
         return NS_BINDING_FAILED;
@@ -507,16 +511,27 @@ FetchDriver::OnStartRequest(nsIRequest* 
 
     response = new InternalResponse(responseStatus, statusText);
 
     RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response);
     rv = httpChannel->VisitResponseHeaders(visitor);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       NS_WARNING("Failed to visit all headers.");
     }
+
+    // If Content-Encoding or Transfer-Encoding headers are set, then the actual
+    // Content-Length (which refer to the decoded data) is obscured behind the encodings.
+    ErrorResult result;
+    if (response->Headers()->Has(NS_LITERAL_CSTRING("content-encoding"), result) ||
+        response->Headers()->Has(NS_LITERAL_CSTRING("transfer-encoding"), result)) {
+      NS_WARNING("Cannot know response Content-Length due to presence of Content-Encoding "
+                 "or Transfer-Encoding headers.");
+      contentLength = InternalResponse::UNKNOWN_BODY_SIZE;
+    }
+    MOZ_ASSERT(!result.Failed());
   } else {
     response = new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
 
     ErrorResult result;
     nsAutoCString contentType;
     rv = channel->GetContentType(contentType);
     if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) {
       nsAutoCString contentCharset;
@@ -526,19 +541,17 @@ FetchDriver::OnStartRequest(nsIRequest* 
       }
 
       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"),
                                   contentType,
                                   result);
       MOZ_ASSERT(!result.Failed());
     }
 
-    int64_t contentLength;
-    rv = channel->GetContentLength(&contentLength);
-    if (NS_SUCCEEDED(rv) && contentLength) {
+    if (contentLength > 0) {
       nsAutoCString contentLenStr;
       contentLenStr.AppendInt(contentLength);
       response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"),
                                   contentLenStr,
                                   result);
       MOZ_ASSERT(!result.Failed());
     }
   }
@@ -556,17 +569,17 @@ FetchDriver::OnStartRequest(nsIRequest* 
                   UINT32_MAX /* infinite pipe */,
                   true /* non-blocking input, otherwise you deadlock */,
                   false /* blocking output, since the pipe is 'in'finite */ );
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
     return rv;
   }
-  response->SetBody(pipeInputStream);
+  response->SetBody(pipeInputStream, contentLength);
 
   response->InitChannelInfo(channel);
 
   nsCOMPtr<nsIURI> channelURI;
   rv = channel->GetURI(getter_AddRefs(channelURI));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailWithNetworkError();
     // Cancel request.
--- a/dom/fetch/InternalResponse.cpp
+++ b/dom/fetch/InternalResponse.cpp
@@ -16,16 +16,17 @@
 namespace mozilla {
 namespace dom {
 
 InternalResponse::InternalResponse(uint16_t aStatus, const nsACString& aStatusText)
   : mType(ResponseType::Default)
   , mStatus(aStatus)
   , mStatusText(aStatusText)
   , mHeaders(new InternalHeaders(HeadersGuardEnum::Response))
+  , mBodySize(UNKNOWN_BODY_SIZE)
 {
 }
 
 InternalResponse::~InternalResponse()
 {
 }
 
 already_AddRefed<InternalResponse>
--- a/dom/fetch/InternalResponse.h
+++ b/dom/fetch/InternalResponse.h
@@ -171,47 +171,60 @@ public:
     if (mWrappedResponse) {
       return mWrappedResponse->Headers();
     };
 
     return Headers();
   }
 
   void
-  GetUnfilteredBody(nsIInputStream** aStream)
+  GetUnfilteredBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
   {
     if (mWrappedResponse) {
       MOZ_ASSERT(!mBody);
-      return mWrappedResponse->GetBody(aStream);
+      return mWrappedResponse->GetBody(aStream, aBodySize);
     }
     nsCOMPtr<nsIInputStream> stream = mBody;
     stream.forget(aStream);
+    if (aBodySize) {
+      *aBodySize = mBodySize;
+    }
   }
 
   void
-  GetBody(nsIInputStream** aStream)
+  GetBody(nsIInputStream** aStream, int64_t* aBodySize = nullptr)
   {
     if (Type() == ResponseType::Opaque ||
         Type() == ResponseType::Opaqueredirect) {
       *aStream = nullptr;
+      if (aBodySize) {
+        *aBodySize = UNKNOWN_BODY_SIZE;
+      }
       return;
     }
 
-    return GetUnfilteredBody(aStream);
+    return GetUnfilteredBody(aStream, aBodySize);
   }
 
   void
-  SetBody(nsIInputStream* aBody)
+  SetBody(nsIInputStream* aBody, int64_t aBodySize)
   {
     if (mWrappedResponse) {
-      return mWrappedResponse->SetBody(aBody);
+      return mWrappedResponse->SetBody(aBody, aBodySize);
     }
     // A request's body may not be reset once set.
     MOZ_ASSERT(!mBody);
+    MOZ_ASSERT(mBodySize == UNKNOWN_BODY_SIZE);
+    // Check arguments.
+    MOZ_ASSERT(aBodySize == UNKNOWN_BODY_SIZE || aBodySize >= 0);
+    // If body is not given, then size must be unknown.
+    MOZ_ASSERT_IF(!aBody, aBodySize == UNKNOWN_BODY_SIZE);
+
     mBody = aBody;
+    mBodySize = aBodySize;
   }
 
   void
   InitChannelInfo(nsIChannel* aChannel)
   {
     mChannelInfo.InitFromChannel(aChannel);
   }
 
@@ -271,16 +284,20 @@ private:
   // A response has an associated url list (a list of zero or more fetch URLs).
   // Unless stated otherwise, it is the empty list. The current url is the last
   // element in mURLlist
   nsTArray<nsCString> mURLList;
   const uint16_t mStatus;
   const nsCString mStatusText;
   RefPtr<InternalHeaders> mHeaders;
   nsCOMPtr<nsIInputStream> mBody;
+  int64_t mBodySize;
+public:
+  static const int64_t UNKNOWN_BODY_SIZE = -1;
+private:
   ChannelInfo mChannelInfo;
   UniquePtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
 
   // For filtered responses.
   // Cache, and SW interception should always serialize/access the underlying
   // unfiltered headers and when deserializing, create an InternalResponse
   // with the unfiltered headers followed by wrapping it.
   RefPtr<InternalResponse> mWrappedResponse;
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -33,16 +33,19 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Request::Request(nsIGlobalObject* aOwner, InternalRequest* aRequest)
   : FetchBody<Request>()
   , mOwner(aOwner)
   , mRequest(aRequest)
 {
+  MOZ_ASSERT(aRequest->Headers()->Guard() == HeadersGuardEnum::Immutable ||
+             aRequest->Headers()->Guard() == HeadersGuardEnum::Request ||
+             aRequest->Headers()->Guard() == HeadersGuardEnum::Request_no_cors);
   SetMimeType();
 }
 
 Request::~Request()
 {
 }
 
 // static
@@ -508,18 +511,21 @@ Request::Constructor(const GlobalObject&
   if (aInit.mBody.WasPassed()) {
     const Nullable<OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams>& bodyInitNullable =
       aInit.mBody.Value();
     if (!bodyInitNullable.IsNull()) {
       const OwningArrayBufferOrArrayBufferViewOrBlobOrFormDataOrUSVStringOrURLSearchParams& bodyInit =
         bodyInitNullable.Value();
       nsCOMPtr<nsIInputStream> stream;
       nsAutoCString contentType;
+      uint64_t contentLengthUnused;
       aRv = ExtractByteStreamFromBody(bodyInit,
-                                      getter_AddRefs(stream), contentType);
+                                      getter_AddRefs(stream),
+                                      contentType,
+                                      contentLengthUnused);
       if (NS_WARN_IF(aRv.Failed())) {
         return nullptr;
       }
 
       temporaryBody = stream;
 
       if (!contentType.IsVoid() &&
           !requestHeaders->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -34,16 +34,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Response::Response(nsIGlobalObject* aGlobal, InternalResponse* aInternalResponse)
   : FetchBody<Response>()
   , mOwner(aGlobal)
   , mInternalResponse(aInternalResponse)
 {
+  MOZ_ASSERT(aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Immutable ||
+             aInternalResponse->Headers()->Guard() == HeadersGuardEnum::Response);
   SetMimeType();
 }
 
 Response::~Response()
 {
 }
 
 /* static */ already_AddRefed<Response>
@@ -200,18 +202,25 @@ Response::Constructor(const GlobalObject
   if (aBody.WasPassed()) {
     if (aInit.mStatus == 204 || aInit.mStatus == 205 || aInit.mStatus == 304) {
       aRv.ThrowTypeError<MSG_RESPONSE_NULL_STATUS_WITH_BODY>();
       return nullptr;
     }
 
     nsCOMPtr<nsIInputStream> bodyStream;
     nsCString contentType;
-    aRv = ExtractByteStreamFromBody(aBody.Value(), getter_AddRefs(bodyStream), contentType);
-    internalResponse->SetBody(bodyStream);
+    uint64_t bodySize = 0;
+    aRv = ExtractByteStreamFromBody(aBody.Value(),
+                                    getter_AddRefs(bodyStream),
+                                    contentType,
+                                    bodySize);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+    internalResponse->SetBody(bodyStream, bodySize);
 
     if (!contentType.IsVoid() &&
         !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
       // Ignore Append() failing here.
       ErrorResult error;
       internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, error);
       error.SuppressException();
     }
@@ -248,20 +257,20 @@ Response::CloneUnfiltered(ErrorResult& a
 
   RefPtr<InternalResponse> clone = mInternalResponse->Clone();
   RefPtr<InternalResponse> ir = clone->Unfiltered();
   RefPtr<Response> ref = new Response(mOwner, ir);
   return ref.forget();
 }
 
 void
-Response::SetBody(nsIInputStream* aBody)
+Response::SetBody(nsIInputStream* aBody, int64_t aBodySize)
 {
   MOZ_ASSERT(!BodyUsed());
-  mInternalResponse->SetBody(aBody);
+  mInternalResponse->SetBody(aBody, aBodySize);
 }
 
 already_AddRefed<InternalResponse>
 Response::GetInternalResponse() const
 {
   RefPtr<InternalResponse> ref = mInternalResponse;
   return ref.forget();
 }
--- a/dom/fetch/Response.h
+++ b/dom/fetch/Response.h
@@ -129,17 +129,17 @@ public:
 
   already_AddRefed<Response>
   Clone(ErrorResult& aRv) const;
 
   already_AddRefed<Response>
   CloneUnfiltered(ErrorResult& aRv) const;
 
   void
-  SetBody(nsIInputStream* aBody);
+  SetBody(nsIInputStream* aBody, int64_t aBodySize);
 
   already_AddRefed<InternalResponse>
   GetInternalResponse() const;
 
 private:
   ~Response();
 
   nsCOMPtr<nsIGlobalObject> mOwner;
--- a/dom/filesystem/Directory.cpp
+++ b/dom/filesystem/Directory.cpp
@@ -161,16 +161,31 @@ Directory::GetRoot(FileSystemBase* aFile
     return nullptr;
   }
 
   FileSystemPermissionRequest::RequestForTask(task);
   return task->GetPromise();
 }
 
 /* static */ already_AddRefed<Directory>
+Directory::Constructor(const GlobalObject& aGlobal,
+                       const nsAString& aRealPath,
+                       ErrorResult& aRv)
+{
+  nsCOMPtr<nsIFile> path;
+  aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aRealPath),
+                              true, getter_AddRefs(path));
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+  }
+
+  return Create(aGlobal.GetAsSupports(), path);
+}
+
+/* static */ already_AddRefed<Directory>
 Directory::Create(nsISupports* aParent, nsIFile* aFile,
                   FileSystemBase* aFileSystem)
 {
   MOZ_ASSERT(aParent);
   MOZ_ASSERT(aFile);
 
 #ifdef DEBUG
   bool isDir;
--- a/dom/filesystem/Directory.h
+++ b/dom/filesystem/Directory.h
@@ -58,16 +58,21 @@ public:
 
   static bool
   WebkitBlinkDirectoryPickerEnabled(JSContext* aCx, JSObject* aObj);
 
   static already_AddRefed<Promise>
   GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv);
 
   static already_AddRefed<Directory>
+  Constructor(const GlobalObject& aGlobal,
+              const nsAString& aRealPath,
+              ErrorResult& aRv);
+
+  static already_AddRefed<Directory>
   Create(nsISupports* aParent, nsIFile* aDirectory,
          FileSystemBase* aFileSystem = 0);
 
   // ========= Begin WebIDL bindings. ===========
 
   nsISupports*
   GetParentObject() const;
 
--- a/dom/filesystem/tests/mochitest.ini
+++ b/dom/filesystem/tests/mochitest.ini
@@ -1,8 +1,9 @@
 [DEFAULT]
 support-files =
   filesystem_commons.js
   script_fileList.js
   worker_basic.js
 
 [test_basic.html]
+[test_webkitdirectory.html]
 [test_worker_basic.html]
new file mode 100644
--- /dev/null
+++ b/dom/filesystem/tests/test_webkitdirectory.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for webkitdirectory and webkitRelativePath</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body>
+<input id="inputFileWebkitDirectory" type="file" webkitdirectory></input>
+<input id="inputFileWebkitDirectoryAndDirectory" type="file" webkitdirectory directory></input>
+<input id="inputFileDirectory" type="file" directory></input>
+
+<script type="application/javascript;version=1.7">
+
+function populateInputFile(aInputFile) {
+  var url = SimpleTest.getTestFileURL("script_fileList.js");
+  var script = SpecialPowers.loadChromeScript(url);
+
+  var MockFilePicker = SpecialPowers.MockFilePicker;
+  MockFilePicker.init(window, "A Mock File Picker", SpecialPowers.Ci.nsIFilePicker.modeOpen);
+
+  function onOpened(message) {
+    MockFilePicker.useDirectory(message.dir);
+
+    var input = document.getElementById(aInputFile);
+    input.addEventListener('change', function() {
+      MockFilePicker.cleanup();
+      script.destroy();
+      next();
+    });
+
+    input.click();
+  }
+
+  script.addMessageListener("dir.opened", onOpened);
+  script.sendAsyncMessage("dir.open", { path: 'test' });
+}
+
+function checkFile(file, fileList) {
+  for (var i = 0; i < fileList.length; ++i) {
+    ok(fileList[i] instanceof File, "We want just files.");
+    if (fileList[i].name == file.name) {
+      is(fileList[i].webkitRelativePath, file.path, "Path matches");
+      return;
+    }
+  }
+
+  ok(false, "File not found.");
+}
+
+function test_fileList(aInputFile, aWhat) {
+  var input = document.getElementById(aInputFile);
+  var fileList = input.files;
+
+  if (aWhat == null) {
+    is(fileList, null, "We want a null fileList for " + aInputFile);
+    next();
+    return;
+  }
+
+  is(fileList.length, aWhat.length, "We want just " + aWhat.length + " elements for " + aInputFile);
+  for (var i = 0; i < aWhat.length; ++i) {
+    checkFile(aWhat[i], fileList);
+  }
+
+  next();
+}
+
+function test_webkitdirectory_attribute() {
+  var a = document.createElement("input");
+  a.setAttribute("type", "file");
+
+  ok("webkitdirectory" in a, "HTMLInputElement.webkitdirectory exists");
+
+  ok(!a.hasAttribute("webkitdirectory"), "No webkitdirectory DOM attribute by default");
+  ok(!a.webkitdirectory, "No webkitdirectory attribute by default");
+
+  a.webkitdirectory = true;
+
+  ok(a.hasAttribute("webkitdirectory"), "Webkitdirectory DOM attribute is set");
+  ok(a.webkitdirectory, "Webkitdirectory attribute is set");
+
+  next();
+}
+
+function test_setup() {
+  SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true],
+                                     ["dom.webkitBlink.dirPicker.enabled", true]]}, next);
+}
+
+var tests = [
+  test_setup,
+
+  function() { populateInputFile('inputFileWebkitDirectory'); },
+  function() { populateInputFile('inputFileWebkitDirectoryAndDirectory'); },
+  function() { populateInputFile('inputFileDirectory'); },
+
+  function() { test_fileList('inputFileWebkitDirectory', [ { name: 'foo.txt', path: '/foo.txt' },
+                                                           { name: 'bar.txt', path: '/subdir/bar.txt' }]); },
+  function() { test_fileList('inputFileWebkitDirectoryAndDirectory', [ { name: 'foo.txt', path: '/foo.txt' },
+                                                                       { name: 'bar.txt', path: '/subdir/bar.txt' }]); },
+  function() { test_fileList('inputFileDirectory', null); },
+
+  test_webkitdirectory_attribute,
+];
+
+function next() {
+  if (!tests.length) {
+    SimpleTest.finish();
+    return;
+  }
+
+  var test = tests.shift();
+  test();
+}
+
+SimpleTest.waitForExplicitFinish();
+next();
+</script>
+</body>
+</html>
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -213,16 +213,28 @@ const Decimal HTMLInputElement::kStepAny
   0x23e2,                                          \
   0x4479,                                          \
   {0xb5, 0x13, 0x7b, 0x36, 0x93, 0x43, 0xe3, 0xa0} \
 }
 
 #define PROGRESS_STR "progress"
 static const uint32_t kProgressEventInterval = 50; // ms
 
+class GetFilesCallback
+{
+public:
+  NS_INLINE_DECL_REFCOUNTING(GetFilesCallback);
+
+  virtual void
+  Callback(nsresult aStatus, const Sequence<RefPtr<File>>& aFiles) = 0;
+
+protected:
+  virtual ~GetFilesCallback() {}
+};
+
 // Retrieving the list of files can be very time/IO consuming. We use this
 // helper class to do it just once.
 class GetFilesHelper final : public Runnable
 {
 public:
   static already_AddRefed<GetFilesHelper>
   Create(nsIGlobalObject* aGlobal,
          const nsTArray<OwningFileOrDirectory>& aFilesOrDirectory,
@@ -290,16 +302,31 @@ public:
       mPromises.AppendElement(aPromise);
       return;
     }
 
     MOZ_ASSERT(mPromises.IsEmpty());
     ResolveOrRejectPromise(aPromise);
   }
 
+  void
+  AddCallback(GetFilesCallback* aCallback)
+  {
+    MOZ_ASSERT(aCallback);
+
+    // Still working.
+    if (!mListingCompleted) {
+      mCallbacks.AppendElement(aCallback);
+      return;
+    }
+
+    MOZ_ASSERT(mCallbacks.IsEmpty());
+    RunCallback(aCallback);
+  }
+
   // CC methods
   void Unlink()
   {
     mGlobal = nullptr;
     mFiles.Clear();
     mPromises.Clear();
   }
 
@@ -348,16 +375,24 @@ private:
     // Let's process the pending promises.
     nsTArray<RefPtr<Promise>> promises;
     promises.SwapElements(mPromises);
 
     for (uint32_t i = 0; i < promises.Length(); ++i) {
       ResolveOrRejectPromise(promises[i]);
     }
 
+    // Let's process the pending callbacks.
+    nsTArray<RefPtr<GetFilesCallback>> callbacks;
+    callbacks.SwapElements(mCallbacks);
+
+    for (uint32_t i = 0; i < callbacks.Length(); ++i) {
+      RunCallback(callbacks[i]);
+    }
+
     return NS_OK;
   }
 
   void
   RunIO()
   {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(!mDirectoryPath.IsEmpty());
@@ -505,16 +540,26 @@ private:
     if (NS_FAILED(mErrorResult)) {
       aPromise->MaybeReject(mErrorResult);
       return;
     }
 
     aPromise->MaybeResolve(mFiles);
   }
 
+  void
+  RunCallback(GetFilesCallback* aCallback)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mListingCompleted);
+    MOZ_ASSERT(aCallback);
+
+    aCallback->Callback(mErrorResult, mFiles);
+  }
+
   nsCOMPtr<nsIGlobalObject> mGlobal;
 
   bool mRecursiveFlag;
   bool mListingCompleted;
   nsString mDirectoryPath;
 
   // We populate this array in the I/O thread with the paths of the Files that
   // we want to send as result to the promise objects.
@@ -526,16 +571,88 @@ private:
 
   // This is the real File sequence that we expose via Promises.
   Sequence<RefPtr<File>> mFiles;
 
   // Error code to propagate.
   nsresult mErrorResult;
 
   nsTArray<RefPtr<Promise>> mPromises;
+  nsTArray<RefPtr<GetFilesCallback>> mCallbacks;
+};
+
+// An helper class for the dispatching of the 'change' event.
+class DispatchChangeEventCallback final : public GetFilesCallback
+{
+public:
+  explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement)
+    : mInputElement(aInputElement)
+  {
+    MOZ_ASSERT(aInputElement);
+  }
+
+  virtual void
+  Callback(nsresult aStatus, const Sequence<RefPtr<File>>& aFiles) override
+  {
+    nsTArray<OwningFileOrDirectory> array;
+    for (uint32_t i = 0; i < aFiles.Length(); ++i) {
+      OwningFileOrDirectory* element = array.AppendElement();
+      element->SetAsFile() = aFiles[i];
+    }
+
+    mInputElement->SetFilesOrDirectories(array, true);
+    NS_WARN_IF(NS_FAILED(DispatchEvents()));
+  }
+
+  nsresult
+  DispatchEvents()
+  {
+    nsresult rv = NS_OK;
+    rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
+                                              static_cast<nsIDOMHTMLInputElement*>(mInputElement.get()),
+                                              NS_LITERAL_STRING("input"), true,
+                                              false);
+    NS_WARN_IF(NS_FAILED(rv));
+
+    rv = nsContentUtils::DispatchTrustedEvent(mInputElement->OwnerDoc(),
+                                              static_cast<nsIDOMHTMLInputElement*>(mInputElement.get()),
+                                              NS_LITERAL_STRING("change"), true,
+                                              false);
+
+    return rv;
+  }
+
+private:
+  RefPtr<HTMLInputElement> mInputElement;
+};
+
+// This callback is used for postponing the calling of SetFilesOrDirectories
+// when the exploration of the directory is completed.
+class AfterSetFilesOrDirectoriesCallback : public GetFilesCallback
+{
+public:
+  AfterSetFilesOrDirectoriesCallback(HTMLInputElement* aInputElement,
+                                     bool aSetValueChanged)
+    : mInputElement(aInputElement)
+    , mSetValueChanged(aSetValueChanged)
+  {
+    MOZ_ASSERT(aInputElement);
+  }
+
+  void
+  Callback(nsresult aStatus, const Sequence<RefPtr<File>>& aFiles) override
+  {
+    if (NS_SUCCEEDED(aStatus)) {
+      mInputElement->AfterSetFilesOrDirectoriesInternal(mSetValueChanged);
+    }
+  }
+
+private:
+  RefPtr<HTMLInputElement> mInputElement;
+  bool mSetValueChanged;
 };
 
 class HTMLInputElementState final : public nsISupports
 {
   public:
     NS_DECLARE_STATIC_IID_ACCESSOR(NS_INPUT_ELEMENT_STATE_IID)
     NS_DECL_ISUPPORTS
 
@@ -860,29 +977,32 @@ HTMLInputElement::nsFilePickerShownCallb
       mInput->OwnerDoc(), lastUsedDir);
   }
 
   // The text control frame (if there is one) isn't going to send a change
   // event because it will think this is done by a script.
   // So, we can safely send one by ourself.
   mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
 
-  nsresult rv = NS_OK;
-  rv = nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
-                                            static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
-                                            NS_LITERAL_STRING("input"), true,
-                                            false);
-  NS_WARN_IF(NS_FAILED(rv));
-
-  rv = nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
-                                            static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
-                                            NS_LITERAL_STRING("change"), true,
-                                            false);
-
-  return rv;
+  RefPtr<DispatchChangeEventCallback> dispatchChangeEventCallback =
+    new DispatchChangeEventCallback(mInput);
+
+  if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
+      mInput->HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
+    ErrorResult error;
+    GetFilesHelper* helper = mInput->GetOrCreateGetFilesHelper(true, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.StealNSResult();
+    }
+
+    helper->AddCallback(dispatchChangeEventCallback);
+    return NS_OK;
+  }
+
+  return dispatchChangeEventCallback->DispatchEvents();
 }
 
 NS_IMPL_ISUPPORTS(HTMLInputElement::nsFilePickerShownCallback,
                   nsIFilePickerShownCallback)
 
 class nsColorPickerShownCallback final
   : public nsIColorPickerShownCallback
 {
@@ -2916,16 +3036,29 @@ HTMLInputElement::SetFiles(nsIDOMFileLis
   }
 
   AfterSetFilesOrDirectories(aSetValueChanged);
 }
 
 void
 HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged)
 {
+  if (Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) &&
+      HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory)) {
+    // This will call AfterSetFilesOrDirectoriesInternal eventually.
+    ExploreDirectoryRecursively(aSetValueChanged);
+    return;
+  }
+
+  AfterSetFilesOrDirectoriesInternal(aSetValueChanged);
+}
+
+void
+HTMLInputElement::AfterSetFilesOrDirectoriesInternal(bool aSetValueChanged)
+{
   // No need to flush here, if there's no frame at this point we
   // don't need to force creation of one just to tell it about this
   // new value.  We just want the display to update as needed.
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(false);
   if (formControlFrame) {
     nsAutoString readableValue;
     GetDisplayFileName(readableValue);
     formControlFrame->SetFormProperty(nsGkAtoms::value, readableValue);
@@ -2978,17 +3111,19 @@ HTMLInputElement::FireChangeEventIfNeede
 FileList*
 HTMLInputElement::GetFiles()
 {
   if (mType != NS_FORM_INPUT_FILE) {
     return nullptr;
   }
 
   if (Preferences::GetBool("dom.input.dirpicker", false) &&
-      HasAttr(kNameSpaceID_None, nsGkAtoms::directory)) {
+      HasAttr(kNameSpaceID_None, nsGkAtoms::directory) &&
+      (!Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false) ||
+       !HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) {
     return nullptr;
   }
 
   if (!mFileList) {
     mFileList = new FileList(static_cast<nsIContent*>(this));
     UpdateFileList();
   }
 
@@ -3660,16 +3795,25 @@ HTMLInputElement::PreHandleEvent(EventCh
       aVisitor.mEvent->AsMouseEvent()->button ==
         WidgetMouseEvent::eMiddleButton) {
     aVisitor.mEvent->mFlags.mNoContentDispatch = false;
   }
 
   // We must cache type because mType may change during JS event (bug 2369)
   aVisitor.mItemFlags |= mType;
 
+  if (aVisitor.mEvent->mMessage == eFocus &&
+      aVisitor.mEvent->IsTrusted() &&
+      MayFireChangeOnBlur() &&
+      // StartRangeThumbDrag already set mFocusedValue on 'mousedown' before
+      // we get the 'focus' event.
+      !mIsDraggingRange) {
+    GetValue(mFocusedValue);
+  }
+
   // Fire onchange (if necessary), before we do the blur, bug 357684.
   if (aVisitor.mEvent->mMessage == eBlur) {
     // Experimental mobile types rely on the system UI to prevent users to not
     // set invalid values but we have to be extra-careful. Especially if the
     // option has been enabled on desktop.
     if (IsExperimentalMobileType(mType)) {
       nsAutoString aValue;
       GetValueInternal(aValue);
@@ -4068,18 +4212,20 @@ HTMLInputElement::MaybeInitPickers(Event
     // If the user clicked on the "Choose folder..." button we open the
     // directory picker, else we open the file picker.
     FilePickerType type = FILE_PICKER_FILE;
     nsCOMPtr<nsIContent> target =
       do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
     if (target &&
         target->GetParent() == this &&
         target->IsRootOfNativeAnonymousSubtree() &&
-        target->HasAttr(kNameSpaceID_None, nsGkAtoms::directory)) {
-      MOZ_ASSERT(Preferences::GetBool("dom.input.dirpicker", false),
+        (target->HasAttr(kNameSpaceID_None, nsGkAtoms::directory) ||
+         target->HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory))) {
+      MOZ_ASSERT(Preferences::GetBool("dom.input.dirpicker", false) ||
+                 Preferences::GetBool("dom.webkitBlink.dirPicker.enabled", false),
                  "No API or UI should have been exposed to allow this code to "
                  "be reached");
       type = FILE_PICKER_DIRECTORY;
     }
     return InitFilePicker(type);
   }
   if (mType == NS_FORM_INPUT_COLOR) {
     return InitColorPicker();
@@ -4093,22 +4239,16 @@ HTMLInputElement::PostHandleEvent(EventC
   if (!aVisitor.mPresContext) {
     // Hack alert! In order to open file picker even in case the element isn't
     // in document, try to init picker even without PresContext.
     return MaybeInitPickers(aVisitor);
   }
 
   if (aVisitor.mEvent->mMessage == eFocus ||
       aVisitor.mEvent->mMessage == eBlur) {
-    if (aVisitor.mEvent->mMessage == eFocus &&
-        MayFireChangeOnBlur() &&
-        !mIsDraggingRange) { // StartRangeThumbDrag already set mFocusedValue
-      GetValue(mFocusedValue);
-    }
-
     if (aVisitor.mEvent->mMessage == eBlur) {
       if (mIsDraggingRange) {
         FinishRangeThumbDrag();
       } else if (mNumberControlSpinnerIsSpinning) {
         StopNumberControlSpinnerSpin();
       }
     }
 
@@ -5279,17 +5419,18 @@ nsChangeHint
 HTMLInputElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
                                          int32_t aModType) const
 {
   nsChangeHint retval =
     nsGenericHTMLFormElementWithState::GetAttributeChangeHint(aAttribute, aModType);
   if (aAttribute == nsGkAtoms::type ||
       // The presence or absence of the 'directory' attribute determines what
       // buttons we show for type=file.
-      aAttribute == nsGkAtoms::directory) {
+      aAttribute == nsGkAtoms::directory ||
+      aAttribute == nsGkAtoms::webkitdirectory) {
     retval |= NS_STYLE_HINT_FRAMECHANGE;
   } else if (mType == NS_FORM_INPUT_IMAGE &&
              (aAttribute == nsGkAtoms::alt ||
               aAttribute == nsGkAtoms::value)) {
     // We might need to rebuild our alt text.  Just go ahead and
     // reconstruct our frame.  This should be quite rare..
     retval |= NS_STYLE_HINT_FRAMECHANGE;
   } else if (aAttribute == nsGkAtoms::value) {
@@ -5415,51 +5556,28 @@ HTMLInputElement::GetFilesAndDirectories
 already_AddRefed<Promise>
 HTMLInputElement::GetFiles(bool aRecursiveFlag, ErrorResult& aRv)
 {
   if (mType != NS_FORM_INPUT_FILE) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
+  GetFilesHelper* helper = GetOrCreateGetFilesHelper(aRecursiveFlag, aRv);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return nullptr;
+   }
+  MOZ_ASSERT(helper);
+
   nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
   if (!global) {
     return nullptr;
   }
 
-  RefPtr<GetFilesHelper> helper;
-  if (aRecursiveFlag) {
-    if (!mGetFilesRecursiveHelper) {
-      mGetFilesRecursiveHelper =
-       GetFilesHelper::Create(global,
-                              GetFilesOrDirectoriesInternal(),
-                              aRecursiveFlag, aRv);
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
-    }
-
-    helper = mGetFilesRecursiveHelper;
-  } else {
-    if (!mGetFilesNonRecursiveHelper) {
-      mGetFilesNonRecursiveHelper =
-       GetFilesHelper::Create(global,
-                              GetFilesOrDirectoriesInternal(),
-                              aRecursiveFlag, aRv);
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
-    }
-
-    helper = mGetFilesNonRecursiveHelper;
-  }
-
-  MOZ_ASSERT(helper);
-
   RefPtr<Promise> p = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   helper->AddPromise(p);
   return p.forget();
 }
@@ -7970,12 +8088,66 @@ HTMLInputElement::ClearGetFilesHelpers()
   }
 
   if (mGetFilesNonRecursiveHelper) {
     mGetFilesNonRecursiveHelper->Unlink();
     mGetFilesNonRecursiveHelper = nullptr;
   }
 }
 
+GetFilesHelper*
+HTMLInputElement::GetOrCreateGetFilesHelper(bool aRecursiveFlag,
+                                            ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
+  MOZ_ASSERT(global);
+  if (!global) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  if (aRecursiveFlag) {
+    if (!mGetFilesRecursiveHelper) {
+      mGetFilesRecursiveHelper =
+       GetFilesHelper::Create(global,
+                              GetFilesOrDirectoriesInternal(),
+                              aRecursiveFlag, aRv);
+      if (NS_WARN_IF(aRv.Failed())) {
+        return nullptr;
+      }
+    }
+
+    return mGetFilesRecursiveHelper;
+  }
+
+  if (!mGetFilesNonRecursiveHelper) {
+    mGetFilesNonRecursiveHelper =
+     GetFilesHelper::Create(global,
+                            GetFilesOrDirectoriesInternal(),
+                            aRecursiveFlag, aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return nullptr;
+    }
+  }
+
+  return mGetFilesNonRecursiveHelper;
+}
+
+void
+HTMLInputElement::ExploreDirectoryRecursively(bool aSetValueChanged)
+{
+  ErrorResult rv;
+  GetFilesHelper* helper = GetOrCreateGetFilesHelper(true /* recursionFlag */,
+                                                     rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    AfterSetFilesOrDirectoriesInternal(aSetValueChanged);
+    return;
+  }
+
+  RefPtr<AfterSetFilesOrDirectoriesCallback> callback =
+    new AfterSetFilesOrDirectoriesCallback(this, aSetValueChanged);
+  helper->AddCallback(callback);
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #undef NS_ORIGINAL_CHECKED_VALUE
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -32,17 +32,19 @@ class nsIRadioVisitor;
 
 namespace mozilla {
 
 class EventChainPostVisitor;
 class EventChainPreVisitor;
 
 namespace dom {
 
+class AfterSetFilesOrDirectoriesRunnable;
 class Date;
+class DispatchChangeEventCallback;
 class File;
 class FileList;
 class GetFilesHelper;
 
 /**
  * A class we use to create a singleton object that is used to keep track of
  * the last directory from which the user has picked files (via
  * <input type=file>) on a per-domain basis. The implementation uses
@@ -102,16 +104,19 @@ public:
 class HTMLInputElement final : public nsGenericHTMLFormElementWithState,
                                public nsImageLoadingContent,
                                public nsIDOMHTMLInputElement,
                                public nsITextControlElement,
                                public nsIPhonetic,
                                public nsIDOMNSEditableElement,
                                public nsIConstraintValidation
 {
+  friend class AfterSetFilesOrDirectoriesCallback;
+  friend class DispatchChangeEventCallback;
+
 public:
   using nsIConstraintValidation::GetValidationMessage;
   using nsIConstraintValidation::CheckValidity;
   using nsIConstraintValidation::ReportValidity;
   using nsIConstraintValidation::WillValidate;
   using nsIConstraintValidation::Validity;
   using nsGenericHTMLFormElementWithState::GetForm;
 
@@ -695,16 +700,26 @@ public:
     return HasAttr(kNameSpaceID_None, nsGkAtoms::directory);
   }
 
   void SetDirectoryAttr(bool aValue, ErrorResult& aRv)
   {
     SetHTMLBoolAttr(nsGkAtoms::directory, aValue, aRv);
   }
 
+  bool WebkitDirectoryAttr() const
+  {
+    return HasAttr(kNameSpaceID_None, nsGkAtoms::webkitdirectory);
+  }
+
+  void SetWebkitDirectoryAttr(bool aValue, ErrorResult& aRv)
+  {
+    SetHTMLBoolAttr(nsGkAtoms::webkitdirectory, aValue, aRv);
+  }
+
   bool IsFilesAndDirectoriesSupported() const;
 
   already_AddRefed<Promise> GetFilesAndDirectories(ErrorResult& aRv);
 
   already_AddRefed<Promise> GetFiles(bool aRecursiveFlag, ErrorResult& aRv);
 
   void ChooseDirectory(ErrorResult& aRv);
 
@@ -933,18 +948,26 @@ protected:
 
   /**
    * Update mFileList with the currently selected file.
    */
   void UpdateFileList();
 
   /**
    * Called after calling one of the SetFilesOrDirectories() functions.
+   * This method can explore the directory recursively if needed.
    */
   void AfterSetFilesOrDirectories(bool aSetValueChanged);
+  void AfterSetFilesOrDirectoriesInternal(bool aSetValueChanged);
+
+  /**
+   * Recursively explore the directory and populate mFileOrDirectories correctly
+   * for webkitdirectory.
+   */
+  void ExploreDirectoryRecursively(bool aSetValuechanged);
 
   /**
    * Determine whether the editor needs to be initialized explicitly for
    * a particular event.
    */
   bool NeedToInitializeEditorForEvent(EventChainPreVisitor& aVisitor) const;
 
   /**
@@ -1251,16 +1274,19 @@ protected:
    * Use this function before trying to open a picker.
    * It checks if the page is allowed to open a new pop-up.
    * If it returns true, you should not create the picker.
    *
    * @return true if popup should be blocked, false otherwise
    */
   bool IsPopupBlocked() const;
 
+  GetFilesHelper* GetOrCreateGetFilesHelper(bool aRecursiveFlag,
+                                            ErrorResult& aRv);
+
   void ClearGetFilesHelpers();
 
   nsCOMPtr<nsIControllers> mControllers;
 
   /*
    * In mInputData, the mState field is used if IsSingleLineTextControl returns
    * true and mValue is used otherwise.  We have to be careful when handling it
    * on a type change.
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1871,17 +1871,18 @@ void HTMLMediaElement::SetVolumeInternal
   if (mDecoder) {
     mDecoder->SetVolume(effectiveVolume);
   } else if (MediaStream* stream = GetSrcMediaStream()) {
     if (mSrcStreamIsPlaying) {
       stream->SetAudioOutputVolume(this, effectiveVolume);
     }
   }
 
-  UpdateAudioChannelPlayingState();
+  NotifyAudioPlaybackChanged(
+    AudioChannelService::AudibleChangedReasons::eVolumeChanged);
 }
 
 NS_IMETHODIMP HTMLMediaElement::SetMuted(bool aMuted)
 {
   if (aMuted == Muted()) {
     return NS_OK;
   }
 
@@ -2276,17 +2277,18 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mAudioChannelVolume(1.0),
     mPlayingThroughTheAudioChannel(false),
     mDisableVideo(false),
     mPlayBlockedBecauseHidden(false),
     mElementInTreeState(ELEMENT_NOT_INTREE),
     mHasUserInteraction(false),
     mFirstFrameLoaded(false),
     mDefaultPlaybackStartPosition(0.0),
-    mIsAudioTrackAudible(false)
+    mIsAudioTrackAudible(false),
+    mAudible(IsAudible())
 {
   mAudioChannel = AudioChannelService::GetDefaultAudioChannel();
 
   mPaused.SetOuter(this);
 
   RegisterActivityObserver();
   NotifyOwnerDocumentActivityChangedInternal();
 
@@ -4964,36 +4966,31 @@ HTMLMediaElement::MaybeCreateAudioChanne
 bool
 HTMLMediaElement::IsPlayingThroughTheAudioChannel() const
 {
   // It might be resumed from remote, we should keep the audio channel agent.
   if (IsSuspendedByAudioChannel()) {
     return true;
   }
 
-  // Are we paused or muted
-  if (mPaused || Muted()) {
+  // Are we paused
+  if (mPaused) {
     return false;
   }
 
   // If we have an error, we are not playing.
   if (mError) {
     return false;
   }
 
   // If this element doesn't have any audio tracks.
   if (!HasAudio()) {
     return false;
   }
 
-  // The volume should not be ~0
-  if (std::fabs(Volume()) <= 1e-7) {
-    return false;
-  }
-
   // We should consider any bfcached page or inactive document as non-playing.
   if (!IsActive()) {
     return false;
   }
 
   // A loop always is playing
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::loop)) {
     return true;
@@ -5048,17 +5045,17 @@ HTMLMediaElement::NotifyAudioChannelAgen
     // The reason we don't call NotifyStartedPlaying after the media element
     // really becomes audible is because there is another case needs to block
     // element as early as we can, we would hear sound leaking if we block it
     // too late. In that case (block autoplay in non-visited-tab), we need to
     // create a connection before decoding, because we don't want user hearing
     // any sound.
     AudioPlaybackConfig config;
     nsresult rv = mAudioChannelAgent->NotifyStartedPlaying(&config,
-                                                           mIsAudioTrackAudible);
+                                                           IsAudible());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
     WindowVolumeChanged(config.mVolume, config.mMuted);
     WindowSuspendChanged(config.mSuspend);
   } else {
     mAudioChannelAgent->NotifyStoppedPlaying();
@@ -5101,24 +5098,28 @@ HTMLMediaElement::WindowSuspendChanged(S
     case nsISuspendedTypes::SUSPENDED_PAUSE:
     case nsISuspendedTypes::SUSPENDED_PAUSE_DISPOSABLE:
       PauseByAudioChannel(aSuspend);
       break;
     case nsISuspendedTypes::SUSPENDED_BLOCK:
       BlockByAudioChannel();
       break;
     case nsISuspendedTypes::SUSPENDED_STOP_DISPOSABLE:
+      SetAudioChannelSuspended(nsISuspendedTypes::NONE_SUSPENDED);
       Pause();
       break;
     default:
       MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
              ("HTMLMediaElement, WindowSuspendChanged, this = %p, "
               "Error : unknown suspended type!\n", this));
   }
 
+  NotifyAudioPlaybackChanged(
+    AudioChannelService::AudibleChangedReasons::ePauseStateChanged);
+
   return NS_OK;
 }
 
 void
 HTMLMediaElement::ResumeFromAudioChannel()
 {
   if (!IsSuspendedByAudioChannel()) {
     return;
@@ -5604,22 +5605,51 @@ HTMLMediaElement::IsCurrentlyPlaying() c
   return false;
 }
 
 void
 HTMLMediaElement::SetAudibleState(bool aAudible)
 {
   if (mIsAudioTrackAudible != aAudible) {
     mIsAudioTrackAudible = aAudible;
-    NotifyAudioPlaybackChanged();
+    NotifyAudioPlaybackChanged(
+      AudioChannelService::AudibleChangedReasons::eDataAudibleChanged);
   }
 }
 
 void
-HTMLMediaElement::NotifyAudioPlaybackChanged()
-{
-  if (mAudioChannelAgent) {
-    mAudioChannelAgent->NotifyStartedAudible(mIsAudioTrackAudible);
-  }
+HTMLMediaElement::NotifyAudioPlaybackChanged(AudibleChangedReasons aReason)
+{
+  if (!mAudioChannelAgent) {
+    return;
+  }
+
+  if (mAudible == IsAudible()) {
+    return;
+  }
+
+  mAudible = IsAudible();
+  mAudioChannelAgent->NotifyStartedAudible(mAudible, aReason);
+}
+
+bool
+HTMLMediaElement::IsAudible() const
+{
+  // Muted or the volume should not be ~0
+  if (Muted() || (std::fabs(Volume()) <= 1e-7)) {
+    return false;
+  }
+
+  // No sound can be heard during suspending.
+  if (IsSuspendedByAudioChannel()) {
+    return false;
+  }
+
+  // Silent audio track.
+  if (!mIsAudioTrackAudible) {
+    return false;
+  }
+
+  return true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -33,16 +33,17 @@
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 typedef uint32_t SuspendTypes;
+typedef uint32_t AudibleChangedReasons;
 
 namespace mozilla {
 class DecoderDoctorDiagnostics;
 class DOMMediaStream;
 class ErrorResult;
 class MediaResource;
 class MediaDecoder;
 class VideoFrameContainer;
@@ -448,17 +449,17 @@ public:
   // when the connection between Rtsp server and client gets lost.
   virtual void ResetConnectionState() final override;
 
   // Called by media decoder when the audible state changed or when input is
   // a media stream.
   virtual void SetAudibleState(bool aAudible) final override;
 
   // Notify agent when the MediaElement changes its audible state.
-  void NotifyAudioPlaybackChanged();
+  void NotifyAudioPlaybackChanged(AudibleChangedReasons aReason);
 
   // XPCOM GetPreload() is OK
   void SetPreload(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::preload, aValue, aRv);
   }
 
   already_AddRefed<TimeRanges> Buffered() const;
@@ -1170,16 +1171,18 @@ protected:
   void ResumeFromAudioChannelPaused(SuspendTypes aSuspend);
   void ResumeFromAudioChannelBlocked();
 
   bool IsSuspendedByAudioChannel() const;
   void SetAudioChannelSuspended(SuspendTypes aSuspend);
 
   bool IsAllowedToPlay();
 
+  bool IsAudible() const;
+
   class nsAsyncEventRunner;
   using nsGenericHTMLElement::DispatchEvent;
   // For nsAsyncEventRunner.
   nsresult DispatchEvent(const nsAString& aName);
 
   // The current decoder. Load() has been called on this decoder.
   // At most one of mDecoder and mSrcStream can be non-null.
   RefPtr<MediaDecoder> mDecoder;
@@ -1601,16 +1604,19 @@ private:
   // True if the first frame has been successfully loaded.
   bool mFirstFrameLoaded;
 
   // Media elements also have a default playback start position, which must
   // initially be set to zero seconds. This time is used to allow the element to
   // be seeked even before the media is loaded.
   double mDefaultPlaybackStartPosition;
 
-  // True if the audio track is producing audible sound.
+  // True if the audio track is not silent.
   bool mIsAudioTrackAudible;
+
+  // True if media element is audible for users.
+  bool mAudible;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLMediaElement_h
--- a/dom/html/test/forms/mochitest.ini
+++ b/dom/html/test/forms/mochitest.ini
@@ -83,16 +83,18 @@ skip-if = buildapp == 'mulet'
 [test_progress_element.html]
 [test_radio_in_label.html]
 [test_radio_radionodelist.html]
 [test_required_attribute.html]
 [test_restore_form_elements.html]
 [test_save_restore_radio_groups.html]
 [test_select_change_event.html]
 skip-if = android_version == '18' || os == 'mac'
+[test_select_input_change_event.html]
+skip-if = android_version == '18' || os == 'mac'
 [test_select_selectedOptions.html]
 [test_select_validation.html]
 [test_set_range_text.html]
 [test_step_attribute.html]
 [test_stepup_stepdown.html]
 [test_textarea_attributes_reflection.html]
 [test_validation.html]
 skip-if = buildapp == 'b2g' # b2g(374 total, bug 901848, no keygen support) b2g-debug(374 total, bug 901848, no keygen support) b2g-desktop(374 total, bug 901848, no keygen support)
--- a/dom/html/test/forms/test_change_event.html
+++ b/dom/html/test/forms/test_change_event.html
@@ -26,27 +26,32 @@ https://bugzilla.mozilla.org/show_bug.cg
 <input type="button" id="input_button" onchange="++NonTextInputChange[0];"></input>
 <input type="submit" id="input_submit" onchange="++NonTextInputChange[1];"></input>
 <input type="image" id="input_image" onchange="++NonTextInputChange[2];"></input>
 <input type="reset" id="input_reset" onchange="++NonTextInputChange[3];"></input>
 <input type="radio" id="input_radio" onchange="++NonTextInputChange[4];"></input>
 <input type="checkbox" id="input_checkbox" onchange="++NonTextInputChange[5];"></input>
 <input type="number" id="input_number" onchange="++numberChange;"></input>
 <input type="range" id="input_range" onchange="++rangeChange;"></input>
+
+<!-- Input text with default value and blurs on focus-->
+<input type="text" id="input_text_value" onchange="++textInputValueChange"
+  onfocus="this.blur();" value="foo"></input>
  
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
   /** Test for Bug 722599 **/
 
   const isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
 
   var textareaChange = 0;
   var fileInputChange = 0;
+  var textInputValueChange = 0;
 
   var textInputTypes = ["text", "email", "search", "tel", "url", "password"];
   var textInputChange = [0, 0, 0, 0, 0, 0];
 
   var NonTextInputTypes = ["button", "submit", "image", "reset", "radio", "checkbox"];
   var NonTextInputChange = [0, 0, 0, 0, 0, 0];
 
   var numberChange = 0;
@@ -231,16 +236,25 @@ https://bugzilla.mozilla.org/show_bug.cg
     //Input type change test.
     input = document.getElementById("input_checkbox");
     input.type = "text";
     input.focus();
     input.click();
     input.blur();
     is(NonTextInputChange[5], 1, "Change event shouldn't be dispatched for checkbox ---> text input type change");
 
+    setTimeout(testInputWithDefaultValue, 0);
+  }
+
+  function testInputWithDefaultValue() {
+    // focus and blur an input text should not trigger change event if content hasn't changed.
+    var input = document.getElementById('input_text_value');
+    input.focus();
+    is(textInputValueChange, 0, "change event shouldn't be dispatched on input text with default value");
+
     SimpleTest.finish();
   }
 
   addLoadEvent(testUserInput);
  
 </script>
 </pre>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_select_input_change_event.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1265968
+-->
+<head>
+  <title>Test for Bug 1024350</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1024350">Mozilla Bug 1024350</a>
+<p id="display"></p>
+<div id="content">
+  <select oninput='++selectInput;' onchange="++selectChange;">
+    <option>one</option>
+  </select>
+  <select oninput='++selectInput;' onchange="++selectChange;">
+    <option>one</option>
+    <option>two</option>
+  </select>
+  <select multiple size='1' oninput='++selectInput;' onchange="++selectChange;">
+    <option>one</option>
+  </select>
+  <select multiple oninput='++selectInput;' onchange="++selectChange;">
+    <option>one</option>
+    <option>two</option>
+  </select>
+</div>
+<pre id="test">
+<script type="application/javascript">
+  var selectSingleOneItem = document.getElementsByTagName('select')[0];
+  var selectSingle = document.getElementsByTagName('select')[1];
+  var selectMultipleOneItem = document.getElementsByTagName('select')[2];
+  var selectMultiple = document.getElementsByTagName('select')[3];
+
+  var selectChange = 0;
+  var selectInput = 0;
+  var expectedChange = 0;
+  var expectedInput = 0;
+
+  selectSingleOneItem.focus();
+  synthesizeKey("VK_DOWN", {});
+  is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+  is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+  synthesizeKey("VK_UP", {});
+  is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+  is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+  selectSingle.focus();
+  for (var i = 1; i < selectSingle.length; i++) {
+    synthesizeKey("VK_DOWN", {});
+
+    is(selectSingle.options[i].selected, true, "Option should be selected");
+    is(selectInput, ++expectedInput, "Down key should fire input event.");
+    is(selectChange, ++expectedChange, "Down key should fire change event.");
+  }
+
+  // We are at the end of the list, going down should not fire change event.
+  synthesizeKey("VK_DOWN", {});
+  is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+  is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+  for (var i = selectSingle.length - 2; i >= 0; i--) {
+    synthesizeKey("VK_UP", {});
+
+    is(selectSingle.options[i].selected, true, "Option should be selected");
+    is(selectInput, ++expectedInput, "Up key should fire input event.");
+    is(selectChange, ++expectedChange, "Up key should fire change event.");
+  }
+
+  // We are at the top of the list, going up should not fire change event.
+  synthesizeKey("VK_UP", {});
+  is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+  is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+  selectMultipleOneItem.focus();
+  synthesizeKey("VK_DOWN", {});
+  is(selectInput, ++expectedInput, "Down key should fire input event when reaching end of the list.");
+  is(selectChange, ++expectedChange, "Down key should fire change event when reaching end of the list.");
+
+  synthesizeKey("VK_DOWN", {});
+  is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+  is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+  synthesizeKey("VK_UP", {});
+  is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+  is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+  selectMultiple.focus();
+  for (var i = 0; i < selectMultiple.length; i++) {
+    synthesizeKey("VK_DOWN", {});
+
+    is(selectMultiple.options[i].selected, true, "Option should be selected");
+    is(selectInput, ++expectedInput, "Down key should fire input event.");
+    is(selectChange, ++expectedChange, "Down key should fire change event.");
+  }
+
+  // We are at the end of the list, going down should not fire change event.
+  synthesizeKey("VK_DOWN", {});
+  is(selectInput, expectedInput, "Down key should not fire input event when reaching end of the list.");
+  is(selectChange, expectedChange, "Down key should not fire change event when reaching end of the list.");
+
+  for (var i = selectMultiple.length - 2; i >= 0; i--) {
+    synthesizeKey("VK_UP", {});
+
+    is(selectMultiple.options[i].selected, true, "Option should be selected");
+    is(selectInput, ++expectedInput, "Up key should fire input event.");
+    is(selectChange, ++expectedChange, "Up key should fire change event.");
+  }
+
+  // We are at the top of the list, going up should not fire change event.
+  synthesizeKey("VK_UP", {});
+  is(selectInput, expectedInput, "Up key should not fire input event when reaching top of the list.");
+  is(selectChange, expectedChange, "Up key should not fire change event when reaching top of the list.");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/interfaces/push/nsIPushNotifier.idl
+++ b/dom/interfaces/push/nsIPushNotifier.idl
@@ -1,32 +1,27 @@
 /* -*- Mode: IDL; 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/. */
 
 #include "nsISupports.idl"
 
 %{C++
-#include "nsTArray.h"
-#include "mozilla/Maybe.h"
-
 #define PUSHNOTIFIER_CONTRACTID \
   "@mozilla.org/push/Notifier;1"
 
 // These constants are duplicated in `PushComponents.js`.
 #define OBSERVER_TOPIC_PUSH "push-message"
 #define OBSERVER_TOPIC_SUBSCRIPTION_CHANGE "push-subscription-change"
 #define OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED "push-subscription-modified"
 %}
 
 interface nsIPrincipal;
 
-[ref] native MaybeData(const mozilla::Maybe<nsTArray<uint8_t>>);
-
 /**
  * Fires XPCOM observer notifications and service worker events for
  * messages sent to push subscriptions.
  */
 [scriptable, builtinclass, uuid(b00dfdeb-14e5-425b-adc7-b531442e3216)]
 interface nsIPushNotifier : nsISupports
 {
   /**
@@ -62,47 +57,16 @@ interface nsIPushNotifier : nsISupports
    * This is useful for Dev Tools and debugging add-ons that passively observe
    * when subscriptions are created or dropped. Other callers should listen for
    * `push-subscription-change` and resubscribe instead.
    */
   void notifySubscriptionModified(in ACString scope, in nsIPrincipal principal);
 
   void notifyError(in ACString scope, in nsIPrincipal principal,
                    in DOMString message, in uint32_t flags);
-
-  /**
-   * Internal methods used to fire service worker events and observer
-   * notifications. These are not exposed to JavaScript.
-   */
-
-  [noscript, nostdcall]
-  void notifyPushWorkers(in ACString scope,
-                         in nsIPrincipal principal,
-                         in DOMString messageId,
-                         in MaybeData data);
-
-  [noscript, nostdcall]
-  void notifyPushObservers(in ACString scope, in nsIPrincipal principal,
-                           in MaybeData data);
-
-  [noscript, nostdcall]
-  void notifySubscriptionChangeWorkers(in ACString scope,
-                                       in nsIPrincipal principal);
-
-  [noscript, nostdcall]
-  void notifySubscriptionChangeObservers(in ACString scope,
-                                         in nsIPrincipal principal);
-
-  [noscript, nostdcall]
-  void notifySubscriptionModifiedObservers(in ACString scope,
-                                           in nsIPrincipal principal);
-
-  [noscript, nostdcall, notxpcom]
-  void notifyErrorWorkers(in ACString scope, in DOMString message,
-                          in uint32_t flags);
 };
 
 /**
  * Provides methods for retrieving push message data in different formats.
  * This interface resembles the `PushMessageData` WebIDL interface.
  */
 [scriptable, builtinclass, uuid(dfc4f151-cead-40df-8eb7-7a7a67c54b16)]
 interface nsIPushData : nsISupports
--- a/dom/interfaces/security/nsIContentSecurityPolicy.idl
+++ b/dom/interfaces/security/nsIContentSecurityPolicy.idl
@@ -54,16 +54,17 @@ interface nsIContentSecurityPolicy : nsI
   const unsigned short REFLECTED_XSS_DIRECTIVE        = 12;
   const unsigned short BASE_URI_DIRECTIVE             = 13;
   const unsigned short FORM_ACTION_DIRECTIVE          = 14;
   const unsigned short REFERRER_DIRECTIVE             = 15;
   const unsigned short WEB_MANIFEST_SRC_DIRECTIVE     = 16;
   const unsigned short UPGRADE_IF_INSECURE_DIRECTIVE  = 17;
   const unsigned short CHILD_SRC_DIRECTIVE            = 18;
   const unsigned short BLOCK_ALL_MIXED_CONTENT        = 19;
+  const unsigned short REQUIRE_SRI_FOR                = 20;
 
   /**
    * Accessor method for a read-only string version of the policy at a given
    * index.
    */
   AString getPolicy(in unsigned long index);
 
   /**
@@ -181,33 +182,42 @@ interface nsIContentSecurityPolicy : nsI
    */
   void logViolationDetails(in unsigned short violationType,
                            in AString sourceFile,
                            in AString scriptSample,
                            in int32_t lineNum,
                            [optional] in AString nonce,
                            [optional] in AString content);
 
-  const unsigned short VIOLATION_TYPE_INLINE_SCRIPT = 1;
-  const unsigned short VIOLATION_TYPE_EVAL          = 2;
-  const unsigned short VIOLATION_TYPE_INLINE_STYLE  = 3;
-  const unsigned short VIOLATION_TYPE_NONCE_SCRIPT  = 4;
-  const unsigned short VIOLATION_TYPE_NONCE_STYLE   = 5;
-  const unsigned short VIOLATION_TYPE_HASH_SCRIPT   = 6;
-  const unsigned short VIOLATION_TYPE_HASH_STYLE    = 7;
+  const unsigned short VIOLATION_TYPE_INLINE_SCRIPT          = 1;
+  const unsigned short VIOLATION_TYPE_EVAL                   = 2;
+  const unsigned short VIOLATION_TYPE_INLINE_STYLE           = 3;
+  const unsigned short VIOLATION_TYPE_NONCE_SCRIPT           = 4;
+  const unsigned short VIOLATION_TYPE_NONCE_STYLE            = 5;
+  const unsigned short VIOLATION_TYPE_HASH_SCRIPT            = 6;
+  const unsigned short VIOLATION_TYPE_HASH_STYLE             = 7;
+  const unsigned short VIOLATION_TYPE_REQUIRE_SRI_FOR_STYLE  = 8;
+  const unsigned short VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT = 9;
 
   /**
    * Called after the CSP object is created to fill in appropriate request
    * context. Either use
    *  * aDocument (preferred), or if no document is available, then provide
    *  * aPrincipal
    */
   void setRequestContext(in nsIDOMDocument aDocument,
                          in nsIPrincipal aPrincipal);
 
+
+  /*
+   * Checks if a CSP requires Subresource Integrity (SRI)
+   * for a given nsContentPolicyType.
+   */
+  bool requireSRIForType(in nsContentPolicyType aContentType);
+
   /**
    * Verifies ancestry as permitted by the policy.
    *
    * NOTE: Calls to this may trigger violation reports when queried, so this
    * value should not be cached.
    *
    * @param docShell
    *    containing the protected resource
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -171,17 +171,17 @@
 #endif
 #include "NuwaChild.h"
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #ifndef MOZ_SIMPLEPUSH
-#include "nsIPushNotifier.h"
+#include "mozilla/dom/PushNotifier.h"
 #endif
 
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/cellbroadcast/CellBroadcastIPCService.h"
 #include "mozilla/dom/icc/IccChild.h"
 #include "mozilla/dom/mobileconnection/MobileConnectionChild.h"
 #include "mozilla/dom/mobilemessage/SmsChild.h"
 #include "mozilla/dom/devicestorage/DeviceStorageRequestChild.h"
@@ -3280,87 +3280,62 @@ ContentChild::RecvEndDragSession(const b
 }
 
 bool
 ContentChild::RecvPush(const nsCString& aScope,
                        const IPC::Principal& aPrincipal,
                        const nsString& aMessageId)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-
-  nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
-                                                  Nothing());
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-
-  rv = pushNotifier->NotifyPushWorkers(aScope, aPrincipal,
-                                       aMessageId, Nothing());
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
 #endif
   return true;
 }
 
 bool
 ContentChild::RecvPushWithData(const nsCString& aScope,
                                const IPC::Principal& aPrincipal,
                                const nsString& aMessageId,
                                InfallibleTArray<uint8_t>&& aData)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-
-  nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
-                                                  Some(aData));
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-
-  rv = pushNotifier->NotifyPushWorkers(aScope, aPrincipal,
-                                       aMessageId, Some(aData));
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
 #endif
   return true;
 }
 
 bool
 ContentChild::RecvPushSubscriptionChange(const nsCString& aScope,
                                          const IPC::Principal& aPrincipal)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-
-  nsresult rv = pushNotifier->NotifySubscriptionChangeObservers(aScope,
-                                                                aPrincipal);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-
-  rv = pushNotifier->NotifySubscriptionChangeWorkers(aScope, aPrincipal);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
 #endif
   return true;
 }
 
 bool
-ContentChild::RecvPushError(const nsCString& aScope, const nsString& aMessage,
-                            const uint32_t& aFlags)
+ContentChild::RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal,
+                            const nsString& aMessage, const uint32_t& aFlags)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-  pushNotifier->NotifyErrorWorkers(aScope, aMessage, aFlags);
+  PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+#endif
+  return true;
+}
+
+bool
+ContentChild::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
+                                                          const IPC::Principal& aPrincipal)
+{
+#ifndef MOZ_SIMPLEPUSH
+  PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 #endif
   return true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -534,18 +534,22 @@ public:
                    const nsString& aMessageId,
                    InfallibleTArray<uint8_t>&& aData) override;
 
   virtual bool
   RecvPushSubscriptionChange(const nsCString& aScope,
                              const IPC::Principal& aPrincipal) override;
 
   virtual bool
-  RecvPushError(const nsCString& aScope, const nsString& aMessage,
-                const uint32_t& aFlags) override;
+  RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal,
+                const nsString& aMessage, const uint32_t& aFlags) override;
+
+  virtual bool
+  RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
+                                              const IPC::Principal& aPrincipal) override;
 
   // Get the directory for IndexedDB files. We query the parent for this and
   // cache the value
   nsString &GetIndexedDBPath();
 
   ContentParentId GetID() const { return mID; }
 
   bool IsForApp() const { return mIsForApp; }
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -262,17 +262,17 @@ using namespace mozilla::system;
 #include "nsIProfileSaveEvent.h"
 #endif
 
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadMonitoring.h"
 #endif
 
 #ifndef MOZ_SIMPLEPUSH
-#include "nsIPushNotifier.h"
+#include "mozilla/dom/PushNotifier.h"
 #endif
 
 #ifdef XP_WIN
 #include "mozilla/widget/AudioSession.h"
 #endif
 
 #include "VRManagerParent.h"            // for VRManagerParent
 
@@ -5823,81 +5823,53 @@ ContentParent::StartProfiler(nsIProfiler
 }
 
 bool
 ContentParent::RecvNotifyPushObservers(const nsCString& aScope,
                                        const IPC::Principal& aPrincipal,
                                        const nsString& aMessageId)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-
-  nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
-                                                  Nothing());
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 #endif
   return true;
 }
 
 bool
 ContentParent::RecvNotifyPushObserversWithData(const nsCString& aScope,
                                                const IPC::Principal& aPrincipal,
                                                const nsString& aMessageId,
                                                InfallibleTArray<uint8_t>&& aData)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-
-  nsresult rv = pushNotifier->NotifyPushObservers(aScope, aPrincipal,
-                                                  Some(aData));
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 #endif
   return true;
 }
 
 bool
 ContentParent::RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope,
                                                          const IPC::Principal& aPrincipal)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-
-  nsresult rv = pushNotifier->NotifySubscriptionChangeObservers(aScope,
-                                                                aPrincipal);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 #endif
   return true;
 }
 
 bool
 ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
                                                            const IPC::Principal& aPrincipal)
 {
 #ifndef MOZ_SIMPLEPUSH
-  nsCOMPtr<nsIPushNotifier> pushNotifier =
-      do_GetService("@mozilla.org/push/Notifier;1");
-  if (NS_WARN_IF(!pushNotifier)) {
-      return true;
-  }
-
-  nsresult rv = pushNotifier->NotifySubscriptionModifiedObservers(aScope,
-                                                                  aPrincipal);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
+  PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 #endif
   return true;
 }
 
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -667,17 +667,18 @@ child:
     /**
      * Send a `pushsubscriptionchange` event to a service worker in the child.
      */
     async PushSubscriptionChange(nsCString scope, Principal principal);
 
     /**
      * Send a Push error message to all service worker clients in the child.
      */
-    async PushError(nsCString scope, nsString message, uint32_t flags);
+    async PushError(nsCString scope, Principal principal, nsString message,
+                    uint32_t flags);
 
     /**
      * Windows specific: associate this content process with the browsers
      * audio session.
      */
     async SetAudioSessionData(nsID aID,
                               nsString aDisplayName,
                               nsString aIconPath);
@@ -1193,21 +1194,21 @@ parent:
                                       nsString messageId, uint8_t[] data);
 
     /**
      * Notify `push-subscription-change` observers in the parent.
      */
     async NotifyPushSubscriptionChangeObservers(nsCString scope,
                                                 Principal principal);
 
+both:
+     async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
+                        Principal aPrincipal, ClonedMessageData aData);
+
     /**
-     * Notify `push-subscription-modified` observers in the parent.
+     * Notify `push-subscription-modified` observers in the parent and child.
      */
     async NotifyPushSubscriptionModifiedObservers(nsCString scope,
                                                   Principal principal);
-
-both:
-     async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
-                        Principal aPrincipal, ClonedMessageData aData);
 };
 
 }
 }
--- a/dom/locales/en-US/chrome/security/csp.properties
+++ b/dom/locales/en-US/chrome/security/csp.properties
@@ -66,16 +66,19 @@ ignoreSrcForDirective = Ignoring srcs for directive ‘%1$S’
 # %1$S is the hostname in question and %2$S is the keyword
 hostNameMightBeKeyword = Interpreting %1$S as a hostname, not a keyword. If you intended this to be a keyword, use ‘%2$S’ (wrapped in single quotes).
 # LOCALIZATION NOTE (notSupportingDirective):
 # directive is not supported (e.g. 'reflected-xss')
 notSupportingDirective = Not supporting directive ‘%1$S’. Directive and values will be ignored.
 # LOCALIZATION NOTE (blockAllMixedContent):
 # %1$S is the URL of the blocked resource load.
 blockAllMixedContent = Blocking insecure request ‘%1$S’.
+# LOCALIZATION NOTE (ignoringDirectiveWithNoValues):
+# %1$S is the name of a CSP directive that requires additional values (e.g., 'require-sri-for')
+ignoringDirectiveWithNoValues = Ignoring ‘%1$S‘ since it does not contain any parameters.
 
 # CSP Errors:
 # LOCALIZATION NOTE (couldntParseInvalidSource):
 # %1$S is the source that could not be parsed
 couldntParseInvalidSource = Couldn’t parse invalid source %1$S
 # LOCALIZATION NOTE (couldntParseInvalidHost):
 # %1$S is the host that's invalid
 couldntParseInvalidHost = Couldn’t parse invalid host %1$S
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -73,88 +73,16 @@ private:
   // Input rate in Hz (characteristic of the media being played)
   uint32_t mInRate;
   // True if the we are timestretching, false if we are resampling.
   bool mPreservesPitch;
   // The history of frames sent to the audio engine in each DataCallback.
   const nsAutoPtr<FrameHistory> mFrameHistory;
 };
 
-class CircularByteBuffer
-{
-public:
-  CircularByteBuffer()
-    : mBuffer(nullptr), mCapacity(0), mStart(0), mCount(0)
-  {}
-
-  // Set the capacity of the buffer in bytes.  Must be called before any
-  // call to append or pop elements.
-  void SetCapacity(uint32_t aCapacity) {
-    MOZ_ASSERT(!mBuffer, "Buffer allocated.");
-    mCapacity = aCapacity;
-    mBuffer = MakeUnique<uint8_t[]>(mCapacity);
-  }
-
-  uint32_t Length() {
-    return mCount;
-  }
-
-  uint32_t Capacity() {
-    return mCapacity;
-  }
-
-  uint32_t Available() {
-    return Capacity() - Length();
-  }
-
-  // Append aLength bytes from aSrc to the buffer.  Caller must check that
-  // sufficient space is available.
-  void AppendElements(const uint8_t* aSrc, uint32_t aLength) {
-    MOZ_ASSERT(mBuffer && mCapacity, "Buffer not initialized.");
-    MOZ_ASSERT(aLength <= Available(), "Buffer full.");
-
-    uint32_t end = (mStart + mCount) % mCapacity;
-
-    uint32_t toCopy = std::min(mCapacity - end, aLength);
-    memcpy(&mBuffer[end], aSrc, toCopy);
-    memcpy(&mBuffer[0], aSrc + toCopy, aLength - toCopy);
-    mCount += aLength;
-  }
-
-  // Remove aSize bytes from the buffer.  Caller must check returned size in
-  // aSize{1,2} before using the pointer returned in aData{1,2}.  Caller
-  // must not specify an aSize larger than Length().
-  void PopElements(uint32_t aSize, void** aData1, uint32_t* aSize1,
-                   void** aData2, uint32_t* aSize2) {
-    MOZ_ASSERT(mBuffer && mCapacity, "Buffer not initialized.");
-    MOZ_ASSERT(aSize <= Length(), "Request too large.");
-
-    *aData1 = &mBuffer[mStart];
-    *aSize1 = std::min(mCapacity - mStart, aSize);
-    *aData2 = &mBuffer[0];
-    *aSize2 = aSize - *aSize1;
-    mCount -= *aSize1 + *aSize2;
-    mStart += *aSize1 + *aSize2;
-    mStart %= mCapacity;
-  }
-
-  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
-  {
-    size_t amount = 0;
-    amount += aMallocSizeOf(mBuffer.get());
-    return amount;
-  }
-
-private:
-  UniquePtr<uint8_t[]> mBuffer;
-  uint32_t mCapacity;
-  uint32_t mStart;
-  uint32_t mCount;
-};
-
 /*
  * A bookkeeping class to track the read/write position of an audio buffer.
  */
 class AudioBufferCursor {
 public:
   AudioBufferCursor(AudioDataValue* aPtr, uint32_t aChannels, uint32_t aFrames)
     : mPtr(aPtr), mChannels(aChannels), mFrames(aFrames) {}
 
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -557,17 +557,13 @@ private:
   MozPromiseHolder<SeekPromise> mSeekPromise;
 
   RefPtr<VideoFrameContainer> mVideoFrameContainer;
   layers::ImageContainer* GetImageContainer();
 
 #ifdef MOZ_EME
   RefPtr<CDMProxy> mCDMProxy;
 #endif
-
-#if defined(READER_DORMANT_HEURISTIC)
-  const bool mDormantEnabled;
-#endif
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -105,18 +105,19 @@ const nsIID nsIMediaDevice::COMTypeInfo<
 namespace {
 already_AddRefed<nsIAsyncShutdownClient> GetShutdownPhase() {
   nsCOMPtr<nsIAsyncShutdownService> svc = mozilla::services::GetAsyncShutdown();
   MOZ_RELEASE_ASSERT(svc);
 
   nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
   nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(shutdownPhase));
   if (!shutdownPhase) {
-    // We are probably in a content process.
-    rv = svc->GetContentChildShutdown(getter_AddRefs(shutdownPhase));
+    // We are probably in a content process. We need to do cleanup at
+    // XPCOM shutdown in leakchecking builds.
+    rv = svc->GetXpcomWillShutdown(getter_AddRefs(shutdownPhase));
   }
   MOZ_RELEASE_ASSERT(shutdownPhase);
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   return shutdownPhase.forget();
 }
 }
 
 namespace mozilla {
--- a/dom/media/MediaShutdownManager.cpp
+++ b/dom/media/MediaShutdownManager.cpp
@@ -50,18 +50,19 @@ static nsCOMPtr<nsIAsyncShutdownClient>
 GetShutdownBarrier()
 {
   nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
   MOZ_RELEASE_ASSERT(svc);
 
   nsCOMPtr<nsIAsyncShutdownClient> barrier;
   nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
   if (!barrier) {
-    // We are probably in a content process.
-    rv = svc->GetContentChildShutdown(getter_AddRefs(barrier));
+    // We are probably in a content process. We need to do cleanup at
+    // XPCOM shutdown in leakchecking builds.
+    rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier));
   }
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
   MOZ_RELEASE_ASSERT(barrier);
   return barrier.forget();
 }
 
 void
 MediaShutdownManager::EnsureCorrectShutdownObserverState()
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -151,18 +151,19 @@ public:
   GetShutdownBarrier()
   {
     nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
     MOZ_RELEASE_ASSERT(svc);
 
     nsCOMPtr<nsIAsyncShutdownClient> barrier;
     nsresult rv = svc->GetProfileBeforeChange(getter_AddRefs(barrier));
     if (!barrier) {
-      // We are probably in a content process.
-      rv = svc->GetContentChildShutdown(getter_AddRefs(barrier));
+      // We are probably in a content process. We need to do cleanup at
+      // XPCOM shutdown in leakchecking builds.
+      rv = svc->GetXpcomWillShutdown(getter_AddRefs(barrier));
     }
     MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
     MOZ_RELEASE_ASSERT(barrier);
     return barrier.forget();
   }
 
   class ShutdownTicket final
   {
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -25,28 +25,20 @@
 #include "AndroidBridge.h"
 #endif
 #include "mozilla/layers/LayersTypes.h"
 
 #include "PDMFactory.h"
 
 namespace mozilla {
 
-#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
-#define MP4_READER_DORMANT_HEURISTIC
-#else
-#undef MP4_READER_DORMANT_HEURISTIC
-#endif
-
 MP4Decoder::MP4Decoder(MediaDecoderOwner* aOwner)
   : MediaDecoder(aOwner)
 {
-#if defined(MP4_READER_DORMANT_HEURISTIC)
   mDormantSupported = Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false);
-#endif
 }
 
 MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
 {
   mReader =
     new MediaFormatReader(this,
                           new MP4Demuxer(GetResource()),
                           GetVideoFrameContainer());
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -28,22 +28,16 @@ class JSObject;
 
 extern mozilla::LogModule* GetMediaSourceLog();
 extern mozilla::LogModule* GetMediaSourceAPILog();
 
 #define MSE_DEBUG(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #define MSE_DEBUGV(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 #define MSE_API(arg, ...) MOZ_LOG(GetMediaSourceAPILog(), mozilla::LogLevel::Debug, ("SourceBuffer(%p:%s)::%s: " arg, this, mType.get(), __func__, ##__VA_ARGS__))
 
-#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN) || defined(MOZ_APPLEMEDIA) || defined(MOZ_FFMPEG)
-#define MP4_READER_DORMANT_HEURISTIC
-#else
-#undef MP4_READER_DORMANT_HEURISTIC
-#endif
-
 namespace mozilla {
 
 using media::TimeUnit;
 typedef SourceBufferAttributes::AppendState AppendState;
 
 namespace dom {
 
 void
@@ -303,19 +297,17 @@ SourceBuffer::SourceBuffer(MediaSource* 
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
 
   mTrackBuffersManager =
     new TrackBuffersManager(aMediaSource->GetDecoder(), aType);
 
   // Now that we know what type we're dealing with, enable dormant as needed.
-#if defined(MP4_READER_DORMANT_HEURISTIC)
   aMediaSource->GetDecoder()->NotifyDormantSupported(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false));
-#endif
 
   MSE_DEBUG("Create mTrackBuffersManager=%p",
             mTrackBuffersManager.get());
 
   ErrorResult dummy;
   if (mCurrentAttributes.mGenerateTimestamps) {
     SetMode(SourceBufferAppendMode::Sequence, dummy);
   } else {
--- a/dom/media/ogg/OggReader.cpp
+++ b/dom/media/ogg/OggReader.cpp
@@ -567,19 +567,20 @@ nsresult OggReader::DecodeVorbis(ogg_pac
 nsresult OggReader::DecodeOpus(ogg_packet* aPacket) {
   NS_ASSERTION(aPacket->granulepos != -1, "Must know opus granulepos!");
 
   // Maximum value is 63*2880, so there's no chance of overflow.
   int32_t frames_number = opus_packet_get_nb_frames(aPacket->packet,
                                                     aPacket->bytes);
   if (frames_number <= 0)
     return NS_ERROR_FAILURE; // Invalid packet header.
-  int32_t samples = opus_packet_get_samples_per_frame(aPacket->packet,
-                                                      (opus_int32) mOpusState->mRate);
-  int32_t frames = frames_number*samples;
+  int32_t samplesPerFrame =
+      opus_packet_get_samples_per_frame(aPacket->packet,
+                                        (opus_int32) mOpusState->mRate);
+  int32_t frames = frames_number * samplesPerFrame;
 
   // A valid Opus packet must be between 2.5 and 120 ms long.
   if (frames < 120 || frames > 5760)
     return NS_ERROR_FAILURE;
   uint32_t channels = mOpusState->mChannels;
   AlignedAudioBuffer buffer(frames * channels);
   if (!buffer) {
     return NS_ERROR_OUT_OF_MEMORY;
@@ -617,22 +618,22 @@ nsresult OggReader::DecodeOpus(ogg_packe
     if (skipFrames == frames) {
       // discard the whole packet
       mOpusState->mSkip -= frames;
       LOG(LogLevel::Debug, ("Opus decoder skipping %d frames"
                          " (whole packet)", frames));
       return NS_OK;
     }
     int32_t keepFrames = frames - skipFrames;
-    int samples = keepFrames * channels;
-    AlignedAudioBuffer trimBuffer(samples);
+    int keepSamples = keepFrames * channels;
+    AlignedAudioBuffer trimBuffer(keepSamples);
     if (!trimBuffer) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
-    for (int i = 0; i < samples; i++)
+    for (int i = 0; i < keepSamples; i++)
       trimBuffer[i] = buffer[skipFrames*channels + i];
 
     startFrame = endFrame - keepFrames;
     frames = keepFrames;
     buffer = Move(trimBuffer);
 
     mOpusState->mSkip -= skipFrames;
     LOG(LogLevel::Debug, ("Opus decoder skipping %d frames", skipFrames));
@@ -640,26 +641,26 @@ nsresult OggReader::DecodeOpus(ogg_packe
   // Save this packet's granule position in case we need to perform end
   // trimming on the next packet.
   mOpusState->mPrevPacketGranulepos = endFrame;
 
   // Apply the header gain if one was specified.
 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
   if (mOpusState->mGain != 1.0f) {
     float gain = mOpusState->mGain;
-    int samples = frames * channels;
-    for (int i = 0; i < samples; i++) {
+    int gainSamples = frames * channels;
+    for (int i = 0; i < gainSamples; i++) {
       buffer[i] *= gain;
     }
   }
 #else
   if (mOpusState->mGain_Q16 != 65536) {
     int64_t gain_Q16 = mOpusState->mGain_Q16;
-    int samples = frames * channels;
-    for (int i = 0; i < samples; i++) {
+    int gainSamples = frames * channels;
+    for (int i = 0; i < gainSamples; i++) {
       int32_t val = static_cast<int32_t>((gain_Q16*buffer[i] + 32768)>>16);
       buffer[i] = static_cast<AudioDataValue>(MOZ_CLIP_TO_15(val));
     }
   }
 #endif
 
   // No channel mapping for more than 8 channels.
   if (channels > 8) {
@@ -1332,17 +1333,16 @@ OggReader::IndexedSeekResult OggReader::
 nsresult OggReader::SeekInBufferedRange(int64_t aTarget,
                                           int64_t aAdjustedTarget,
                                           int64_t aStartTime,
                                           int64_t aEndTime,
                                           const nsTArray<SeekRange>& aRanges,
                                           const SeekRange& aRange)
 {
   LOG(LogLevel::Debug, ("%p Seeking in buffered data to %lld using bisection search", mDecoder, aTarget));
-  nsresult res = NS_OK;
   if (HasVideo() || aAdjustedTarget >= aTarget) {
     // We know the exact byte range in which the target must lie. It must
     // be buffered in the media cache. Seek there.
     nsresult res = SeekBisection(aTarget, aRange, 0);
     if (NS_FAILED(res) || !HasVideo()) {
       return res;
     }
 
@@ -1366,16 +1366,18 @@ nsresult OggReader::SeekInBufferedRange(
       int shift = mTheoraState->mInfo.keyframe_granule_shift;
       int64_t keyframeGranulepos = (video->mTimecode >> shift) << shift;
       int64_t keyframeTime = mTheoraState->StartTime(keyframeGranulepos);
       SEEK_LOG(LogLevel::Debug, ("Keyframe for %lld is at %lld, seeking back to it",
                               video->mTime, keyframeTime));
       aAdjustedTarget = std::min(aAdjustedTarget, keyframeTime);
     }
   }
+
+  nsresult res = NS_OK;
   if (aAdjustedTarget < aTarget) {
     SeekRange k = SelectSeekRange(aRanges,
                                   aAdjustedTarget,
                                   aStartTime,
                                   aEndTime,
                                   false);
     res = SeekBisection(aAdjustedTarget, k, SEEK_FUZZ_USECS);
   }
@@ -1692,26 +1694,26 @@ nsresult OggReader::SeekBisection(int64_
       NS_ASSERTION(guess != previousGuess, "Guess should be different to previous");
       previousGuess = guess;
 
       hops++;
 
       // Locate the next page after our seek guess, and then figure out the
       // granule time of the audio and video bitstreams there. We can then
       // make a bisection decision based on our location in the media.
-      PageSyncResult res = PageSync(&mResource,
-                                    &mOggState,
-                                    false,
-                                    guess,
-                                    endOffset,
-                                    &page,
-                                    skippedBytes);
-      NS_ENSURE_TRUE(res != PAGE_SYNC_ERROR, NS_ERROR_FAILURE);
+      PageSyncResult pageSyncResult = PageSync(&mResource,
+                                               &mOggState,
+                                               false,
+                                               guess,
+                                               endOffset,
+                                               &page,
+                                               skippedBytes);
+      NS_ENSURE_TRUE(pageSyncResult != PAGE_SYNC_ERROR, NS_ERROR_FAILURE);
 
-      if (res == PAGE_SYNC_END_OF_RANGE) {
+      if (pageSyncResult == PAGE_SYNC_END_OF_RANGE) {
         // Our guess was too close to the end, we've ended up reading the end
         // page. Backoff exponentially from the end point, in case the last
         // page/frame/sample is huge.
         mustBackoff = true;
         SEEK_LOG(LogLevel::Debug, ("Hit the end of range, backing off"));
         continue;
       }
 
@@ -1886,26 +1888,26 @@ media::TimeIntervals OggReader::GetBuffe
 
     // Find the start time of the range. Read pages until we find one with a
     // granulepos which we can convert into a timestamp to use as the time of
     // the start of the buffered range.
     ogg_sync_reset(&sync.mState);
     while (startTime == -1) {
       ogg_page page;
       int32_t discard;
-      PageSyncResult res = PageSync(&mResource,
-                                    &sync.mState,
-                                    true,
-                                    startOffset,
-                                    endOffset,
-                                    &page,
-                                    discard);
-      if (res == PAGE_SYNC_ERROR) {
+      PageSyncResult pageSyncResult = PageSync(&mResource,
+                                               &sync.mState,
+                                               true,
+                                               startOffset,
+                                               endOffset,
+                                               &page,
+                                               discard);
+      if (pageSyncResult == PAGE_SYNC_ERROR) {
         return media::TimeIntervals::Invalid();
-      } else if (res == PAGE_SYNC_END_OF_RANGE) {
+      } else if (pageSyncResult == PAGE_SYNC_END_OF_RANGE) {
         // Hit the end of range without reading a page, give up trying to
         // find a start time for this buffered range, skip onto the next one.
         break;
       }
 
       int64_t granulepos = ogg_page_granulepos(&page);
       if (granulepos == -1) {
         // Page doesn't have an end time, advance to the next page
@@ -2039,9 +2041,8 @@ bool OggCodecStore::Contains(uint32_t se
 
 OggCodecState* OggCodecStore::Get(uint32_t serial)
 {
   MonitorAutoLock mon(mMonitor);
   return mCodecStates.Get(serial);
 }
 
 } // namespace mozilla
-
--- a/dom/media/ogg/OpusParser.cpp
+++ b/dom/media/ogg/OpusParser.cpp
@@ -160,18 +160,17 @@ bool OpusParser::DecodeTags(unsigned cha
     return false;
   uint32_t ncomments = LittleEndian::readUint32(buf);
   buf += 4;
   bytes -= 4;
   // If there are so many comments even their length fields
   // won't fit in the packet, stop reading now.
   if (ncomments > (bytes>>2))
     return false;
-  uint32_t i;
-  for (i = 0; i < ncomments; i++) {
+  for (uint32_t i = 0; i < ncomments; i++) {
     if (bytes < 4)
       return false;
     len = LittleEndian::readUint32(buf);
     buf += 4;
     bytes -= 4;
     if (len > bytes)
       return false;
     mTags.AppendElement(nsCString(reinterpret_cast<const char*>(buf), len));
@@ -185,9 +184,8 @@ bool OpusParser::DecodeTags(unsigned cha
   for (uint32_t i = 0; i < mTags.Length(); i++) {
     OPUS_LOG(LogLevel::Debug, (" %s", mTags[i].get()));
   }
 #endif
   return true;
 }
 
 } // namespace mozilla
-
--- a/dom/media/ogg/moz.build
+++ b/dom/media/ogg/moz.build
@@ -16,11 +16,8 @@ UNIFIED_SOURCES += [
     'OggCodecState.cpp',
     'OggDecoder.cpp',
     'OggReader.cpp',
     'OggWriter.cpp',
     'OpusParser.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
-
-if CONFIG['GNU_CXX']:
-    CXXFLAGS += ['-Wno-error=shadow']
--- a/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp
+++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp
@@ -170,31 +170,31 @@ GonkDecoderManager::ProcessInput(bool aE
         mToDo->setInt32("input-eos", 1);
       }
       mDecoder->requestActivityNotification(mToDo);
     } else if (aEndOfStream) {
       mToDo->setInt32("input-eos", 1);
     }
   } else {
     GMDD_LOG("input processed: error#%d", rv);
-    mDecodeCallback->Error();
+    mDecodeCallback->Error(MediaDataDecoderError::FATAL_ERROR);
   }
 }
 
 void
 GonkDecoderManager::ProcessFlush()
 {
   MOZ_ASSERT(OnTaskLooper());
 
   mLastTime = INT64_MIN;
   MonitorAutoLock lock(mFlushMonitor);
   mWaitOutput.Clear();
   if (mDecoder->flush() != OK) {
     GMDD_LOG("flush error");
-    mDecodeCallback->Error();
+    mDecodeCallback->Error(MediaDataDecoderError::FATAL_ERROR);
   }
   mIsFlushing = false;
   lock.NotifyAll();
 }
 
 // Use output timestamp to determine which output buffer is already returned
 // and remove corresponding info, except for EOS, from the waiting list.
 // This method handles the cases that audio decoder sends multiple output
@@ -220,17 +220,17 @@ void
 GonkDecoderManager::ProcessToDo(bool aEndOfStream)
 {
   MOZ_ASSERT(OnTaskLooper());
 
   MOZ_ASSERT(mToDo.get() != nullptr);
   mToDo.clear();
 
   if (NumQueuedSamples() > 0 && ProcessQueuedSamples() < 0) {
-    mDecodeCallback->Error();
+    mDecodeCallback->Error(MediaDataDecoderError::FATAL_ERROR);
     return;
   }
 
   while (mWaitOutput.Length() > 0) {
     RefPtr<MediaData> output;
     WaitOutputInfo wait = mWaitOutput.ElementAt(0);
     nsresult rv = Output(wait.mOffset, output);
     if (rv == NS_OK) {
@@ -247,17 +247,17 @@ GonkDecoderManager::ProcessToDo(bool aEn
       MOZ_ASSERT(mWaitOutput.Length() == 1);
       mWaitOutput.RemoveElementAt(0);
       mDecodeCallback->DrainComplete();
       ResetEOS();
       return;
     } else if (rv == NS_ERROR_NOT_AVAILABLE) {
       break;
     } else {
-      mDecodeCallback->Error();
+      mDecodeCallback->Error(MediaDataDecoderError::FATAL_ERROR);
       return;
     }
   }
 
   if (!aEndOfStream && NumQueuedSamples() <= MIN_QUEUED_SAMPLES) {
     mDecodeCallback->InputExhausted();
     // No need to shedule todo task this time because InputExhausted() will
     // cause Input() to be invoked and do it for us.
@@ -275,17 +275,17 @@ GonkDecoderManager::ProcessToDo(bool aEn
 
 void
 GonkDecoderManager::ResetEOS()
 {
   // After eos, android::MediaCodec needs to be flushed to receive next input
   mWaitOutput.Clear();
   if (mDecoder->flush() != OK) {
     GMDD_LOG("flush error");
-    mDecodeCallback->Error();
+    mDecodeCallback->Error(MediaDataDecoderError::FATAL_ERROR);
   }
 }
 
 void
 GonkDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
 {
   switch (aMessage->what()) {
     case kNotifyProcessInput:
--- a/dom/media/test/external/external_media_harness/testcase.py
+++ b/dom/media/test/external/external_media_harness/testcase.py
@@ -1,14 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re
 import os
+import time
 
 from marionette import BrowserMobProxyTestCaseMixin, MarionetteTestCase
 from marionette_driver import Wait
 from marionette_driver.errors import TimeoutException
 from marionette.marionette_test import SkipTest
 
 from firefox_puppeteer.testcases import BaseFirefoxTestCase
 from external_media_tests.utils import (timestamp_now, verbose_until)
@@ -195,17 +196,17 @@ class NetworkBandwidthTestsMixin(object)
 
     def test_playback_limiting_bandwidth_1000(self):
         self.proxy.limits({'downstream_kbps': 1000})
         self.run_videos(timeout=120)
 
 
 reset_adobe_gmp_script = """
 navigator.requestMediaKeySystemAccess('com.adobe.primetime',
-[{initDataType: 'cenc'}]).then(
+[{initDataTypes: ['cenc']}]).then(
     function(access) {
         marionetteScriptFinished('success');
     },
     function(ex) {
         marionetteScriptFinished(ex);
     }
 );
 """
@@ -236,18 +237,19 @@ class EMESetupMixin(object):
             # https://bugzilla.mozilla.org/show_bug.cgi?id=1187471#c28
             # 2015-09-28 cpearce says this is no longer necessary, but in case
             # we are working with older firefoxes...
             self.prefs.set_pref('media.gmp.trial-create.enabled', False)
 
     def reset_GMP_version(self):
         if EMESetupMixin.version_needs_reset:
             with self.marionette.using_context('chrome'):
-                if self.prefs.get_pref('media.gm-eme-adobe.version'):
-                    self.prefs.set_pref('media.gm-eme-adobe.version', None)
+                if self.prefs.get_pref('media.gmp-eme-adobe.version'):
+                    self.prefs.reset_pref('media.gmp-eme-adobe.version')
+            with self.marionette.using_context('content'):
                 result = self.marionette.execute_async_script(
                     reset_adobe_gmp_script,
                     script_timeout=60000)
                 if not result == 'success':
                     raise VideoException(
                         'ERROR: Resetting Adobe GMP failed % s' % result)
 
             EMESetupMixin.version_needs_reset = False
--- a/dom/push/PushNotifier.cpp
+++ b/dom/push/PushNotifier.cpp
@@ -49,322 +49,92 @@ PushNotifier::NotifyPushWithData(const n
 {
   nsTArray<uint8_t> data;
   if (!data.SetCapacity(aDataLen, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
   if (!data.InsertElementsAt(0, aData, aDataLen, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
-  return NotifyPush(aScope, aPrincipal, aMessageId, Some(data));
+  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(data));
+  return Dispatch(dispatcher);
 }
 
 NS_IMETHODIMP
 PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
                          const nsAString& aMessageId)
 {
-  return NotifyPush(aScope, aPrincipal, aMessageId, Nothing());
+  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
+  return Dispatch(dispatcher);
 }
 
 NS_IMETHODIMP
 PushNotifier::NotifySubscriptionChange(const nsACString& aScope,
                                        nsIPrincipal* aPrincipal)
 {
-  nsresult rv = NotifySubscriptionChangeObservers(aScope, aPrincipal);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-
-  if (XRE_IsContentProcess()) {
-    // Forward XPCOM observer notifications to the parent.
-    ContentChild* parentActor = ContentChild::GetSingleton();
-    if (!NS_WARN_IF(!parentActor)) {
-      Unused << NS_WARN_IF(
-        !parentActor->SendNotifyPushSubscriptionChangeObservers(
-          PromiseFlatCString(aScope), IPC::Principal(aPrincipal)));
-    }
-  }
-
-  rv = NotifySubscriptionChangeWorkers(aScope, aPrincipal);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
+  PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+  return Dispatch(dispatcher);
 }
 
 NS_IMETHODIMP
 PushNotifier::NotifySubscriptionModified(const nsACString& aScope,
                                          nsIPrincipal* aPrincipal)
 {
-  nsresult rv = NotifySubscriptionModifiedObservers(aScope, aPrincipal);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-
-  if (XRE_IsContentProcess()) {
-    ContentChild* parentActor = ContentChild::GetSingleton();
-    if (!NS_WARN_IF(!parentActor)) {
-      Unused << NS_WARN_IF(
-        !parentActor->SendNotifyPushSubscriptionModifiedObservers(
-          PromiseFlatCString(aScope), IPC::Principal(aPrincipal)));
-    }
-  }
-
-  return NS_OK;
+  PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+  return Dispatch(dispatcher);
 }
 
 NS_IMETHODIMP
 PushNotifier::NotifyError(const nsACString& aScope, nsIPrincipal* aPrincipal,
                           const nsAString& aMessage, uint32_t aFlags)
 {
-  if (ShouldNotifyWorkers(aPrincipal)) {
-    // For service worker subscriptions, report the error to all clients.
-    NotifyErrorWorkers(aScope, aMessage, aFlags);
-    return NS_OK;
-  }
-  // For system subscriptions, log the error directly to the browser console.
-  return nsContentUtils::ReportToConsoleNonLocalized(aMessage,
-                                                     aFlags,
-                                                     NS_LITERAL_CSTRING("Push"),
-                                                     nullptr, /* aDocument */
-                                                     nullptr, /* aURI */
-                                                     EmptyString(), /* aLine */
-                                                     0, /* aLineNumber */
-                                                     0, /* aColumnNumber */
-                                                     nsContentUtils::eOMIT_LOCATION);
-}
-
-nsresult
-PushNotifier::NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
-                         const nsAString& aMessageId,
-                         const Maybe<nsTArray<uint8_t>>& aData)
-{
-  // Notify XPCOM observers in the current process.
-  nsresult rv = NotifyPushObservers(aScope, aPrincipal, aData);
-  Unused << NS_WARN_IF(NS_FAILED(rv));
-
-  if (XRE_IsContentProcess()) {
-    // If we're in the content process, forward the notification to the parent.
-    // We don't need to do anything if we're already in the parent;
-    // `ContentChild::RecvPush` will notify content process observers.
-    ContentChild* parentActor = ContentChild::GetSingleton();
-    if (!NS_WARN_IF(!parentActor)) {
-      if (aData) {
-        Unused << NS_WARN_IF(
-          !parentActor->SendNotifyPushObserversWithData(
-            PromiseFlatCString(aScope), IPC::Principal(aPrincipal),
-            PromiseFlatString(aMessageId), aData.ref()));
-      } else {
-        Unused << NS_WARN_IF(
-          !parentActor->SendNotifyPushObservers(
-            PromiseFlatCString(aScope), IPC::Principal(aPrincipal),
-            PromiseFlatString(aMessageId)));
-      }
-    }
-  }
-
-  rv = NotifyPushWorkers(aScope, aPrincipal, aMessageId, aData);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  return NS_OK;
-}
-
-nsresult
-PushNotifier::NotifyPushWorkers(const nsACString& aScope,
-                                nsIPrincipal* aPrincipal,
-                                const nsAString& aMessageId,
-                                const Maybe<nsTArray<uint8_t>>& aData)
-{
-  AssertIsOnMainThread();
-  NS_ENSURE_ARG(aPrincipal);
-
-  if (XRE_IsContentProcess() || !BrowserTabsRemoteAutostart()) {
-    // Notify the worker from the current process. Either we're running in
-    // the content process and received a message from the parent, or e10s
-    // is disabled.
-    if (!ShouldNotifyWorkers(aPrincipal)) {
-      return NS_OK;
-    }
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    if (!swm) {
-      return NS_ERROR_FAILURE;
-    }
-    nsAutoCString originSuffix;
-    nsresult rv = aPrincipal->GetOriginSuffix(originSuffix);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    return swm->SendPushEvent(originSuffix, aScope, aMessageId, aData);
-  }
-
-  // Otherwise, we're in the parent and e10s is enabled. Broadcast the event
-  // to all content processes.
-  bool ok = true;
-  nsTArray<ContentParent*> contentActors;
-  ContentParent::GetAll(contentActors);
-  for (uint32_t i = 0; i < contentActors.Length(); ++i) {
-    if (aData) {
-      ok &= contentActors[i]->SendPushWithData(PromiseFlatCString(aScope),
-        IPC::Principal(aPrincipal), PromiseFlatString(aMessageId), aData.ref());
-    } else {
-      ok &= contentActors[i]->SendPush(PromiseFlatCString(aScope),
-        IPC::Principal(aPrincipal), PromiseFlatString(aMessageId));
-    }
-  }
-  return ok ? NS_OK : NS_ERROR_FAILURE;
+  PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
+  return Dispatch(dispatcher);
 }
 
 nsresult
-PushNotifier::NotifySubscriptionChangeWorkers(const nsACString& aScope,
-                                              nsIPrincipal* aPrincipal)
+PushNotifier::Dispatch(PushDispatcher& aDispatcher)
 {
-  AssertIsOnMainThread();
-  NS_ENSURE_ARG(aPrincipal);
+  if (XRE_IsParentProcess()) {
+    // Always notify XPCOM observers in the parent process.
+    Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers()));
 
-  if (XRE_IsContentProcess() || !BrowserTabsRemoteAutostart()) {
-    // Content process or e10s disabled.
-    if (!ShouldNotifyWorkers(aPrincipal)) {
+    nsTArray<ContentParent*> contentActors;
+    ContentParent::GetAll(contentActors);
+    if (!contentActors.IsEmpty()) {
+      // At least one content process is active, so e10s must be enabled.
+      // Broadcast a message to notify observers and service workers.
+      for (uint32_t i = 0; i < contentActors.Length(); ++i) {
+        Unused << NS_WARN_IF(!aDispatcher.SendToChild(contentActors[i]));
+      }
       return NS_OK;
     }
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    if (!swm) {
-      return NS_ERROR_FAILURE;
-    }
-    nsAutoCString originSuffix;
-    nsresult rv = aPrincipal->GetOriginSuffix(originSuffix);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-    return swm->SendPushSubscriptionChangeEvent(originSuffix, aScope);
-  }
 
-  // Parent process, e10s enabled.
-  bool ok = true;
-  nsTArray<ContentParent*> contentActors;
-  ContentParent::GetAll(contentActors);
-  for (uint32_t i = 0; i < contentActors.Length(); ++i) {
-    ok &= contentActors[i]->SendPushSubscriptionChange(
-      PromiseFlatCString(aScope), IPC::Principal(aPrincipal));
-  }
-  return ok ? NS_OK : NS_ERROR_FAILURE;
-}
+    if (BrowserTabsRemoteAutostart()) {
+      // e10s is enabled, but no content processes are active.
+      return aDispatcher.HandleNoChildProcesses();
+    }
 
-void
-PushNotifier::NotifyErrorWorkers(const nsACString& aScope,
-                                 const nsAString& aMessage,
-                                 uint32_t aFlags)
-{
-  AssertIsOnMainThread();
-
-  if (XRE_IsContentProcess() || !BrowserTabsRemoteAutostart()) {
-    // Content process or e10s disabled.
-    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    if (swm) {
-      swm->ReportToAllClients(PromiseFlatCString(aScope),
-                              PromiseFlatString(aMessage),
-                              NS_ConvertUTF8toUTF16(aScope), /* aFilename */
-                              EmptyString(), /* aLine */
-                              0, /* aLineNumber */
-                              0, /* aColumnNumber */
-                              aFlags);
-    }
-    return;
+    // e10s is disabled; notify workers in the parent.
+    return aDispatcher.NotifyWorkers();
   }
 
-  // Parent process, e10s enabled.
-  nsTArray<ContentParent*> contentActors;
-  ContentParent::GetAll(contentActors);
-  if (!contentActors.IsEmpty()) {
-    // At least one content process active.
-    for (uint32_t i = 0; i < contentActors.Length(); ++i) {
-      Unused << NS_WARN_IF(
-        !contentActors[i]->SendPushError(PromiseFlatCString(aScope),
-          PromiseFlatString(aMessage), aFlags));
-    }
-    return;
-  }
-  // Report to the console if no content processes are active.
-  nsCOMPtr<nsIURI> scopeURI;
-  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-  Unused << NS_WARN_IF(NS_FAILED(
-    nsContentUtils::ReportToConsoleNonLocalized(aMessage,
-                                                aFlags,
-                                                NS_LITERAL_CSTRING("Push"),
-                                                nullptr, /* aDocument */
-                                                scopeURI, /* aURI */
-                                                EmptyString(), /* aLine */
-                                                0, /* aLineNumber */
-                                                0, /* aColumnNumber */
-                                                nsContentUtils::eOMIT_LOCATION)));
-}
+  // Otherwise, we're in the content process, so e10s must be enabled. Notify
+  // observers and workers, then send a message to notify observers in the
+  // parent.
+  MOZ_ASSERT(XRE_IsContentProcess());
 
-nsresult
-PushNotifier::NotifyPushObservers(const nsACString& aScope,
-                                  nsIPrincipal* aPrincipal,
-                                  const Maybe<nsTArray<uint8_t>>& aData)
-{
-  nsCOMPtr<nsIPushData> data;
-  if (aData) {
-    data = new PushData(aData.ref());
-  }
-  nsCOMPtr<nsIPushMessage> message = new PushMessage(aPrincipal, data);
-  return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, aScope);
-}
+  nsresult rv = aDispatcher.NotifyObserversAndWorkers();
 
-nsresult
-PushNotifier::NotifySubscriptionChangeObservers(const nsACString& aScope,
-                                                nsIPrincipal* aPrincipal)
-{
-  return DoNotifyObservers(aPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
-                           aScope);
-}
-
-nsresult
-PushNotifier::NotifySubscriptionModifiedObservers(const nsACString& aScope,
-                                                  nsIPrincipal* aPrincipal)
-{
-  return DoNotifyObservers(aPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
-                           aScope);
-}
-
-nsresult
-PushNotifier::DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
-                                const nsACString& aScope)
-{
-  nsCOMPtr<nsIObserverService> obsService =
-    mozilla::services::GetObserverService();
-  if (!obsService) {
-    return NS_ERROR_FAILURE;
+  ContentChild* parentActor = ContentChild::GetSingleton();
+  if (!NS_WARN_IF(!parentActor)) {
+    Unused << NS_WARN_IF(!aDispatcher.SendToParent(parentActor));
   }
-  // If there's a service for this push category, make sure it is alive.
-  nsCOMPtr<nsICategoryManager> catMan =
-    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
-  if (catMan) {
-    nsXPIDLCString contractId;
-    nsresult rv = catMan->GetCategoryEntry("push",
-                                           PromiseFlatCString(aScope).get(),
-                                           getter_Copies(contractId));
-    if (NS_SUCCEEDED(rv)) {
-      // Ensure the service is created - we don't need to do anything with
-      // it though - we assume the service constructor attaches a listener.
-      nsCOMPtr<nsISupports> service = do_GetService(contractId);
-    }
-  }
-  return obsService->NotifyObservers(aSubject, aTopic,
-                                     NS_ConvertUTF8toUTF16(aScope).get());
-}
 
-bool
-PushNotifier::ShouldNotifyWorkers(nsIPrincipal* aPrincipal)
-{
-  // System subscriptions use observer notifications instead of service worker
-  // events. The `testing.notifyWorkers` pref disables worker events for
-  // non-system subscriptions.
-  return !nsContentUtils::IsSystemPrincipal(aPrincipal) &&
-         Preferences::GetBool("dom.push.testing.notifyWorkers", true);
+  return rv;
 }
 
 PushData::PushData(const nsTArray<uint8_t>& aData)
   : mData(aData)
 {}
 
 PushData::~PushData()
 {}
@@ -476,10 +246,297 @@ PushMessage::GetData(nsIPushData** aData
 {
   NS_ENSURE_ARG_POINTER(aData);
 
   nsCOMPtr<nsIPushData> data = mData;
   data.forget(aData);
   return NS_OK;
 }
 
+PushDispatcher::PushDispatcher(const nsACString& aScope,
+                               nsIPrincipal* aPrincipal)
+  : mScope(aScope)
+  , mPrincipal(aPrincipal)
+{}
+
+PushDispatcher::~PushDispatcher()
+{}
+
+nsresult
+PushDispatcher::HandleNoChildProcesses()
+{
+  return NS_OK;
+}
+
+nsresult
+PushDispatcher::NotifyObserversAndWorkers()
+{
+  Unused << NS_WARN_IF(NS_FAILED(NotifyObservers()));
+  return NotifyWorkers();
+}
+
+bool
+PushDispatcher::ShouldNotifyWorkers()
+{
+  // System subscriptions use observer notifications instead of service worker
+  // events. The `testing.notifyWorkers` pref disables worker events for
+  // non-system subscriptions.
+  return !nsContentUtils::IsSystemPrincipal(mPrincipal) &&
+         Preferences::GetBool("dom.push.testing.notifyWorkers", true);
+}
+
+nsresult
+PushDispatcher::DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
+                                  const nsACString& aScope)
+{
+  nsCOMPtr<nsIObserverService> obsService =
+    mozilla::services::GetObserverService();
+  if (!obsService) {
+    return NS_ERROR_FAILURE;
+  }
+  // If there's a service for this push category, make sure it is alive.
+  nsCOMPtr<nsICategoryManager> catMan =
+    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+  if (catMan) {
+    nsXPIDLCString contractId;
+    nsresult rv = catMan->GetCategoryEntry("push",
+                                           mScope.BeginReading(),
+                                           getter_Copies(contractId));
+    if (NS_SUCCEEDED(rv)) {
+      // Ensure the service is created - we don't need to do anything with
+      // it though - we assume the service constructor attaches a listener.
+      nsCOMPtr<nsISupports> service = do_GetService(contractId);
+    }
+  }
+  return obsService->NotifyObservers(aSubject, aTopic,
+                                     NS_ConvertUTF8toUTF16(mScope).get());
+}
+
+PushMessageDispatcher::PushMessageDispatcher(const nsACString& aScope,
+                                             nsIPrincipal* aPrincipal,
+                                             const nsAString& aMessageId,
+                                             const Maybe<nsTArray<uint8_t>>& aData)
+  : PushDispatcher(aScope, aPrincipal)
+  , mMessageId(aMessageId)
+  , mData(aData)
+{}
+
+PushMessageDispatcher::~PushMessageDispatcher()
+{}
+
+nsresult
+PushMessageDispatcher::NotifyObservers()
+{
+  nsCOMPtr<nsIPushData> data;
+  if (mData) {
+    data = new PushData(mData.ref());
+  }
+  nsCOMPtr<nsIPushMessage> message = new PushMessage(mPrincipal, data);
+  return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, mScope);
+}
+
+nsresult
+PushMessageDispatcher::NotifyWorkers()
+{
+  if (!ShouldNotifyWorkers()) {
+    return NS_OK;
+  }
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    return NS_ERROR_FAILURE;
+  }
+  nsAutoCString originSuffix;
+  nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return swm->SendPushEvent(originSuffix, mScope, mMessageId, mData);
+}
+
+bool
+PushMessageDispatcher::SendToParent(ContentChild* aParentActor)
+{
+  if (mData) {
+    return aParentActor->SendNotifyPushObserversWithData(mScope,
+                                                         IPC::Principal(mPrincipal),
+                                                         mMessageId,
+                                                         mData.ref());
+  }
+  return aParentActor->SendNotifyPushObservers(mScope,
+                                               IPC::Principal(mPrincipal),
+                                               mMessageId);
+}
+
+bool
+PushMessageDispatcher::SendToChild(ContentParent* aContentActor)
+{
+  if (mData) {
+    return aContentActor->SendPushWithData(mScope, IPC::Principal(mPrincipal),
+                                           mMessageId, mData.ref());
+  }
+  return aContentActor->SendPush(mScope, IPC::Principal(mPrincipal),
+                                 mMessageId);
+}
+
+PushSubscriptionChangeDispatcher::PushSubscriptionChangeDispatcher(const nsACString& aScope,
+                                                                   nsIPrincipal* aPrincipal)
+  : PushDispatcher(aScope, aPrincipal)
+{}
+
+PushSubscriptionChangeDispatcher::~PushSubscriptionChangeDispatcher()
+{}
+
+nsresult
+PushSubscriptionChangeDispatcher::NotifyObservers()
+{
+  return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
+                           mScope);
+}
+
+nsresult
+PushSubscriptionChangeDispatcher::NotifyWorkers()
+{
+  if (!ShouldNotifyWorkers()) {
+    return NS_OK;
+  }
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    return NS_ERROR_FAILURE;
+  }
+  nsAutoCString originSuffix;
+  nsresult rv = mPrincipal->GetOriginSuffix(originSuffix);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return swm->SendPushSubscriptionChangeEvent(originSuffix, mScope);
+}
+
+bool
+PushSubscriptionChangeDispatcher::SendToParent(ContentChild* aParentActor)
+{
+  return aParentActor->SendNotifyPushSubscriptionChangeObservers(mScope,
+                                                                 IPC::Principal(mPrincipal));
+}
+
+bool
+PushSubscriptionChangeDispatcher::SendToChild(ContentParent* aContentActor)
+{
+  return aContentActor->SendPushSubscriptionChange(mScope,
+                                                   IPC::Principal(mPrincipal));
+}
+
+PushSubscriptionModifiedDispatcher::PushSubscriptionModifiedDispatcher(const nsACString& aScope,
+                                                                       nsIPrincipal* aPrincipal)
+  : PushDispatcher(aScope, aPrincipal)
+{}
+
+PushSubscriptionModifiedDispatcher::~PushSubscriptionModifiedDispatcher()
+{}
+
+nsresult
+PushSubscriptionModifiedDispatcher::NotifyObservers()
+{
+  return DoNotifyObservers(mPrincipal, OBSERVER_TOPIC_SUBSCRIPTION_MODIFIED,
+                           mScope);
+}
+
+nsresult
+PushSubscriptionModifiedDispatcher::NotifyWorkers()
+{
+  return NS_OK;
+}
+
+bool
+PushSubscriptionModifiedDispatcher::SendToParent(ContentChild* aParentActor)
+{
+  return aParentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
+                                                                   IPC::Principal(mPrincipal));
+}
+
+bool
+PushSubscriptionModifiedDispatcher::SendToChild(ContentParent* aContentActor)
+{
+  return aContentActor->SendNotifyPushSubscriptionModifiedObservers(mScope,
+                                                                    IPC::Principal(mPrincipal));
+}
+
+PushErrorDispatcher::PushErrorDispatcher(const nsACString& aScope,
+                                         nsIPrincipal* aPrincipal,
+                                         const nsAString& aMessage,
+                                         uint32_t aFlags)
+  : PushDispatcher(aScope, aPrincipal)
+  , mMessage(aMessage)
+  , mFlags(aFlags)
+{}
+
+PushErrorDispatcher::~PushErrorDispatcher()
+{}
+
+nsresult
+PushErrorDispatcher::NotifyObservers()
+{
+  return NS_OK;
+}
+
+nsresult
+PushErrorDispatcher::NotifyWorkers()
+{
+  if (!ShouldNotifyWorkers()) {
+    // For system subscriptions, log the error directly to the browser console.
+    return nsContentUtils::ReportToConsoleNonLocalized(mMessage,
+                                                       mFlags,
+                                                       NS_LITERAL_CSTRING("Push"),
+                                                       nullptr, /* aDocument */
+                                                       nullptr, /* aURI */
+                                                       EmptyString(), /* aLine */
+                                                       0, /* aLineNumber */
+                                                       0, /* aColumnNumber */
+                                                       nsContentUtils::eOMIT_LOCATION);
+  }
+  // For service worker subscriptions, report the error to all clients.
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (swm) {
+    swm->ReportToAllClients(mScope,
+                            mMessage,
+                            NS_ConvertUTF8toUTF16(mScope), /* aFilename */
+                            EmptyString(), /* aLine */
+                            0, /* aLineNumber */
+                            0, /* aColumnNumber */
+                            mFlags);
+  }
+  return NS_OK;
+}
+
+bool
+PushErrorDispatcher::SendToParent(ContentChild*)
+{
+  return true;
+}
+
+bool
+PushErrorDispatcher::SendToChild(ContentParent* aContentActor)
+{
+  return aContentActor->SendPushError(mScope, IPC::Principal(mPrincipal),
+                                      mMessage, mFlags);
+}
+
+nsresult
+PushErrorDispatcher::HandleNoChildProcesses()
+{
+  // Report to the console if no content processes are active.
+  nsCOMPtr<nsIURI> scopeURI;
+  nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return nsContentUtils::ReportToConsoleNonLocalized(mMessage,
+                                                     mFlags,
+                                                     NS_LITERAL_CSTRING("Push"),
+                                                     nullptr, /* aDocument */
+                                                     scopeURI, /* aURI */
+                                                     EmptyString(), /* aLine */
+                                                     0, /* aLineNumber */
+                                                     0, /* aColumnNumber */
+                                                     nsContentUtils::eOMIT_LOCATION);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/push/PushNotifier.h
+++ b/dom/push/PushNotifier.h
@@ -6,19 +6,70 @@
 #define mozilla_dom_PushNotifier_h
 
 #include "nsIPushNotifier.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsIPrincipal.h"
 #include "nsString.h"
 
+#include "mozilla/Maybe.h"
+
 namespace mozilla {
 namespace dom {
 
+class ContentChild;
+class ContentParent;
+
+/**
+ * `PushDispatcher` is a base class used to forward observer notifications and
+ * service worker events to the correct process.
+ */
+class MOZ_STACK_CLASS PushDispatcher
+{
+public:
+  // Fires an XPCOM observer notification. This method may be called from both
+  // processes.
+  virtual nsresult NotifyObservers() = 0;
+
+  // Fires a service worker event. This method is called from the content
+  // process if e10s is enabled, or the parent otherwise.
+  virtual nsresult NotifyWorkers() = 0;
+
+  // A convenience method that calls `NotifyObservers` and `NotifyWorkers`.
+  nsresult NotifyObserversAndWorkers();
+
+  // Sends an IPDL message to fire an observer notification in the parent
+  // process. This method is only called from the content process, and only
+  // if e10s is enabled.
+  virtual bool SendToParent(ContentChild* aParentActor) = 0;
+
+  // Sends an IPDL message to fire an observer notification and a service worker
+  // event in the content process. This method is only called from the parent,
+  // and only if e10s is enabled.
+  virtual bool SendToChild(ContentParent* aContentActor) = 0;
+
+  // An optional method, called from the parent if e10s is enabled and there
+  // are no active content processes. The default behavior is a no-op.
+  virtual nsresult HandleNoChildProcesses();
+
+protected:
+  PushDispatcher(const nsACString& aScope,
+                 nsIPrincipal* aPrincipal);
+
+  virtual ~PushDispatcher();
+
+  bool ShouldNotifyWorkers();
+  nsresult DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
+                             const nsACString& aScope);
+
+  const nsCString mScope;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+};
+
 /**
  * `PushNotifier` implements the `nsIPushNotifier` interface. This service
  * broadcasts XPCOM observer notifications for incoming push messages, then
  * forwards incoming push messages to service workers.
  *
  * All scriptable methods on this interface may be called from the parent or
  * content process. Observer notifications are broadcasted to both processes.
  */
@@ -27,41 +78,36 @@ class PushNotifier final : public nsIPus
 public:
   PushNotifier();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PushNotifier, nsIPushNotifier)
   NS_DECL_NSIPUSHNOTIFIER
 
 private:
-  virtual ~PushNotifier();
+  ~PushNotifier();
 
-  nsresult NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal,
-                      const nsAString& aMessageId,
-                      const Maybe<nsTArray<uint8_t>>& aData);
-  nsresult DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
-                             const nsACString& aScope);
-  bool ShouldNotifyWorkers(nsIPrincipal* aPrincipal);
+  nsresult Dispatch(PushDispatcher& aDispatcher);
 };
 
 /**
  * `PushData` provides methods for retrieving push message data in different
  * formats. This class is similar to the `PushMessageData` WebIDL interface.
  */
 class PushData final : public nsIPushData
 {
 public:
   explicit PushData(const nsTArray<uint8_t>& aData);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PushData, nsIPushData)
   NS_DECL_NSIPUSHDATA
 
 private:
-  virtual ~PushData();
+  ~PushData();
 
   nsresult EnsureDecodedText();
 
   nsTArray<uint8_t> mData;
   nsString mDecodedText;
 };
 
 /**
@@ -74,18 +120,84 @@ class PushMessage final : public nsIPush
 public:
   PushMessage(nsIPrincipal* aPrincipal, nsIPushData* aData);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PushMessage, nsIPushMessage)
   NS_DECL_NSIPUSHMESSAGE
 
 private:
-  virtual ~PushMessage();
+  ~PushMessage();
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIPushData> mData;
 };
 
+class PushMessageDispatcher final : public PushDispatcher
+{
+public:
+  PushMessageDispatcher(const nsACString& aScope,
+               nsIPrincipal* aPrincipal,
+               const nsAString& aMessageId,
+               const Maybe<nsTArray<uint8_t>>& aData);
+  ~PushMessageDispatcher();
+
+  nsresult NotifyObservers() override;
+  nsresult NotifyWorkers() override;
+  bool SendToParent(ContentChild* aParentActor) override;
+  bool SendToChild(ContentParent* aContentActor) override;
+
+private:
+  const nsString mMessageId;
+  const Maybe<nsTArray<uint8_t>> mData;
+};
+
+class PushSubscriptionChangeDispatcher final : public PushDispatcher
+{
+public:
+  PushSubscriptionChangeDispatcher(const nsACString& aScope,
+                                 nsIPrincipal* aPrincipal);
+  ~PushSubscriptionChangeDispatcher();
+
+  nsresult NotifyObservers() override;
+  nsresult NotifyWorkers() override;
+  bool SendToParent(ContentChild* aParentActor) override;
+  bool SendToChild(ContentParent* aContentActor) override;
+};
+
+class PushSubscriptionModifiedDispatcher : public PushDispatcher
+{
+public:
+  PushSubscriptionModifiedDispatcher(const nsACString& aScope,
+                                     nsIPrincipal* aPrincipal);
+  ~PushSubscriptionModifiedDispatcher();
+
+  nsresult NotifyObservers() override;
+  nsresult NotifyWorkers() override;
+  bool SendToParent(ContentChild* aParentActor) override;
+  bool SendToChild(ContentParent* aContentActor) override;
+};
+
+class PushErrorDispatcher final : public PushDispatcher
+{
+public:
+  PushErrorDispatcher(const nsACString& aScope,
+                      nsIPrincipal* aPrincipal,
+                      const nsAString& aMessage,
+                      uint32_t aFlags);
+  ~PushErrorDispatcher();
+
+  nsresult NotifyObservers() override;
+  nsresult NotifyWorkers() override;
+  bool SendToParent(ContentChild* aParentActor) override;
+  bool SendToChild(ContentParent* aContentActor) override;
+
+private:
+  nsresult HandleNoChildProcesses() override;
+
+  const nsString mMessage;
+  uint32_t mFlags;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PushNotifier_h
deleted file mode 100644
--- a/dom/push/test/xpcshell/test_handler_service_parent.js
+++ /dev/null
@@ -1,50 +0,0 @@
-'use strict';
-
-function run_test() {
-  do_get_profile();
-  run_next_test();
-}
-
-add_task(function* test_observer_notifications() {
-  // Push observer notifications dispatched in the child should be forwarded to
-  // the parent.
-  let notifyPromise = promiseObserverNotification(
-    PushServiceComponent.pushTopic);
-  let subChangePromise = promiseObserverNotification(
-    PushServiceComponent.subscriptionChangeTopic);
-  let subModifiedPromise = promiseObserverNotification(
-    PushServiceComponent.subscriptionModifiedTopic);
-
-  yield run_test_in_child('./test_handler_service.js');
-
-  let principal = Services.scriptSecurityManager.getSystemPrincipal();
-
-  let {
-    data: notifyScope,
-    subject: notifySubject,
-  } = yield notifyPromise;
-  equal(notifyScope, 'chrome://test-scope',
-    'Should forward push notifications with the correct scope');
-  let message = notifySubject.QueryInterface(Ci.nsIPushMessage);
-  equal(message.principal, principal,
-    'Should include the principal in the push message');
-  strictEqual(message.data, null, 'Should not include data');
-
-  let {
-    data: subChangeScope,
-    subject: subChangePrincipal,
-  } = yield subChangePromise;
-  equal(subChangeScope, 'chrome://test-scope',
-    'Should forward subscription change notifications with the correct scope');
-  equal(subChangePrincipal, principal,
-    'Should pass the principal as the subject of a change notification');
-
-  let {
-    data: subModifiedScope,
-    subject: subModifiedPrincipal,
-  } = yield subModifiedPromise;
-  equal(subModifiedScope, 'chrome://test-scope',
-    'Should forward subscription modified notifications with the correct scope');
-  equal(subModifiedPrincipal, principal,
-    'Should pass the principal as the subject of a modified notification');
-});
new file mode 100644
--- /dev/null
+++ b/dom/push/test/xpcshell/test_observer_remoting.js
@@ -0,0 +1,90 @@
+'use strict';
+
+const pushNotifier = Cc['@mozilla.org/push/Notifier;1']
+                       .getService(Ci.nsIPushNotifier);
+
+add_task(function* test_observer_remoting() {
+  if (isParent) {
+    yield testInParent();
+  } else {
+    yield testInChild();
+  }
+});
+
+function* testInParent() {
+  // Register observers for notifications from the child, then run the test in
+  // the child and wait for the notifications.
+  let promiseNotifications = waitForNotifierObservers('Hello from child!');
+  let promiseFinished = run_test_in_child('./test_observer_remoting.js');
+  yield promiseNotifications;
+
+  // Wait until the child is listening for notifications from the parent.
+  yield do_await_remote_message('push_test_observer_remoting_child_ready');
+
+  // Fire an observer notification in the parent that should be forwarded to
+  // the child.
+  yield waitForNotifierObservers('Hello from parent!', true);
+
+  // Wait for the child to exit.
+  yield promiseFinished;
+}
+
+function* testInChild() {
+  // Fire an observer notification in the child that should be forwarded to
+  // the parent.
+  yield waitForNotifierObservers('Hello from child!', true);
+
+  // Register observers for notifications from the parent, let the parent know
+  // we're ready, and wait for the notifications.
+  let promiseNotifierObservers = waitForNotifierObservers('Hello from parent!');
+  do_send_remote_message('push_test_observer_remoting_child_ready');
+  yield promiseNotifierObservers;
+}
+
+function* waitForNotifierObservers(expectedText, shouldNotify = false) {
+  let notifyPromise = promiseObserverNotification(
+    PushServiceComponent.pushTopic);
+  let subChangePromise = promiseObserverNotification(
+    PushServiceComponent.subscriptionChangeTopic);
+  let subModifiedPromise = promiseObserverNotification(
+    PushServiceComponent.subscriptionModifiedTopic);
+
+  let scope = 'chrome://test-scope';
+  let principal = Services.scriptSecurityManager.getSystemPrincipal();
+  let data = new TextEncoder('utf-8').encode(expectedText);
+
+  if (shouldNotify) {
+    pushNotifier.notifyPushWithData(scope, principal, '', data.length, data);
+    pushNotifier.notifySubscriptionChange(scope, principal);
+    pushNotifier.notifySubscriptionModified(scope, principal);
+  }
+
+  let {
+    data: notifyScope,
+    subject: notifySubject,
+  } = yield notifyPromise;
+  equal(notifyScope, scope,
+    'Should fire push notifications with the correct scope');
+  let message = notifySubject.QueryInterface(Ci.nsIPushMessage);
+  equal(message.principal, principal,
+    'Should include the principal in the push message');
+  strictEqual(message.data.text(), expectedText, 'Should include data');
+
+  let {
+    data: subChangeScope,
+    subject: subChangePrincipal,
+  } = yield subChangePromise;
+  equal(subChangeScope, scope,
+    'Should fire subscription change notifications with the correct scope');
+  equal(subChangePrincipal, principal,
+    'Should pass the principal as the subject of a change notification');
+
+  let {
+    data: subModifiedScope,
+    subject: subModifiedPrincipal,
+  } = yield subModifiedPromise;
+  equal(subModifiedScope, scope,
+    'Should fire subscription modified notifications with the correct scope');
+  equal(subModifiedPrincipal, principal,
+    'Should pass the principal as the subject of a modified notification');
+}
--- a/dom/push/test/xpcshell/xpcshell.ini
+++ b/dom/push/test/xpcshell/xpcshell.ini
@@ -2,26 +2,26 @@
 head = head.js head-http2.js
 tail =
 # Push notifications and alarms are currently disabled on Android.
 skip-if = toolkit == 'android'
 
 [test_clear_origin_data.js]
 [test_crypto.js]
 [test_drop_expired.js]
-[test_handler_service_parent.js]
 [test_handler_service.js]
 support-files = PushServiceHandler.js PushServiceHandler.manifest
 [test_notification_ack.js]
 [test_notification_data.js]
 [test_notification_duplicate.js]
 [test_notification_error.js]
 [test_notification_incomplete.js]
 [test_notification_version_string.js]
 [test_observer_data.js]
+[test_observer_remoting.js]
 
 [test_permissions.js]
 run-sequentially = This will delete all existing push subscriptions.
 
 [test_quota_exceeded.js]
 [test_quota_observer.js]
 [test_quota_with_notification.js]
 [test_record.js]
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -587,16 +587,21 @@ nsCSPContext::LogViolationDetails(uint16
       CASE_CHECK_AND_REPORT(NONCE_SCRIPT,      SCRIPT,     aNonce,
                             CSP_UNSAFE_INLINE, SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC);
       CASE_CHECK_AND_REPORT(NONCE_STYLE,       STYLESHEET, aNonce,
                             CSP_UNSAFE_INLINE, STYLE_NONCE_VIOLATION_OBSERVER_TOPIC);
       CASE_CHECK_AND_REPORT(HASH_SCRIPT,       SCRIPT,     aContent,
                             CSP_UNSAFE_INLINE, SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC);
       CASE_CHECK_AND_REPORT(HASH_STYLE,        STYLESHEET, aContent,
                             CSP_UNSAFE_INLINE, STYLE_HASH_VIOLATION_OBSERVER_TOPIC);
+      CASE_CHECK_AND_REPORT(REQUIRE_SRI_FOR_STYLE,   STYLESHEET, NS_LITERAL_STRING(""),
+                            CSP_REQUIRE_SRI_FOR, REQUIRE_SRI_STYLE_VIOLATION_OBSERVER_TOPIC);
+      CASE_CHECK_AND_REPORT(REQUIRE_SRI_FOR_SCRIPT,   SCRIPT, NS_LITERAL_STRING(""),
+                            CSP_REQUIRE_SRI_FOR, REQUIRE_SRI_SCRIPT_VIOLATION_OBSERVER_TOPIC);
+
 
       default:
         NS_ASSERTION(false, "LogViolationDetails with invalid type");
         break;
     }
   }
   return NS_OK;
 }
@@ -1127,16 +1132,31 @@ nsCSPContext::AsyncReportViolation(nsISu
                                                       aObserverSubject,
                                                       aSourceFile,
                                                       aScriptSample,
                                                       aLineNum,
                                                       this));
    return NS_OK;
 }
 
+NS_IMETHODIMP
+nsCSPContext::RequireSRIForType(nsContentPolicyType aContentType, bool* outRequiresSRIForType)
+{
+  *outRequiresSRIForType = false;
+  for (uint32_t i = 0; i < mPolicies.Length(); i++) {
+    if (mPolicies[i]->hasDirective(REQUIRE_SRI_FOR)) {
+      if (mPolicies[i]->requireSRIForType(aContentType)) {
+        *outRequiresSRIForType = true;
+        return NS_OK;
+      }
+    }
+  }
+  return NS_OK;
+}
+
 /**
  * Based on the given docshell, determines if this CSP context allows the
  * ancestry.
  *
  * In order to determine the URI of the parent document (one causing the load
  * of this protected document), this function obtains the docShellTreeItem,
  * then walks up the hierarchy until it finds a privileged (chrome) tree item.
  * Getting the a tree item's URI looks like this in pseudocode:
--- a/dom/security/nsCSPParser.cpp
+++ b/dom/security/nsCSPParser.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 "mozilla/ArrayUtils.h"
 #include "nsCOMPtr.h"
 #include "nsCSPParser.h"
 #include "nsCSPUtils.h"
 #include "nsIConsoleService.h"
+#include "nsIContentPolicy.h"
 #include "nsIScriptError.h"
 #include "nsIStringBundle.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/net/ReferrerPolicy.h"
 
@@ -52,16 +53,19 @@ static const char16_t CLOSINGBRACE = ')'
 static const char16_t EQUALS       = '=';
 static const char16_t ATSYMBOL     = '@';
 
 static const uint32_t kSubHostPathCharacterCutoff = 512;
 
 static const char *const kHashSourceValidFns [] = { "sha256", "sha384", "sha512" };
 static const uint32_t kHashSourceValidFnsLen = 3;
 
+static const char* const kStyle    = "style";
+static const char* const kScript   = "script";
+
 /* ===== nsCSPTokenizer ==================== */
 
 nsCSPTokenizer::nsCSPTokenizer(const char16_t* aStart,
                                const char16_t* aEnd)
   : mCurChar(aStart)
   , mEndChar(aEnd)
 {
   CSPPARSERLOG(("nsCSPTokenizer::nsCSPTokenizer"));
@@ -908,16 +912,56 @@ nsCSPParser::referrerDirectiveValue()
     return;
   }
 
   // the referrer policy is valid, so go ahead and use it.
   mPolicy->setReferrerPolicy(&mCurDir[1]);
 }
 
 void
+nsCSPParser::requireSRIForDirectiveValue(nsRequireSRIForDirective* aDir) {
+  // directive-value = "style" / "script"
+  // directive name is token 0, we need to examine the remaining tokens
+  for (uint32_t i = 1; i < mCurDir.Length(); i++) {
+    // mCurToken is only set here and remains the current token
+    // to be processed, which avoid passing arguments between functions.
+    mCurToken = mCurDir[i];
+    resetCurValue();
+    CSPPARSERLOG(("nsCSPParser:::directive (require-sri-for directive), "
+                  "mCurToken: %s (valid), mCurValue: %s",
+                  NS_ConvertUTF16toUTF8(mCurToken).get(),
+                  NS_ConvertUTF16toUTF8(mCurValue).get()));
+    // add contentPolicyTypes to the CSP's required-SRI list for this token
+    if (mCurToken.LowerCaseEqualsASCII(kScript)) {
+      aDir->addType(nsIContentPolicy::TYPE_SCRIPT);
+    }
+    else if (mCurToken.LowerCaseEqualsASCII(kStyle)) {
+      aDir->addType(nsIContentPolicy::TYPE_STYLESHEET);
+    } else {
+      const char16_t* invalidTokenName[] = { mCurToken.get() };
+      logWarningErrorToConsole(nsIScriptError::warningFlag, "failedToParseUnrecognizedSource",
+                            invalidTokenName, ArrayLength(invalidTokenName));
+      CSPPARSERLOG(("nsCSPParser:::directive (require-sri-for directive), "
+                    "mCurToken: %s (invalid), mCurValue: %s",
+                    NS_ConvertUTF16toUTF8(mCurToken).get(),
+                    NS_ConvertUTF16toUTF8(mCurValue).get()));
+    }
+  }
+  if (!(aDir->hasType(nsIContentPolicy::TYPE_STYLESHEET)) &&
+      !(aDir->hasType(nsIContentPolicy::TYPE_SCRIPT))) {
+    const char16_t* directiveName[] = { mCurToken.get() };
+    logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringDirectiveWithNoValues",
+                               directiveName, ArrayLength(directiveName));
+    return;
+  } else {
+    mPolicy->addDirective(aDir);
+  }
+}
+
+void
 nsCSPParser::reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs)
 {
   nsCOMPtr<nsIURI> uri;
   nsresult rv;
 
   // remember, srcs start at index 1
   for (uint32_t i = 1; i < mCurDir.Length(); i++) {
     mCurToken = mCurDir[i];
@@ -958,16 +1002,23 @@ nsCSPParser::directiveValue(nsTArray<nsC
 
   // special case handling of the referrer directive (since it doesn't contain
   // source lists)
   if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
     referrerDirectiveValue();
     return;
   }
 
+  // special case handling of the require-sri-for directive (since it doesn't
+  // contain a source lists but rather types, e.g., style or script)
+  if (CSP_IsDirective(mCurDir[0], nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
+    // handled in directive()
+    return;
+  }
+
   // Otherwise just forward to sourceList
   sourceList(outSrcs);
 }
 
 // directive-name = 1*( ALPHA / DIGIT / "-" )
 nsCSPDirective*
 nsCSPParser::directiveName()
 {
@@ -1037,16 +1088,20 @@ nsCSPParser::directiveName()
   if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE)) {
     const char16_t* params[] = { mCurToken.get(), NS_LITERAL_STRING("child-src").get() };
     logWarningErrorToConsole(nsIScriptError::warningFlag, "deprecatedDirective",
                              params, ArrayLength(params));
     mFrameSrc = new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
     return mFrameSrc;
   }
 
+  if (CSP_IsDirective(mCurToken, nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
+    return new nsRequireSRIForDirective(CSP_StringToCSPDirective(mCurToken));
+  }
+
   return new nsCSPDirective(CSP_StringToCSPDirective(mCurToken));
 }
 
 // directive = *WSP [ directive-name [ WSP directive-value ] ]
 void
 nsCSPParser::directive()
 {
   // Set the directiveName to mCurToken
@@ -1096,16 +1151,23 @@ nsCSPParser::directive()
                                "ignoreSrcForDirective",
                                params, ArrayLength(params));
     }
     // add the directive and return
     mPolicy->addUpgradeInsecDir(static_cast<nsUpgradeInsecureDirective*>(cspDir));
     return;
   }
 
+  // special case handling for require-sri-for, which has directive values that
+  // are well-defined tokens but are not sources
+  if (cspDir->equals(nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
+    requireSRIForDirectiveValue(static_cast<nsRequireSRIForDirective*>(cspDir));
+    return;
+  }
+
   // make sure to reset cache variables when trying to invalidate unsafe-inline;
   // unsafe-inline might not only appear in script-src, but also in default-src
   mHasHashOrNonce = false;
   mUnsafeInlineKeywordSrc = nullptr;
 
   // Try to parse all the srcs by handing the array off to directiveValue
   nsTArray<nsCSPBaseSrc*> srcs;
   directiveValue(srcs);
--- a/dom/security/nsCSPParser.h
+++ b/dom/security/nsCSPParser.h
@@ -110,34 +110,35 @@ class nsCSPParser {
                 nsIURI* aSelfURI,
                 nsCSPContext* aCSPContext,
                 bool aDeliveredViaMetaTag);
 
     ~nsCSPParser();
 
 
     // Parsing the CSP using the source-list from http://www.w3.org/TR/CSP11/#source-list
-    nsCSPPolicy*    policy();
-    void            directive();
-    nsCSPDirective* directiveName();
-    void            directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs);
-    void            referrerDirectiveValue();
-    void            sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs);
-    nsCSPBaseSrc*   sourceExpression();
-    nsCSPSchemeSrc* schemeSource();
-    nsCSPHostSrc*   hostSource();
-    nsCSPBaseSrc*   keywordSource();
-    nsCSPNonceSrc*  nonceSource();
-    nsCSPHashSrc*   hashSource();
-    nsCSPHostSrc*   appHost(); // helper function to support app specific hosts
-    nsCSPHostSrc*   host();
-    bool            hostChar();
-    bool            schemeChar();
-    bool            port();
-    bool            path(nsCSPHostSrc* aCspHost);
+    nsCSPPolicy*        policy();
+    void                directive();
+    nsCSPDirective*     directiveName();
+    void                directiveValue(nsTArray<nsCSPBaseSrc*>& outSrcs);
+    void                requireSRIForDirectiveValue(nsRequireSRIForDirective* aDir);
+    void                referrerDirectiveValue();
+    void                sourceList(nsTArray<nsCSPBaseSrc*>& outSrcs);
+    nsCSPBaseSrc*       sourceExpression();
+    nsCSPSchemeSrc*     schemeSource();
+    nsCSPHostSrc*       hostSource();
+    nsCSPBaseSrc*       keywordSource();
+    nsCSPNonceSrc*      nonceSource();
+    nsCSPHashSrc*       hashSource();
+    nsCSPHostSrc*       appHost(); // helper function to support app specific hosts
+    nsCSPHostSrc*       host();
+    bool                hostChar();
+    bool                schemeChar();
+    bool                port();
+    bool                path(nsCSPHostSrc* aCspHost);
 
     bool subHost();                                       // helper function to parse subDomains
     bool atValidUnreservedChar();                         // helper function to parse unreserved
     bool atValidSubDelimChar();                           // helper function to parse sub-delims
     bool atValidPctEncodedChar();                         // helper function to parse pct-encoded
     bool subPath(nsCSPHostSrc* aCspHost);                 // helper function to parse paths
     void reportURIList(nsTArray<nsCSPBaseSrc*>& outSrcs); // helper function to parse report-uris
     void percentDecodeStr(const nsAString& aEncStr,       // helper function to percent-decode
--- a/dom/security/nsCSPUtils.cpp
+++ b/dom/security/nsCSPUtils.cpp
@@ -948,17 +948,17 @@ nsCSPDirective::toDomCSPStruct(mozilla::
       // does not have any srcs
       return;
 
     case nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE:
       outCSP.mChild_src.Construct();
       outCSP.mChild_src.Value() = mozilla::Move(srcs);
       return;
 
-    // REFERRER_DIRECTIVE is handled in nsCSPPolicy::toDomCSPStruct()
+    // REFERRER_DIRECTIVE and REQUIRE_SRI_FOR are handled in nsCSPPolicy::toDomCSPStruct()
 
     default:
       NS_ASSERTION(false, "cannot find directive to convert CSP to JSON");
   }
 }
 
 
 bool
@@ -1070,16 +1070,66 @@ nsUpgradeInsecureDirective::~nsUpgradeIn
 
 void
 nsUpgradeInsecureDirective::toString(nsAString& outStr) const
 {
   outStr.AppendASCII(CSP_CSPDirectiveToString(
     nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
 }
 
+/* ===== nsRequireSRIForDirective ========================= */
+
+nsRequireSRIForDirective::nsRequireSRIForDirective(CSPDirective aDirective)
+: nsCSPDirective(aDirective)
+{
+}
+
+nsRequireSRIForDirective::~nsRequireSRIForDirective()
+{
+}
+
+void
+nsRequireSRIForDirective::toString(nsAString &outStr) const
+{
+  outStr.AppendASCII(CSP_CSPDirectiveToString(
+    nsIContentSecurityPolicy::REQUIRE_SRI_FOR));
+  for (uint32_t i = 0; i < mTypes.Length(); i++) {
+    if (mTypes[i] == nsIContentPolicy::TYPE_SCRIPT) {
+      outStr.AppendASCII(" script");
+    }
+    else if (mTypes[i] == nsIContentPolicy::TYPE_STYLESHEET) {
+      outStr.AppendASCII(" style");
+    }
+  }
+}
+
+bool
+nsRequireSRIForDirective::hasType(nsContentPolicyType aType) const
+{
+  for (uint32_t i = 0; i < mTypes.Length(); i++) {
+    if (mTypes[i] == aType) {
+      return true;
+    }
+  }
+  return false;
+}
+
+bool
+nsRequireSRIForDirective::restrictsContentType(const nsContentPolicyType aType) const
+{
+  return this->hasType(aType);
+}
+
+bool
+nsRequireSRIForDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const
+{
+  // can only disallow CSP_REQUIRE_SRI_FOR.
+  return (aKeyword != CSP_REQUIRE_SRI_FOR);
+}
+
 /* ===== nsCSPPolicy ========================= */
 
 nsCSPPolicy::nsCSPPolicy()
   : mUpgradeInsecDir(nullptr)
   , mReportOnly(false)
 {
   CSPUTILSLOG(("nsCSPPolicy::nsCSPPolicy"));
 }
@@ -1309,8 +1359,19 @@ nsCSPPolicy::visitDirectiveSrcs(CSPDirec
 {
   for (uint32_t i = 0; i < mDirectives.Length(); i++) {
     if (mDirectives[i]->equals(aDir)) {
       return mDirectives[i]->visitSrcs(aVisitor);
     }
   }
   return false;
 }
+
+bool
+nsCSPPolicy::requireSRIForType(nsContentPolicyType aContentType)
+{
+  for (uint32_t i = 0; i < mDirectives.Length(); i++) {
+    if (mDirectives[i]->equals(nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
+      return static_cast<nsRequireSRIForDirective*>(mDirectives[i])->hasType(aContentType);
+    }
+  }
+  return false;
+}
--- a/dom/security/nsCSPUtils.h
+++ b/dom/security/nsCSPUtils.h
@@ -49,23 +49,25 @@ void CSP_LogMessage(const nsAString& aMe
                     uint32_t aColumnNumber,
                     uint32_t aFlags,
                     const char* aCategory,
                     uint32_t aInnerWindowID);
 
 
 /* =============== Constant and Type Definitions ================== */
 
-#define INLINE_STYLE_VIOLATION_OBSERVER_TOPIC   "violated base restriction: Inline Stylesheets will not apply"
-#define INLINE_SCRIPT_VIOLATION_OBSERVER_TOPIC  "violated base restriction: Inline Scripts will not execute"
-#define EVAL_VIOLATION_OBSERVER_TOPIC           "violated base restriction: Code will not be created from strings"
-#define SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC   "Inline Script had invalid nonce"
-#define STYLE_NONCE_VIOLATION_OBSERVER_TOPIC    "Inline Style had invalid nonce"
-#define SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC    "Inline Script had invalid hash"
-#define STYLE_HASH_VIOLATION_OBSERVER_TOPIC     "Inline Style had invalid hash"
+#define INLINE_STYLE_VIOLATION_OBSERVER_TOPIC        "violated base restriction: Inline Stylesheets will not apply"
+#define INLINE_SCRIPT_VIOLATION_OBSERVER_TOPIC       "violated base restriction: Inline Scripts will not execute"
+#define EVAL_VIOLATION_OBSERVER_TOPIC                "violated base restriction: Code will not be created from strings"
+#define SCRIPT_NONCE_VIOLATION_OBSERVER_TOPIC        "Inline Script had invalid nonce"
+#define STYLE_NONCE_VIOLATION_OBSERVER_TOPIC         "Inline Style had invalid nonce"
+#define SCRIPT_HASH_VIOLATION_OBSERVER_TOPIC         "Inline Script had invalid hash"
+#define STYLE_HASH_VIOLATION_OBSERVER_TOPIC          "Inline Style had invalid hash"
+#define REQUIRE_SRI_SCRIPT_VIOLATION_OBSERVER_TOPIC  "Missing required Subresource Integrity for Script"
+#define REQUIRE_SRI_STYLE_VIOLATION_OBSERVER_TOPIC   "Missing required Subresource Integrity for Style"
 
 // these strings map to the CSPDirectives in nsIContentSecurityPolicy
 // NOTE: When implementing a new directive, you will need to add it here but also
 // add a corresponding entry to the constants in nsIContentSecurityPolicy.idl
 // and also create an entry for the new directive in
 // nsCSPDirective::toDomCSPStruct() and add it to CSPDictionaries.webidl.
 // Order of elements below important! Make sure it matches the order as in
 // nsIContentSecurityPolicy.idl
@@ -84,17 +86,19 @@ static const char* CSPStrDirectives[] = 
   "frame-ancestors",           // FRAME_ANCESTORS_DIRECTIVE
   "reflected-xss",             // REFLECTED_XSS_DIRECTIVE
   "base-uri",                  // BASE_URI_DIRECTIVE
   "form-action",               // FORM_ACTION_DIRECTIVE
   "referrer",                  // REFERRER_DIRECTIVE
   "manifest-src",              // MANIFEST_SRC_DIRECTIVE
   "upgrade-insecure-requests", // UPGRADE_IF_INSECURE_DIRECTIVE
   "child-src",                 // CHILD_SRC_DIRECTIVE
-  "block-all-mixed-content"    // BLOCK_ALL_MIXED_CONTENT
+  "block-all-mixed-content",   // BLOCK_ALL_MIXED_CONTENT
+  "require-sri-for"            // REQUIRE_SRI_FOR
+
 };
 
 inline const char* CSP_CSPDirectiveToString(CSPDirective aDir)
 {
   return CSPStrDirectives[static_cast<uint32_t>(aDir)];
 }
 
 inline CSPDirective CSP_StringToCSPDirective(const nsAString& aDir)
@@ -116,31 +120,33 @@ inline CSPDirective CSP_StringToCSPDirec
 // a string version for every enum >> using the same index << to
 // CSPStrKeywords underneath.
 enum CSPKeyword {
   CSP_SELF = 0,
   CSP_UNSAFE_INLINE,
   CSP_UNSAFE_EVAL,
   CSP_NONE,
   CSP_NONCE,
+  CSP_REQUIRE_SRI_FOR,
   // CSP_LAST_KEYWORD_VALUE always needs to be the last element in the enum
   // because we use it to calculate the size for the char* array.
   CSP_LAST_KEYWORD_VALUE,
   // Putting CSP_HASH after the delimitor, because CSP_HASH is not a valid
   // keyword (hash uses e.g. sha256, sha512) but we use CSP_HASH internally
   // to identify allowed hashes in ::allows.
   CSP_HASH
  };
 
 static const char* CSPStrKeywords[] = {
   "'self'",          // CSP_SELF = 0
   "'unsafe-inline'", // CSP_UNSAFE_INLINE
   "'unsafe-eval'",   // CSP_UNSAFE_EVAL
   "'none'",          // CSP_NONE
   "'nonce-",         // CSP_NONCE
+  "require-sri-for"  // CSP_REQUIRE_SRI_FOR
   // Remember: CSP_HASH is not supposed to be used
 };
 
 inline const char* CSP_EnumToKeyword(enum CSPKeyword aKey)
 {
   // Make sure all elements in enum CSPKeyword got added to CSPStrKeywords.
   static_assert((sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0]) ==
                 static_cast<uint32_t>(CSP_LAST_KEYWORD_VALUE)),
@@ -474,16 +480,35 @@ class nsUpgradeInsecureDirective : publi
       { return false; }
 
     void toString(nsAString& outStr) const;
 
     void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs)
       {  MOZ_ASSERT(false, "upgrade-insecure-requests does not hold any srcs"); }
 };
 
+/* ===== nsRequireSRIForDirective ========================= */
+
+class nsRequireSRIForDirective : public nsCSPDirective {
+  public:
+    explicit nsRequireSRIForDirective(CSPDirective aDirective);
+    ~nsRequireSRIForDirective();
+
+    void toString(nsAString& outStr) const;
+
+    void addType(nsContentPolicyType aType)
+      { mTypes.AppendElement(aType); }
+    bool hasType(nsContentPolicyType aType) const;
+    bool restrictsContentType(nsContentPolicyType aType) const;
+    bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
+
+  private:
+    nsTArray<nsContentPolicyType> mTypes;
+};
+
 /* =============== nsCSPPolicy ================== */
 
 class nsCSPPolicy {
   public:
     nsCSPPolicy();
     virtual ~nsCSPPolicy();
 
     bool permits(CSPDirective aDirective,
@@ -528,16 +553,18 @@ class nsCSPPolicy {
 
     void getReportURIs(nsTArray<nsString> &outReportURIs) const;
 
     void getDirectiveStringForContentType(nsContentPolicyType aContentType,
                                           nsAString& outDirective) const;
 
     void getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const;
 
+    bool requireSRIForType(nsContentPolicyType aContentType);
+
     inline uint32_t getNumDirectives() const
       { return mDirectives.Length(); }
 
     bool visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const;
 
   private:
     nsUpgradeInsecureDirective* mUpgradeInsecDir;
     nsTArray<nsCSPDirective*>   mDirectives;
--- a/dom/security/test/TestCSPParser.cpp
+++ b/dom/security/test/TestCSPParser.cpp
@@ -199,17 +199,19 @@ nsresult TestDirectives() {
       "connect-src http://www.example.com" },
     { "report-uri http://www.example.com",
       "report-uri http://www.example.com/" },
     { "script-src 'nonce-correctscriptnonce'",
       "script-src 'nonce-correctscriptnonce'" },
     { "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
       "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
     { "referrer no-referrer",
-      "referrer no-referrer" }
+      "referrer no-referrer" },
+    { "require-sri-for script style",
+      "require-sri-for script style"}
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestKeywords ========================
 
@@ -265,17 +267,19 @@ nsresult TestIgnoreUpperLowerCasePolicie
       "script-src 'nonce-correctscriptnonce'" },
     { "script-src 'NoncE-NONCENEEDSTOBEUPPERCASE'",
       "script-src 'nonce-NONCENEEDSTOBEUPPERCASE'" },
     { "script-src 'SHA256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
       "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
     { "refERRer No-refeRRer",
       "referrer No-refeRRer" },
     { "upgrade-INSECURE-requests",
-      "upgrade-insecure-requests" }
+      "upgrade-insecure-requests" },
+    { "require-SRI-for sCript stYle",
+        "require-sri-for script style"}
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestPaths ========================
 
@@ -502,19 +506,24 @@ nsresult TestPoliciesWithInvalidSrc() {
     { "script-src http://www.example.com:*.js",
       "script-src 'none'" },
     { "script-src http://www.example.com:*.",
       "script-src 'none'" },
     { "connect-src http://www.example.com/foo%zz;",
       "connect-src 'none'" },
     { "script-src https://foo.com/%$",
       "script-src 'none'" },
+    { "require-SRI-for script elephants",
+      "require-sri-for script"},
+    { "require-sri-for paul",
+      ""}
   };
 
-  uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
+  // amount of tests - 1, because the latest should be ignored.
+  uint32_t policyCount = (sizeof(policies) / sizeof(PolicyTest)) -1;
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestBadPolicies ========================
 
 nsresult TestBadPolicies() {
 
   static const PolicyTest policies[] =
new file mode 100644
--- /dev/null
+++ b/dom/security/test/sri/iframe_require-sri-for_main.html
@@ -0,0 +1,31 @@
+<script>
+  window.hasCORSLoaded = false; // set through script_crossdomain1.js
+</script>
+
+<!-- cors-enabled. should be loaded -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain1.js"
+        crossorigin=""
+        integrity="sha512-9Tv2DL1fHvmPQa1RviwKleE/jq72jgxj8XGLyWn3H6Xp/qbtfK/jZINoPFAv2mf0Nn1TxhZYMFULAbzJNGkl4Q=="
+        onload="parent.postMessage('good_sriLoaded', '*');"></script>
+
+<!-- cors but not using SRI. should trigger onerror -->
+<script src="http://example.com/tests/dom/security/test/sri/script_crossdomain5.js"
+          onload="parent.postMessage('bad_nonsriLoaded', '*');"
+          onerror="parent.postMessage('good_nonsriBlocked', '*');"></script>
+
+<!-- cors and integrity. it should just load fine. -->
+<link rel="stylesheet" href="style1.css"
+      integrity="sha256-qs8lnkunWoVldk5d5E+652yth4VTSHohlBKQvvgGwa8="
+      onload="parent.postMessage('good_sriLoaded', '*');">
+
+<!-- not using SRI, should trigger onerror -->
+<link rel="stylesheet" href="style3.css"
+      onload="parent.postMessage('bad_nonsriLoaded', '*');"
+      onerror="parent.postMessage('good_nonsriBlocked', '*');">
+
+<p id="black-text">black text</p>
+<script>
+  window.onload = function() {
+    parent.postMessage("finish", '*');
+  }
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/sri/iframe_require-sri-for_main.html^headers^
@@ -0,0 +1,1 @@
+content-security-policy: require-sri-for script style
--- a/dom/security/test/sri/mochitest.ini
+++ b/dom/security/test/sri/mochitest.ini
@@ -1,10 +1,12 @@
 [DEFAULT]
 support-files =
+  iframe_require-sri-for_main.html
+  iframe_require-sri-for_main.html^headers^
   iframe_script_crossdomain.html
   iframe_script_sameorigin.html
   iframe_sri_disabled.html
   iframe_style_crossdomain.html
   iframe_style_sameorigin.html
   script_crossdomain1.js
   script_crossdomain1.js^headers^
   script_crossdomain2.js
@@ -29,8 +31,9 @@ support-files =
   style_301.css
   style_301.css^headers^
 
 [test_script_sameorigin.html]
 [test_script_crossdomain.html]
 [test_sri_disabled.html]
 [test_style_crossdomain.html]
 [test_style_sameorigin.html]
+[test_require-sri-for_csp_directive.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/sri/test_require-sri-for_csp_directive.html
@@ -0,0 +1,44 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for SRI require-sri-for CSP directive</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1265318">Mozilla Bug 1265318</a>
+<iframe style="width:200px;height:200px;" id="test_frame"></iframe>
+</body>
+<script type="application/javascript">
+  SimpleTest.waitForExplicitFinish();
+  function handler(event) {
+    switch (event.data) {
+      case 'good_sriLoaded':
+        ok(true, "Eligible SRI resources was correctly loaded.");
+        break;
+      case 'bad_nonsriLoaded':
+        ok(false, "Eligible non-SRI resource should be blocked by the CSP!");
+        break;
+      case 'good_nonsriBlocked':
+        ok(true, "Eligible non-SRI resources was correctly blocked by the CSP.");
+        break;
+      case 'finish':
+        var blackText = frame.contentDocument.getElementById('black-text');
+        var blackTextColor = frame.contentWindow.getComputedStyle(blackText, null).getPropertyValue('color');
+        ok(blackTextColor == 'rgb(0, 0, 0)', "The second part should still be black.");
+        removeEventListener('message', handler);
+        SimpleTest.finish();
+        break;
+      default:
+        break;
+    }
+  }
+  addEventListener("message", handler);
+  var frame = document.getElementById("test_frame");
+  frame.src = "iframe_require-sri-for_main.html";
+</script>
+</html>
--- a/dom/svg/nsSVGViewBox.cpp
+++ b/dom/svg/nsSVGViewBox.cpp
@@ -62,16 +62,21 @@ nsSVGAttrTearoffTable<nsSVGViewBox, dom:
 
 
 /* Implementation of nsSVGViewBox methods */
 
 void
 nsSVGViewBox::Init()
 {
   mHasBaseVal = false;
+  // We shouldn't use mBaseVal for rendering (its usages should be guarded with
+  // "mHasBaseVal" checks), but just in case we do by accident, this will
+  // ensure that we treat it as "none" and ignore its numeric values:
+  mBaseVal.none = true;
+
   mAnimVal = nullptr;
 }
 
 bool
 nsSVGViewBox::HasRect() const
 {
   // Check mAnimVal if we have one; otherwise, check mBaseVal if we have one;
   // otherwise, just return false (we clearly do not have a rect).
--- a/dom/tests/mochitest/localstorage/chrome.ini
+++ b/dom/tests/mochitest/localstorage/chrome.ini
@@ -1,13 +1,15 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   frame_clear_browser_data.html
   page_blank.html
+  frameQuota.html
+  interOriginFrame.js
 
 [test_app_uninstall.html]
 skip-if = buildapp != 'mulet'
 [test_clear_browser_data.html]
 skip-if = buildapp != 'mulet'
 [test_localStorageBasePrivateBrowsing_perwindowpb.html]
 skip-if = true # bug 1156725
 [test_localStorageFromChrome.xhtml]
--- a/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
+++ b/dom/tests/mochitest/localstorage/test_localStorageQuotaPrivateBrowsing_perwindowpb.html
@@ -7,17 +7,17 @@
 
 <script type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 const Ci = Components.interfaces;
 const CONTENT_PAGE = "http://mochi.test:8888/chrome/dom/tests/mochitest/localstorage/page_blank.html";
-const slavePath = "/tests/dom/tests/mochitest/localstorage/";
+const slavePath = "/chrome/dom/tests/mochitest/localstorage/";
 var currentTest = 1;
 var quota;
 
 try {
   quota = Services.prefs.getIntPref("dom.storage.default_quota");
 } catch (ex) {
   quota = 5 * 1024;
 }
new file mode 100644
--- /dev/null
+++ b/dom/u2f/NSSU2FTokenRemote.cpp
@@ -0,0 +1,150 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "mozilla/dom/ContentChild.h"
+
+#include "NSSU2FTokenRemote.h"
+
+using mozilla::dom::ContentChild;
+
+NS_IMPL_ISUPPORTS(NSSU2FTokenRemote, nsIU2FToken)
+
+static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
+
+NSSU2FTokenRemote::NSSU2FTokenRemote()
+{}
+
+NSSU2FTokenRemote::~NSSU2FTokenRemote()
+{}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::IsCompatibleVersion(const nsAString& aVersionString,
+                                       bool* aIsCompatible)
+{
+  NS_ENSURE_ARG_POINTER(aIsCompatible);
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenIsCompatibleVersion(
+        nsString(aVersionString), aIsCompatible)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::IsRegistered(uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
+                                bool* aIsRegistered)
+{
+  NS_ENSURE_ARG_POINTER(aKeyHandle);
+  NS_ENSURE_ARG_POINTER(aIsRegistered);
+
+  nsTArray<uint8_t> keyHandle;
+  if (!keyHandle.ReplaceElementsAt(0, keyHandle.Length(), aKeyHandle,
+                                   aKeyHandleLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenIsRegistered(keyHandle, aIsRegistered)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::Register(uint8_t* aApplication,
+                            uint32_t aApplicationLen,
+                            uint8_t* aChallenge,
+                            uint32_t aChallengeLen,
+                            uint8_t** aRegistration,
+                            uint32_t* aRegistrationLen)
+{
+  NS_ENSURE_ARG_POINTER(aApplication);
+  NS_ENSURE_ARG_POINTER(aChallenge);
+  NS_ENSURE_ARG_POINTER(aRegistration);
+  NS_ENSURE_ARG_POINTER(aRegistrationLen);
+
+  nsTArray<uint8_t> application;
+  if (!application.ReplaceElementsAt(0, application.Length(), aApplication,
+                                     aApplicationLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  nsTArray<uint8_t> challenge;
+  if (!challenge.ReplaceElementsAt(0, challenge.Length(), aChallenge,
+                                   aChallengeLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsTArray<uint8_t> registrationBuffer;
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenRegister(application, challenge,
+                                   &registrationBuffer)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  size_t dataLen = registrationBuffer.Length();
+  uint8_t* tmp = reinterpret_cast<uint8_t*>(moz_xmalloc(dataLen));
+  if (NS_WARN_IF(!tmp)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  memcpy(tmp, registrationBuffer.Elements(), dataLen);
+  *aRegistration = tmp;
+  *aRegistrationLen = dataLen;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+NSSU2FTokenRemote::Sign(uint8_t* aApplication, uint32_t aApplicationLen,
+                        uint8_t* aChallenge, uint32_t aChallengeLen,
+                        uint8_t* aKeyHandle, uint32_t aKeyHandleLen,
+                        uint8_t** aSignature, uint32_t* aSignatureLen)
+{
+  NS_ENSURE_ARG_POINTER(aApplication);
+  NS_ENSURE_ARG_POINTER(aChallenge);
+  NS_ENSURE_ARG_POINTER(aKeyHandle);
+  NS_ENSURE_ARG_POINTER(aSignature);
+  NS_ENSURE_ARG_POINTER(aSignatureLen);
+
+  nsTArray<uint8_t> application;
+  if (!application.ReplaceElementsAt(0, application.Length(), aApplication,
+                                     aApplicationLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsTArray<uint8_t> challenge;
+  if (!challenge.ReplaceElementsAt(0, challenge.Length(), aChallenge,
+                                   aChallengeLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+  nsTArray<uint8_t> keyHandle;
+  if (!keyHandle.ReplaceElementsAt(0, keyHandle.Length(), aKeyHandle,
+                                   aKeyHandleLen)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  nsTArray<uint8_t> signatureBuffer;
+  ContentChild* cc = ContentChild::GetSingleton();
+  MOZ_ASSERT(cc);
+  if (!cc->SendNSSU2FTokenSign(application, challenge, keyHandle,
+                               &signatureBuffer)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  size_t dataLen = signatureBuffer.Length();
+  uint8_t* tmp = reinterpret_cast<uint8_t*>(moz_xmalloc(dataLen));
+  if (NS_WARN_IF(!tmp)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  memcpy(tmp, signatureBuffer.Elements(), dataLen);
+  *aSignature = tmp;
+  *aSignatureLen = dataLen;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/u2f/NSSU2FTokenRemote.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 NSSU2FTokenRemote_h
+#define NSSU2FTokenRemote_h
+
+#include "nsIU2FToken.h"
+
+class NSSU2FTokenRemote : public nsIU2FToken
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIU2FTOKEN
+
+  NSSU2FTokenRemote();
+
+private:
+  virtual ~NSSU2FTokenRemote();
+};
+
+#endif // NSSU2FTokenRemote_h
--- a/dom/u2f/U2F.cpp
+++ b/dom/u2f/U2F.cpp
@@ -2,20 +2,22 @@
 /* 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 "hasht.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CryptoBuffer.h"
+#include "mozilla/dom/NSSU2FTokenRemote.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/U2FBinding.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
+#include "nsINSSU2FToken.h"
 #include "nsNetCID.h"
 #include "nsNSSComponent.h"
 #include "nsURLParsers.h"
 #include "pk11pub.h"
 
 using mozilla::dom::ContentChild;
 
 namespace mozilla {
@@ -32,17 +34,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(U2F)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(U2F)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(U2F, mParent)
 
-static mozilla::LazyLogModule gU2FLog("webauth_u2f");
+static mozilla::LazyLogModule gWebauthLog("webauth_u2f");
 
 template <class CB, class Rsp>
 void
 SendError(CB* aCallback, ErrorCode aErrorCode)
 {
   Rsp response;
   response.mErrorCode.Construct(static_cast<uint32_t>(aErrorCode));
 
@@ -69,143 +71,35 @@ AssembleClientData(const nsAString& aOri
 
   if (NS_WARN_IF(!aClientData.Assign(NS_ConvertUTF16toUTF8(json)))) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-static nsresult
-NSSTokenIsCompatible(nsINSSU2FToken* aNSSToken, const nsString& aVersionString,
-                     bool* aIsCompatible)
-{
-  MOZ_ASSERT(aIsCompatible);
-
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    return aNSSToken->IsCompatibleVersion(aVersionString, aIsCompatible);
-  }
-
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenIsCompatibleVersion(aVersionString, aIsCompatible)) {
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-static nsresult
-NSSTokenIsRegistered(nsINSSU2FToken* aNSSToken, CryptoBuffer& aKeyHandle,
-                     bool* aIsRegistered)
-{
-  MOZ_ASSERT(aIsRegistered);
-
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    return aNSSToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(),
-                                   aIsRegistered);
-  }
-
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenIsRegistered(aKeyHandle, aIsRegistered)) {
-    return NS_ERROR_FAILURE;
-  }
-  return NS_OK;
-}
-
-static nsresult
-NSSTokenSign(nsINSSU2FToken* aNSSToken, CryptoBuffer& aKeyHandle,
-             CryptoBuffer& aApplication, CryptoBuffer& aChallenge,
-             CryptoBuffer& aSignatureData)
-{
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    uint8_t* buffer;
-    uint32_t bufferlen;
-    nsresult rv = aNSSToken->Sign(aApplication.Elements(), aApplication.Length(),
-                                  aChallenge.Elements(), aChallenge.Length(),
-                                  aKeyHandle.Elements(), aKeyHandle.Length(),
-                                  &buffer, &bufferlen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    MOZ_ASSERT(buffer);
-    aSignatureData.Assign(buffer, bufferlen);
-    free(buffer);
-    return NS_OK;
-  }
-
-  nsTArray<uint8_t> signatureBuffer;
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenSign(aApplication, aChallenge, aKeyHandle,
-                               &signatureBuffer)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aSignatureData.Assign(signatureBuffer);
-  return NS_OK;
-}
-
-static nsresult
-NSSTokenRegister(nsINSSU2FToken* aNSSToken, CryptoBuffer& aApplication,
-                 CryptoBuffer& aChallenge, CryptoBuffer& aRegistrationData)
-{
-  if (XRE_IsParentProcess()) {
-    MOZ_ASSERT(aNSSToken);
-    uint8_t* buffer;
-    uint32_t bufferlen;
-    nsresult rv;
-    rv = aNSSToken->Register(aApplication.Elements(), aApplication.Length(),
-                             aChallenge.Elements(), aChallenge.Length(),
-                             &buffer, &bufferlen);
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
-    }
-
-    MOZ_ASSERT(buffer);
-    aRegistrationData.Assign(buffer, bufferlen);
-    free(buffer);
-    return NS_OK;
-  }
-
-  nsTArray<uint8_t> registrationBuffer;
-  ContentChild* cc = ContentChild::GetSingleton();
-  MOZ_ASSERT(cc);
-  if (!cc->SendNSSU2FTokenRegister(aApplication, aChallenge,
-                                   &registrationBuffer)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aRegistrationData.Assign(registrationBuffer);
-  return NS_OK;
-}
-
 U2FTask::U2FTask(const nsAString& aOrigin, const nsAString& aAppId)
   : mOrigin(aOrigin)
   , mAppId(aAppId)
 {}
 
 U2FTask::~U2FTask()
 {}
 
 U2FRegisterTask::U2FRegisterTask(const nsAString& aOrigin,
                                  const nsAString& aAppId,
                                  const Sequence<RegisterRequest>& aRegisterRequests,
                                  const Sequence<RegisteredKey>& aRegisteredKeys,
                                  U2FRegisterCallback* aCallback,
-                                 const nsCOMPtr<nsINSSU2FToken>& aNSSToken)
+                                 const Sequence<Authenticator>& aAuthenticators)
   : U2FTask(aOrigin, aAppId)
   , mRegisterRequests(aRegisterRequests)
   , mRegisteredKeys(aRegisteredKeys)
   , mCallback(aCallback)
-  , mNSSToken(aNSSToken)
+  , mAuthenticators(aAuthenticators)
 {}
 
 U2FRegisterTask::~U2FRegisterTask()
 {
   nsNSSShutDownPreventionLock locker;
 
   if (isAlreadyShutDown()) {
     return;
@@ -223,20 +117,16 @@ NS_IMETHODIMP
 U2FRegisterTask::Run()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     ReturnError(ErrorCode::OTHER_ERROR);
     return NS_ERROR_FAILURE;
   }
 
-  // TODO: Implement USB Tokens in Bug 1245527
-  const bool softTokenEnabled =
-    Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
-
   for (size_t i = 0; i < mRegisteredKeys.Length(); ++i) {
     RegisteredKey request(mRegisteredKeys[i]);
 
     // Check for required attributes
     if (!(request.mKeyHandle.WasPassed() &&
           request.mVersion.WasPassed())) {
       continue;
     }
@@ -257,25 +147,28 @@ U2FRegisterTask::Run()
     // We ignore mTransports, as it is intended to be used for sorting the
     // available devices by preference, but is not an exclusion factor.
 
     bool isCompatible = false;
     bool isRegistered = false;
 
     // Determine if the provided keyHandle is registered at any device. If so,
     // then we'll return DEVICE_INELIGIBLE to signify we're already registered.
-    if (softTokenEnabled) {
-      rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
-                                &isCompatible);
+    for (auto token : mAuthenticators) {
+      rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
+      if (!isCompatible) {
+        continue;
+      }
 
-      rv = NSSTokenIsRegistered(mNSSToken, keyHandle, &isRegistered);
+      rv = token->IsRegistered(keyHandle.Elements(), keyHandle.Length(),
+                               &isRegistered);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
 
       if (isCompatible && isRegistered) {
         ReturnError(ErrorCode::DEVICE_INELIGIBLE);
         return NS_OK;
@@ -328,31 +221,40 @@ U2FRegisterTask::Run()
       return NS_ERROR_FAILURE;
     }
 
     // Get the registration data from the token
     CryptoBuffer regData;
     bool registerSuccess = false;
     bool isCompatible = false;
 
-    if (!registerSuccess && softTokenEnabled) {
-      rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
-                                &isCompatible);
+    for (auto token : mAuthenticators) {
+      rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
 
       if (isCompatible) {
-        rv = NSSTokenRegister(mNSSToken, appParam, challengeParam, regData);
+        uint8_t* buffer;
+        uint32_t bufferlen;
+        nsresult rv;
+        rv = token->Register(appParam.Elements(), appParam.Length(),
+                             challengeParam.Elements(), challengeParam.Length(),
+                             &buffer, &bufferlen);
         if (NS_FAILED(rv)) {
           ReturnError(ErrorCode::OTHER_ERROR);
           return NS_ERROR_FAILURE;
         }
+
+        MOZ_ASSERT(buffer);
+        regData.Assign(buffer, bufferlen);
+        free(buffer);
         registerSuccess = true;
+        break;
       }
     }
 
     if (!registerSuccess) {
       // Try another request
       continue;
     }
 
@@ -386,22 +288,22 @@ U2FRegisterTask::Run()
   return NS_ERROR_FAILURE;
 }
 
 U2FSignTask::U2FSignTask(const nsAString& aOrigin,
                          const nsAString& aAppId,
                          const nsAString& aChallenge,
                          const Sequence<RegisteredKey>& aRegisteredKeys,
                          U2FSignCallback* aCallback,
-                         const nsCOMPtr<nsINSSU2FToken>& aNSSToken)
+                         const Sequence<Authenticator>& aAuthenticators)
   : U2FTask(aOrigin, aAppId)
   , mChallenge(aChallenge)
   , mRegisteredKeys(aRegisteredKeys)
   , mCallback(aCallback)
-  , mNSSToken(aNSSToken)
+  , mAuthenticators(aAuthenticators)
 {}
 
 U2FSignTask::~U2FSignTask()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return;
   }
@@ -418,20 +320,16 @@ NS_IMETHODIMP
 U2FSignTask::Run()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     ReturnError(ErrorCode::OTHER_ERROR);
     return NS_ERROR_FAILURE;
   }
 
-  // TODO: Implement USB Tokens in Bug 1245527
-  const bool softTokenEnabled =
-    Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED);
-
   // Search the requests for one a token can fulfill
   for (size_t i = 0; i < mRegisteredKeys.Length(); i += 1) {
     RegisteredKey request(mRegisteredKeys[i]);
 
     // Check for required attributes
     if (!(request.mVersion.WasPassed() &&
           request.mKeyHandle.WasPassed())) {
       continue;
@@ -487,40 +385,52 @@ U2FSignTask::Run()
 
     // Get the signature from the token
     CryptoBuffer signatureData;
     bool signSuccess = false;
 
     // We ignore mTransports, as it is intended to be used for sorting the
     // available devices by preference, but is not an exclusion factor.
 
-    if (!signSuccess && softTokenEnabled) {
+    for (size_t a = 0; a < mAuthenticators.Length() && !signSuccess; ++a) {
+      Authenticator token(mAuthenticators[a]);
       bool isCompatible = false;
       bool isRegistered = false;
 
-      rv = NSSTokenIsCompatible(mNSSToken, request.mVersion.Value(),
-                                &isCompatible);
+      rv = token->IsCompatibleVersion(request.mVersion.Value(), &isCompatible);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
+      if (!isCompatible) {
+        continue;
+      }
 
-      rv = NSSTokenIsRegistered(mNSSToken, keyHandle, &isRegistered);
+      rv = token->IsRegistered(keyHandle.Elements(), keyHandle.Length(),
+                               &isRegistered);
       if (NS_FAILED(rv)) {
         ReturnError(ErrorCode::OTHER_ERROR);
         return NS_ERROR_FAILURE;
       }
 
       if (isCompatible && isRegistered) {
-        rv = NSSTokenSign(mNSSToken, keyHandle, appParam, challengeParam,
-                          signatureData);
+        uint8_t* buffer;
+        uint32_t bufferlen;
+        nsresult rv = token->Sign(appParam.Elements(), appParam.Length(),
+                                  challengeParam.Elements(), challengeParam.Length(),
+                                  keyHandle.Elements(), keyHandle.Length(),
+                                  &buffer, &bufferlen);
         if (NS_FAILED(rv)) {
           ReturnError(ErrorCode::OTHER_ERROR);
           return NS_ERROR_FAILURE;
         }
+
+        MOZ_ASSERT(buffer);
+        signatureData.Assign(buffer, bufferlen);
+        free(buffer);
         signSuccess = true;
       }
     }
 
     if (!signSuccess) {
       // Try another request
       continue;
     }
@@ -671,61 +581,79 @@ U2F::Init(nsPIDOMWindowInner* aParent, E
   }
 
   if (NS_WARN_IF(mOrigin.IsEmpty())) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   if (!EnsureNSSInitializedChromeOrContent()) {
-    MOZ_LOG(gU2FLog, LogLevel::Debug, ("Failed to get NSS context for U2F"));
+    MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Failed to get NSS context for U2F"));
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
-  if (XRE_IsParentProcess()) {
-    mNSSToken = do_GetService(NS_NSSU2FTOKEN_CONTRACTID);
-    if (NS_WARN_IF(!mNSSToken)) {
-      aRv.Throw(NS_ERROR_FAILURE);
-      return;
+  // Monolithically insert compatible nsIU2FToken objects into mAuthenticators.
+  // In future functionality expansions, this is where we could add a dynamic
+  // add/remove interface.
+  if (Preferences::GetBool(PREF_U2F_SOFTTOKEN_ENABLED)) {
+    if (!XRE_IsParentProcess()) {
+      MOZ_LOG(gWebauthLog, LogLevel::Debug,
+        ("Is e10s Process, getting remote U2F soft token"));
+
+      if (!mAuthenticators.AppendElement(new NSSU2FTokenRemote(),
+                                         mozilla::fallible)) {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return;
+      }
+    } else {
+       MOZ_LOG(gWebauthLog, LogLevel::Debug,
+        ("Is non-e10s Process, getting direct U2F soft token"));
+
+      nsCOMPtr<nsINSSU2FToken> softToken =
+        do_GetService(NS_NSSU2FTOKEN_CONTRACTID);
+      if (NS_WARN_IF(!softToken)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+
+      if (!mAuthenticators.AppendElement(softToken, mozilla::fallible)) {
+        aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+        return;
+      }
     }
   }
-
-  aRv = mUSBToken.Init();
-  if (NS_WARN_IF(aRv.Failed())) {
-    return;
-  }
 }
 
 void
 U2F::Register(const nsAString& aAppId,
               const Sequence<RegisterRequest>& aRegisterRequests,
               const Sequence<RegisteredKey>& aRegisteredKeys,
               U2FRegisterCallback& aCallback,
               const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
               ErrorResult& aRv)
 {
   RefPtr<U2FRegisterTask> registerTask = new U2FRegisterTask(mOrigin, aAppId,
                                                              aRegisterRequests,
                                                              aRegisteredKeys,
                                                              &aCallback,
-                                                             mNSSToken);
+                                                             mAuthenticators);
 
   EvaluateAppIDAndRunTask(registerTask);
 }
 
 void
 U2F::Sign(const nsAString& aAppId,
           const nsAString& aChallenge,
           const Sequence<RegisteredKey>& aRegisteredKeys,
           U2FSignCallback& aCallback,
           const Optional<Nullable<int32_t>>& opt_aTimeoutSeconds,
           ErrorResult& aRv)
 {
   RefPtr<U2FSignTask> signTask = new U2FSignTask(mOrigin, aAppId, aChallenge,
                                                  aRegisteredKeys, &aCallback,
-                                                 mNSSToken);
+                                                 mAuthenticators);
 
   EvaluateAppIDAndRunTask(signTask);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/u2f/U2F.h
+++ b/dom/u2f/U2F.h
@@ -8,17 +8,17 @@
 #define mozilla_dom_U2F_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsINSSU2FToken.h"
+#include "nsIU2FToken.h"
 #include "nsNSSShutDown.h"
 #include "nsPIDOMWindow.h"
 #include "nsWrapperCache.h"
 
 #include "USBToken.h"
 
 namespace mozilla {
 namespace dom {
@@ -35,16 +35,18 @@ enum class ErrorCode {
   OK = 0,
   OTHER_ERROR = 1,
   BAD_REQUEST = 2,
   CONFIGURATION_UNSUPPORTED = 3,
   DEVICE_INELIGIBLE = 4,
   TIMEOUT = 5
 };
 
+typedef nsCOMPtr<nsIU2FToken> Authenticator;
+
 class U2FTask : public Runnable
 {
 public:
   U2FTask(const nsAString& aOrigin,
           const nsAString& aAppId);
 
   nsString mOrigin;
   nsString mAppId;
@@ -60,59 +62,59 @@ class U2FRegisterTask final : public nsN
                               public U2FTask
 {
 public:
   U2FRegisterTask(const nsAString& aOrigin,
                   const nsAString& aAppId,
                   const Sequence<RegisterRequest>& aRegisterRequests,
                   const Sequence<RegisteredKey>& aRegisteredKeys,
                   U2FRegisterCallback* aCallback,
-                  const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
+                  const Sequence<Authenticator>& aAuthenticators);
 
   // No NSS resources to release.
   virtual
   void virtualDestroyNSSReference() override {};
 
   void ReturnError(ErrorCode code) override;
 
   NS_DECL_NSIRUNNABLE
 private:
   ~U2FRegisterTask();
 
   Sequence<RegisterRequest> mRegisterRequests;
   Sequence<RegisteredKey> mRegisteredKeys;
   RefPtr<U2FRegisterCallback> mCallback;
-  nsCOMPtr<nsINSSU2FToken> mNSSToken;
+  Sequence<Authenticator> mAuthenticators;
 };
 
 class U2FSignTask final : public nsNSSShutDownObject,
                           public U2FTask
 {
 public:
   U2FSignTask(const nsAString& aOrigin,
               const nsAString& aAppId,
               const nsAString& aChallenge,
               const Sequence<RegisteredKey>& aRegisteredKeys,
               U2FSignCallback* aCallback,
-              const nsCOMPtr<nsINSSU2FToken>& aNSSToken);
+              const Sequence<Authenticator>& aAuthenticators);
 
   // No NSS resources to release.
   virtual
   void virtualDestroyNSSReference() override {};
 
   void ReturnError(ErrorCode code) override;
 
   NS_DECL_NSIRUNNABLE
 private:
   ~U2FSignTask();
 
   nsString mChallenge;
   Sequence<RegisteredKey> mRegisteredKeys;
   RefPtr<U2FSignCallback> mCallback;
-  nsCOMPtr<nsINSSU2FToken> mNSSToken;
+  Sequence<Authenticator> mAuthenticators;
 };
 
 class U2F final : public nsISupports,
                   public nsWrapperCache,
                   public nsNSSShutDownObject
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -150,18 +152,17 @@ public:
 
   // No NSS resources to release.
   virtual
   void virtualDestroyNSSReference() override {};
 
 private:
   nsCOMPtr<nsPIDOMWindowInner> mParent;
   nsString mOrigin;
-  USBToken mUSBToken;
-  nsCOMPtr<nsINSSU2FToken> mNSSToken;
+  Sequence<Authenticator> mAuthenticators;
 
   ~U2F();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_U2F_h
--- a/dom/u2f/moz.build
+++ b/dom/u2f/moz.build
@@ -1,20 +1,22 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
+    'NSSU2FTokenRemote.h',
     'U2F.h',
     'USBToken.h',
 ]
 
 UNIFIED_SOURCES += [
+    'NSSU2FTokenRemote.cpp',
     'U2F.cpp',
     'USBToken.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
--- a/dom/webidl/Blob.webidl
+++ b/dom/webidl/Blob.webidl
@@ -5,18 +5,20 @@
  *
  * The origin of this IDL file is
  * http://dev.w3.org/2006/webapi/FileAPI/#blob
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-[Constructor,
- Constructor(sequence<(ArrayBuffer or ArrayBufferView or Blob or DOMString)> blobParts, optional BlobPropertyBag options),
+typedef (ArrayBuffer or ArrayBufferView or Blob or USVString) BlobPart;
+
+[Constructor(optional sequence<BlobPart> blobParts,
+             optional BlobPropertyBag options),
  Exposed=(Window,Worker)]
 interface Blob {
 
   [GetterThrows]
   readonly attribute unsigned long long size;
 
   readonly attribute DOMString type;
 
--- a/dom/webidl/CSPDictionaries.webidl
+++ b/dom/webidl/CSPDictionaries.webidl
@@ -23,13 +23,14 @@ dictionary CSP {
   // sequence<DOMString> reflected-xss; // not suppored in Firefox
   sequence<DOMString> base-uri;
   sequence<DOMString> form-action;
   sequence<DOMString> referrer;
   sequence<DOMString> manifest-src;
   sequence<DOMString> upgrade-insecure-requests;
   sequence<DOMString> child-src;
   sequence<DOMString> block-all-mixed-content;
+  sequence<DOMString> require-sri-for;
 };
 
 dictionary CSPPolicies {
   sequence<CSP> csp-policies;
 };
--- a/dom/webidl/Directory.webidl
+++ b/dom/webidl/Directory.webidl
@@ -10,17 +10,20 @@
  * path should be a descendent path like "path/to/file.txt" and not contain a
  * segment of ".." or ".". So the paths aren't allowed to walk up the directory
  * tree. For example, paths like "../foo", "..", "/foo/bar" or "foo/../bar" are
  * not allowed.
  *
  * http://w3c.github.io/filesystem-api/#idl-def-Directory
  * https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface
  */
-[Exposed=(Window,Worker)]
+
+// This chromeConstructor is used by the MockFilePicker for testing only.
+[ChromeConstructor(DOMString path),
+ Exposed=(Window,Worker)]
 interface Directory {
   /*
    * The leaf name of the directory.
    */
   [Throws]
   readonly attribute DOMString name;
 
   /*
--- a/dom/webidl/File.webidl
+++ b/dom/webidl/File.webidl
@@ -4,17 +4,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * The origin of this IDL file is
  * https://w3c.github.io/FileAPI/#file
  */
 
 interface nsIFile;
 
-[Constructor(sequence<(ArrayBuffer or ArrayBufferView or Blob or DOMString)> fileBits,
+[Constructor(sequence<BlobPart> fileBits,
              USVString fileName, optional FilePropertyBag options),
 
  // These constructors are just for chrome callers:
  Constructor(Blob fileBits, optional ChromeFilePropertyBag options),
  Constructor(nsIFile fileBits, optional ChromeFilePropertyBag options),
  Constructor(USVString fileBits, optional ChromeFilePropertyBag options),
 
  Exposed=(Window,Worker)]
--- a/dom/webidl/HTMLInputElement.webidl
+++ b/dom/webidl/HTMLInputElement.webidl
@@ -204,16 +204,19 @@ partial interface HTMLInputElement {
   [Throws, Pref="dom.input.dirpicker"]
   Promise<sequence<(File or Directory)>> getFilesAndDirectories();
 
   [Throws, Pref="dom.input.dirpicker"]
   Promise<sequence<File>> getFiles(optional boolean recursiveFlag = false);
 
   [Throws, Pref="dom.input.dirpicker"]
   void chooseDirectory();
+
+  [Pref="dom.webkitBlink.dirPicker.enabled", BinaryName="WebkitDirectoryAttr", SetterThrows]
+  attribute boolean webkitdirectory;
 };
 
 [NoInterfaceObject]
 interface MozPhonetic {
   [Pure, ChromeOnly]
   readonly attribute DOMString phonetic;
 };
 
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -652,17 +652,17 @@ private:
     ScriptLoadInfo& loadInfo = mLoadInfos[aIndex];
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
     MOZ_ASSERT(channel == loadInfo.mChannel);
 
     // We synthesize the result code, but its never exposed to content.
     RefPtr<mozilla::dom::InternalResponse> ir =
       new mozilla::dom::InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-    ir->SetBody(loadInfo.mCacheReadStream);
+    ir->SetBody(loadInfo.mCacheReadStream, InternalResponse::UNKNOWN_BODY_SIZE);
     // Drop our reference to the stream now that we've passed it along, so it
     // doesn't hang around once the cache is done with it and keep data alive.
     loadInfo.mCacheReadStream = nullptr;
 
     // Set the channel info of the channel on the response so that it's
     // saved in the cache.
     ir->InitChannelInfo(channel);
 
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -542,17 +542,17 @@ private:
     if (NS_WARN_IF(result.Failed())) {
       MOZ_ASSERT(!result.IsErrorWithMessage());
       Fail(result.StealNSResult());
       return;
     }
 
     RefPtr<InternalResponse> ir =
       new InternalResponse(200, NS_LITERAL_CSTRING("OK"));
-    ir->SetBody(body);
+    ir->SetBody(body, mCN->Buffer().Length());
 
     ir->InitChannelInfo(mChannelInfo);
     if (mPrincipalInfo) {
       ir->SetPrincipalInfo(Move(mPrincipalInfo));
     }
 
     RefPtr<Response> response = new Response(aCache->GetGlobalObject(), ir);
 
--- a/dom/xbl/nsBindingManager.cpp
+++ b/dom/xbl/nsBindingManager.cpp
@@ -400,21 +400,18 @@ nsBindingManager::DoProcessAttachedQueue
     // Hold a strong reference while calling UnblockOnload since that might
     // run script.
     nsCOMPtr<nsIDocument> doc = mDocument;
     doc->UnblockOnload(true);
   }
 }
 
 void
-nsBindingManager::ProcessAttachedQueue(uint32_t aSkipSize)
+nsBindingManager::ProcessAttachedQueueInternal(uint32_t aSkipSize)
 {
-  if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize)
-    return;
-
   mProcessingAttachedStack = true;
 
   // Excute constructors. Do this from high index to low
   while (mAttachedStack.Length() > aSkipSize) {
     uint32_t lastItem = mAttachedStack.Length() - 1;
     RefPtr<nsXBLBinding> binding = mAttachedStack.ElementAt(lastItem);
     mAttachedStack.RemoveElementAt(lastItem);
     if (binding) {
@@ -1012,31 +1009,16 @@ nsBindingManager::Traverse(nsIContent *a
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable key");
     cb.NoteXPCOMChild(aContent);
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[via binding manager] mWrapperTable value");
     cb.NoteXPCOMChild(value);
   }
 }
 
 void
-nsBindingManager::BeginOutermostUpdate()
-{
-  mAttachedStackSizeOnOutermost = mAttachedStack.Length();
-}
-
-void
-nsBindingManager::EndOutermostUpdate()
-{
-  if (!mProcessingAttachedStack) {
-    ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
-    mAttachedStackSizeOnOutermost = 0;
-  }
-}
-
-void
 nsBindingManager::HandleChildInsertion(nsIContent* aContainer,
                                        nsIContent* aChild,
                                        uint32_t aIndexInContainer,
                                        bool aAppend)
 {
   NS_PRECONDITION(aChild, "Must have child");
   NS_PRECONDITION(!aContainer ||
                   uint32_t(aContainer->IndexOf(aChild)) == aIndexInContainer,
--- a/dom/xbl/nsBindingManager.h
+++ b/dom/xbl/nsBindingManager.h
@@ -90,17 +90,28 @@ public:
   nsINodeList* GetAnonymousNodesFor(nsIContent* aContent);
 
   nsresult ClearBinding(nsIContent* aContent);
   nsresult LoadBindingDocument(nsIDocument* aBoundDoc, nsIURI* aURL,
                                nsIPrincipal* aOriginPrincipal);
 
   nsresult AddToAttachedQueue(nsXBLBinding* aBinding);
   void RemoveFromAttachedQueue(nsXBLBinding* aBinding);
-  void ProcessAttachedQueue(uint32_t aSkipSize = 0);
+  void ProcessAttachedQueue(uint32_t aSkipSize = 0)
+  {
+    if (mProcessingAttachedStack || mAttachedStack.Length() <= aSkipSize) {
+      return;
+    }
+
+    ProcessAttachedQueueInternal(aSkipSize);
+  }
+private:
+  void ProcessAttachedQueueInternal(uint32_t aSkipSize);
+
+public:
 
   void ExecuteDetachedHandlers();
 
   nsresult PutXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo);
   nsXBLDocumentInfo* GetXBLDocumentInfo(nsIURI* aURI);
   void RemoveXBLDocumentInfo(nsXBLDocumentInfo* aDocumentInfo);
 
   nsresult PutLoadingDocListener(nsIURI* aURL, nsIStreamListener* aListener);
@@ -130,18 +141,28 @@ public:
 
   void Traverse(nsIContent *aContent,
                             nsCycleCollectionTraversalCallback &cb);
 
   NS_DECL_CYCLE_COLLECTION_CLASS(nsBindingManager)
 
   // Notify the binding manager when an outermost update begins and
   // ends.  The end method can execute script.
-  void BeginOutermostUpdate();
-  void EndOutermostUpdate();
+  void BeginOutermostUpdate()
+  {
+    mAttachedStackSizeOnOutermost = mAttachedStack.Length();
+  }
+
+  void EndOutermostUpdate()
+  {
+    if (!mProcessingAttachedStack) {
+      ProcessAttachedQueue(mAttachedStackSizeOnOutermost);
+      mAttachedStackSizeOnOutermost = 0;
+    }
+  }
 
   // When removing an insertion point or a parent of one, clear the insertion
   // points and their insertion parents.
   void ClearInsertionPointsRecursively(nsIContent* aContent);
 
   // Called when the document is going away
   void DropDocumentReference();
 
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -3143,17 +3143,17 @@ XULDocument::MaybeBroadcast()
         }
         if (!mHandlingDelayedAttrChange) {
             mHandlingDelayedAttrChange = true;
             for (uint32_t i = 0; i < mDelayedAttrChangeBroadcasts.Length(); ++i) {
                 nsIAtom* attrName = mDelayedAttrChangeBroadcasts[i].mAttrName;
                 if (mDelayedAttrChangeBroadcasts[i].mNeedsAttrChange) {
                     nsCOMPtr<nsIContent> listener =
                         do_QueryInterface(mDelayedAttrChangeBroadcasts[i].mListener);
-                    nsString value = mDelayedAttrChangeBroadcasts[i].mAttr;
+                    const nsString& value = mDelayedAttrChangeBroadcasts[i].mAttr;
                     if (mDelayedAttrChangeBroadcasts[i].mSetAttr) {
                         listener->SetAttr(kNameSpaceID_None, attrName, value,
                                           true);
                     } else {
                         listener->UnsetAttr(kNameSpaceID_None, attrName,
                                             true);
                     }
                 }
--- a/embedding/components/printingui/ipc/PPrinting.ipdl
+++ b/embedding/components/printingui/ipc/PPrinting.ipdl
@@ -18,17 +18,17 @@ sync protocol PPrinting
   manager PContent;
   manages PPrintProgressDialog;
   manages PPrintSettingsDialog;
   manages PRemotePrintJob;
 
 parent:
   sync ShowProgress(PBrowser browser,
                     PPrintProgressDialog printProgressDialog,
-                    PRemotePrintJob remotePrintJob,
+                    nullable PRemotePrintJob remotePrintJob,
                     bool isForPrinting)
     returns(bool notifyOnOpen,
             nsresult rv);
 
   async ShowPrintDialog(PPrintSettingsDialog dialog,
                         PBrowser browser,
                         PrintData settings);
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1955,21 +1955,27 @@ DrawTarget::Draw3DTransformedSurface(Sou
     return false;
   }
 
   // Wrap the surfaces in pixman images and do the transform.
   pixman_image_t* dst =
     pixman_image_create_bits(PIXMAN_a8r8g8b8,
                              xformBounds.width, xformBounds.height,
                              (uint32_t*)dstSurf->GetData(), dstSurf->Stride());
+  if (!dst) {
+    return false;
+  }
   pixman_image_t* src =
     pixman_image_create_bits(srcFormat,
                              srcSurf->GetSize().width, srcSurf->GetSize().height,
                              (uint32_t*)srcMap.GetData(), srcMap.GetStride());
-  MOZ_ASSERT(src && dst, "Failed to create pixman images?");
+  if (!src) {
+    pixman_image_unref(dst);
+    return false;
+  }
 
   pixman_image_set_filter(src, PIXMAN_FILTER_BILINEAR, nullptr, 0);
   pixman_image_set_transform(src, &xform);
 
   pixman_image_composite32(PIXMAN_OP_SRC,
                            src, nullptr, dst,
                            0, 0, 0, 0, 0, 0,
                            xformBounds.width, xformBounds.height);
--- a/gfx/cairo/cairo/src/cairo-surface-wrapper.c
+++ b/gfx/cairo/cairo/src/cairo-surface-wrapper.c
@@ -44,27 +44,19 @@
 
 static void
 _copy_transformed_pattern (cairo_pattern_t *pattern,
 			   const cairo_pattern_t *original,
 			   const cairo_matrix_t  *ctm_inverse)
 {
     _cairo_pattern_init_static_copy (pattern, original);
 
-    /* apply device_transform first so that it is transformed by ctm_inverse */
-    if (original->type == CAIRO_PATTERN_TYPE_SURFACE) {
-	cairo_surface_pattern_t *surface_pattern;
-	cairo_surface_t *surface;
-
-        surface_pattern = (cairo_surface_pattern_t *) original;
-        surface = surface_pattern->surface;
-
-	if (_cairo_surface_has_device_transform (surface))
-	    _cairo_pattern_transform (pattern, &surface->device_transform);
-    }
+    /* Device transform should already have been applied before cairo_surface_wrapper_* functions
+     * are called in _cairo_gstate_copy_transformed_pattern which all the gstate drawing
+     * functions call through _cairo_gstate_copy_transformed_source and such. */
 
     if (! _cairo_matrix_is_identity (ctm_inverse))
 	_cairo_pattern_transform (pattern, ctm_inverse);
 }
 
 static inline cairo_bool_t
 _cairo_surface_wrapper_needs_device_transform (cairo_surface_wrapper_t *wrapper)
 {
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -185,41 +185,26 @@ struct EventRegions {
   explicit EventRegions(nsIntRegion aHitRegion)
     : mHitRegion(aHitRegion)
   {
   }
 
   bool operator==(const EventRegions& aRegions) const
   {
     return mHitRegion == aRegions.mHitRegion &&
-           mDispatchToContentHitRegion == aRegions.mDispatchToContentHitRegion;
+           mDispatchToContentHitRegion == aRegions.mDispatchToContentHitRegion &&
+           mNoActionRegion == aRegions.mNoActionRegion &&
+           mHorizontalPanRegion == aRegions.mHorizontalPanRegion &&
+           mVerticalPanRegion == aRegions.mVerticalPanRegion;
   }
   bool operator!=(const EventRegions& aRegions) const
   {
     return !(*this == aRegions);
   }
 
-  void OrWith(const EventRegions& aOther)
-  {
-    mHitRegion.OrWith(aOther.mHitRegion);
-    mDispatchToContentHitRegion.OrWith(aOther.mDispatchToContentHitRegion);
-  }
-
-  void AndWith(const nsIntRegion& aRegion)
-  {
-    mHitRegion.AndWith(aRegion);
-    mDispatchToContentHitRegion.AndWith(aRegion);
-  }
-
-  void Sub(const EventRegions& aMinuend, const nsIntRegion& aSubtrahend)
-  {
-    mHitRegion.Sub(aMinuend.mHitRegion, aSubtrahend);
-    mDispatchToContentHitRegion.Sub(aMinuend.mDispatchToContentHitRegion, aSubtrahend);
-  }
-
   void ApplyTranslationAndScale(float aXTrans, float aYTrans, float aXScale, float aYScale)
   {
     mHitRegion.ScaleRoundOut(aXScale, aYScale);
     mDispatchToContentHitRegion.ScaleRoundOut(aXScale, aYScale);
     mNoActionRegion.ScaleRoundOut(aXScale, aYScale);
     mHorizontalPanRegion.ScaleRoundOut(aXScale, aYScale);
     mVerticalPanRegion.ScaleRoundOut(aXScale, aYScale);
 
@@ -229,22 +214,28 @@ struct EventRegions {
     mHorizontalPanRegion.MoveBy(aXTrans, aYTrans);
     mVerticalPanRegion.MoveBy(aXTrans, aYTrans);
   }
 
   void Transform(const gfx::Matrix4x4& aTransform)
   {
     mHitRegion.Transform(aTransform);
     mDispatchToContentHitRegion.Transform(aTransform);
+    mNoActionRegion.Transform(aTransform);
+    mHorizontalPanRegion.Transform(aTransform);
+    mVerticalPanRegion.Transform(aTransform);
   }
 
   bool IsEmpty() const
   {
     return mHitRegion.IsEmpty()
-        && mDispatchToContentHitRegion.IsEmpty();
+        && mDispatchToContentHitRegion.IsEmpty()
+        && mNoActionRegion.IsEmpty()
+        && mHorizontalPanRegion.IsEmpty()
+        && mVerticalPanRegion.IsEmpty();
   }
 
   nsCString ToString() const
   {
     nsCString result = mHitRegion.ToString();
     result.AppendLiteral(";dispatchToContent=");
     result.Append(mDispatchToContentHitRegion.ToString());
     return result;
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -691,17 +691,17 @@ APZCTreeManager::ReceiveInputEvent(Input
 
       // When the mouse is outside the window we still want to handle dragging
       // but we won't find an APZC. Fallback to root APZC then.
       if (!apzc && mRootNode) {
         apzc = mRootNode->GetApzc();
       }
 
       if (apzc) {
-        bool targetConfirmed = (hitResult == HitLayer);
+        bool targetConfirmed = (hitResult != HitNothing && hitResult != HitDispatchToContentRegion);
         if (gfxPrefs::APZDragEnabled() && hitScrollbar) {
           // If scrollbar dragging is enabled and we hit a scrollbar, wait
           // for the main-thread confirmation because it contains drag metrics
           // that we need.
           targetConfirmed = false;
         }
         result = mInputQueue->ReceiveInputEvent(
           apzc, targetConfirmed,
@@ -747,17 +747,17 @@ APZCTreeManager::ReceiveInputEvent(Input
       wheelInput.mHandledByAPZ = WillHandleInput(wheelInput);
       if (!wheelInput.mHandledByAPZ) {
         return result;
       }
 
       RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
                                                             &hitResult);
       if (apzc) {
-        MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion);
+        MOZ_ASSERT(hitResult != HitNothing);
 
         // For wheel events, the call to ReceiveInputEvent below may result in
         // scrolling, which changes the async transform. However, the event we
         // want to pass to gecko should be the pre-scroll event coordinates,
         // transformed into the gecko space. (pre-scroll because the mouse
         // cursor is stationary during wheel scrolling, unlike touchmove
         // events). Since we just flushed the pending repaints the transform to
         // gecko space should only consist of overscroll-cancelling transforms.
@@ -767,17 +767,17 @@ APZCTreeManager::ReceiveInputEvent(Input
           transformToGecko, wheelInput.mOrigin);
 
         if (!untransformedOrigin) {
           return result;
         }
 
         result = mInputQueue->ReceiveInputEvent(
           apzc,
-          /* aTargetConfirmed = */ hitResult == HitLayer,
+          /* aTargetConfirmed = */ hitResult != HitDispatchToContentRegion,
           wheelInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
         wheelInput.mOrigin = *untransformedOrigin;
       }
       break;
     } case PANGESTURE_INPUT: {
@@ -797,17 +797,17 @@ APZCTreeManager::ReceiveInputEvent(Input
       WidgetWheelEvent wheelEvent = panInput.ToWidgetWheelEvent(nullptr);
       EventStateManager::GetUserPrefsForWheelEvent(&wheelEvent,
         &panInput.mUserDeltaMultiplierX,
         &panInput.mUserDeltaMultiplierY);