merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 02 Jun 2016 11:56:07 +0200
changeset 300068 34a8be4346a9231e472fc36b1d7c0531e0fbf7c5
parent 299919 7c0dce574b3f2d5e8f6e4e60caab5532f8e13fd6 (current diff)
parent 300067 5c3ef07cd34c800c5032e45d90b1114b48cf7977 (diff)
child 300069 379f4fff66d54866353e62f184d54cd399b7445d
child 300087 58ac3bd12914f8d2d683176139e5630b4e32c2ca
child 300173 a4ec9c9adf18e33f71a9a0daa92939900e8501e7
push id30305
push usercbook@mozilla.com
push dateThu, 02 Jun 2016 09:56:32 +0000
treeherdermozilla-central@34a8be4346a9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone49.0a1
first release with
nightly linux32
34a8be4346a9 / 49.0a1 / 20160602030220 / files
nightly linux64
34a8be4346a9 / 49.0a1 / 20160602030220 / files
nightly mac
34a8be4346a9 / 49.0a1 / 20160602030220 / files
nightly win32
34a8be4346a9 / 49.0a1 / 20160602030220 / files
nightly win64
34a8be4346a9 / 49.0a1 / 20160602030220 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
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
mobile/android/base/moz.build
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(