Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Sun, 05 Feb 2017 10:39:54 -0500
changeset 342199 ae8db963d2f4a0a6930945cc53d1d7ef48c85097
parent 342198 f925e9ed9b94792d761e34c0c3a63af3a9b44e0d (current diff)
parent 340831 3e555770a90a41e04bbb4ac41b65fa2f1db6977d (diff)
child 342200 4f4ba281b7ab46f48395a2129cd2211004f3c9f8
push id31345
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 20:35:09 +0000
treeherdermozilla-central@a288fe35e494 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to graphics MozReview-Commit-ID: EcjJhQuqDFI
CLOBBER
browser/components/sessionstore/PrivacyLevel.jsm
browser/installer/package-manifest.in
browser/themes/shared/addons/addon-badge.svg
build/build-clang/disable-mac-tsan.patch
build/build-clang/query-selector-visibility.patch
devtools/client/netmonitor/components/clear-button.js
devtools/client/netmonitor/components/filter-buttons.js
devtools/client/netmonitor/components/search-box.js
devtools/client/netmonitor/components/summary-button.js
devtools/client/netmonitor/components/toggle-button.js
devtools/client/netmonitor/statistics-view.js
devtools/client/netmonitor/toolbar-view.js
ipc/chromium/src/base/lock.cc
js/src/jit/BaselineCacheIRCompiler.cpp
js/src/jit/ExecutableAllocatorPosix.cpp
js/src/jit/ExecutableAllocatorWin.cpp
layout/painting/FrameLayerBuilder.cpp
layout/reftests/layers/reftest.list
layout/style/CSSUnprefixingService.js
layout/style/CSSUnprefixingService.manifest
layout/style/nsICSSUnprefixingService.idl
layout/style/test/test_unprefixing_service.html
layout/style/test/test_unprefixing_service_prefs.html
layout/style/test/unprefixing_service_iframe.html
layout/style/test/unprefixing_service_utils.js
mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoViewContent.java
modules/libpref/init/all.js
taskcluster/ci/test/test-sets.yml
taskcluster/docker/desktop-build/Dockerfile
testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/postMessage/second-argument-null-in-array.html.ini
third_party/rust/gcc/.cargo-checksum.json
third_party/rust/gcc/.travis.yml
third_party/rust/gcc/Cargo.toml
third_party/rust/gcc/src/lib.rs
third_party/rust/gcc/src/windows_registry.rs
third_party/rust/gcc/tests/support/mod.rs
third_party/rust/gcc/tests/test.rs
third_party/rust/libc/.cargo-checksum.json
third_party/rust/libc/Cargo.toml
third_party/rust/libc/ci/docker/mipsel-unknown-linux-musl/Dockerfile
third_party/rust/libc/src/redox.rs
third_party/rust/libc/src/unix/bsd/apple/mod.rs
third_party/rust/libc/src/unix/bsd/freebsdlike/dragonfly/mod.rs
third_party/rust/libc/src/unix/bsd/freebsdlike/freebsd/mod.rs
third_party/rust/libc/src/unix/bsd/freebsdlike/mod.rs
third_party/rust/libc/src/unix/bsd/mod.rs
third_party/rust/libc/src/unix/bsd/netbsdlike/mod.rs
third_party/rust/libc/src/unix/bsd/netbsdlike/netbsd/mod.rs
third_party/rust/libc/src/unix/bsd/netbsdlike/openbsdlike/mod.rs
third_party/rust/libc/src/unix/haiku/mod.rs
third_party/rust/libc/src/unix/mod.rs
third_party/rust/libc/src/unix/notbsd/android/mod.rs
third_party/rust/libc/src/unix/notbsd/linux/mips/mips32.rs
third_party/rust/libc/src/unix/notbsd/linux/mips/mips64.rs
third_party/rust/libc/src/unix/notbsd/linux/mips/mod.rs
third_party/rust/libc/src/unix/notbsd/linux/mod.rs
third_party/rust/libc/src/unix/notbsd/linux/musl/b64/mod.rs
third_party/rust/libc/src/unix/notbsd/linux/musl/mod.rs
third_party/rust/libc/src/unix/notbsd/linux/other/mod.rs
third_party/rust/libc/src/unix/notbsd/linux/s390x.rs
third_party/rust/libc/src/unix/notbsd/mod.rs
third_party/rust/libc/src/unix/solaris/mod.rs
third_party/rust/libz-sys/.cargo-checksum.json
third_party/rust/libz-sys/Cargo.toml
third_party/rust/libz-sys/build.rs
toolkit/library/gtest/rust/Cargo.lock
toolkit/library/rust/Cargo.lock
toolkit/library/rust/shared/Cargo.toml
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Merge day clobber
\ No newline at end of file
+Touch clobber because of bug 1336456
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -2082,21 +2082,23 @@ DocAccessible::DoARIAOwnsRelocation(Acce
           TreeMutation imut(aOwner);
           aOwner->InsertChildAt(insertIdx, child);
           imut.AfterInsertion(child);
           imut.Done();
 
           child->SetRelocated(true);
           children->InsertElementAt(arrayIdx, child);
 
+          // Create subtree before adjusting the insertion index, since subtree
+          // creation may alter children in the container.
+          CreateSubtree(child);
+          FireEventsOnInsertion(aOwner);
+
           insertIdx = child->IndexInParent() + 1;
           arrayIdx++;
-
-          CreateSubtree(child);
-          FireEventsOnInsertion(aOwner);
         }
       }
       continue;
     }
 
 #ifdef A11Y_LOG
   logging::TreeInfo("aria owns traversal", logging::eVerbose,
                     "candidate", child, nullptr);
--- a/accessible/interfaces/gecko/IGeckoCustom.idl
+++ b/accessible/interfaces/gecko/IGeckoCustom.idl
@@ -9,16 +9,17 @@ import "oaidl.idl";
 
 [object, uuid(7510360f-cdae-4de9-88c8-d167eda62afc)]
 interface IGeckoCustom : IUnknown
 {
   [propget] HRESULT ID([out, retval] unsigned __int64* aID);
   [propget] HRESULT anchorCount([out, retval] long* aCount);
   [propget] HRESULT DOMNodeID([out, retval] BSTR* aID);
   [propget] HRESULT minimumIncrement([out, retval] double* aIncrement);
+  [propget] HRESULT mozState([out, retval] unsigned __int64* aState);
 }
 
 
 [
     uuid(55769d85-f830-4d76-9e39-3670914a28f7),
     helpstring("private custom gecko interfaces")
 ]
 library IGeckoCustom
--- a/accessible/ipc/ProxyAccessibleBase.cpp
+++ b/accessible/ipc/ProxyAccessibleBase.cpp
@@ -34,17 +34,17 @@ ProxyAccessibleBase<Derived>::Shutdown()
 
   // XXX Ideally  this wouldn't be necessary, but it seems OuterDoc accessibles
   // can be destroyed before the doc they own.
   if (!mOuterDoc) {
     uint32_t childCount = mChildren.Length();
     for (uint32_t idx = 0; idx < childCount; idx++)
       mChildren[idx]->Shutdown();
   } else {
-    if (mChildren.Length() > 1)
+    if (mChildren.Length() != 1)
       MOZ_CRASH("outer doc doesn't own adoc!");
 
     mChildren[0]->AsDoc()->Unbind();
   }
 
   mChildren.Clear();
   ProxyDestroyed(static_cast<Derived*>(this));
   mDoc->RemoveAccessible(static_cast<Derived*>(this));
@@ -71,17 +71,19 @@ void
 ProxyAccessibleBase<Derived>::ClearChildDoc(DocAccessibleParent* aChildDoc)
 {
   MOZ_ASSERT(aChildDoc);
   // This is possible if we're replacing one document with another: Doc 1
   // has not had a chance to remove itself, but was already replaced by Doc 2
   // in SetChildDoc(). This could result in two subsequent calls to
   // ClearChildDoc() even though mChildren.Length() == 1.
   MOZ_ASSERT(mChildren.Length() <= 1);
-  mChildren.RemoveElement(aChildDoc);
+  if (mChildren.RemoveElement(aChildDoc)) {
+    mOuterDoc = false;
+  }
 }
 
 template <class Derived>
 bool
 ProxyAccessibleBase<Derived>::MustPruneChildren() const
 {
   // this is the equivalent to nsAccUtils::MustPrune for proxies and should be
   // kept in sync with that.
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -188,28 +188,27 @@ ProxyAccessible::Description(nsString& a
     return;
   }
   aDesc = (wchar_t*)resultWrap;
 }
 
 uint64_t
 ProxyAccessible::State() const
 {
-  uint64_t state = 0;
-  RefPtr<IAccessible> acc;
-  if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
-    return state;
+  RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+  if (!custom) {
+    return 0;
   }
 
-  VARIANT varState;
-  HRESULT hr = acc->get_accState(kChildIdSelf, &varState);
+  uint64_t state;
+  HRESULT hr = custom->get_mozState(&state);
   if (FAILED(hr)) {
-    return state;
+    return 0;
   }
-  return uint64_t(varState.lVal);
+  return state;
 }
 
 nsIntRect
 ProxyAccessible::Bounds()
 {
   nsIntRect rect;
 
   RefPtr<IAccessible> acc;
--- a/accessible/tests/mochitest/treeupdate/test_ariaowns.html
+++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html
@@ -527,16 +527,60 @@
       }
 
       this.getID = function setARIAOwnsOnElToRemove_getID()
       {
         return `set ARIA owns on an element, and then remove it, and then remove its parent`;
       }
     }
 
+    /**
+     * Set ARIA owns on inaccessible span element that contains
+     * accessible children. This will move children from the container for
+     * the span.
+     */
+    function test8()
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, "t8_container")
+      ];
+
+      this.invoke = function test8_invoke()
+      {
+        var tree =
+          { SECTION: [
+            { PUSHBUTTON: [] },
+            { ENTRY: [] },
+            { ENTRY: [] },
+            { ENTRY: [] }
+          ] };
+        testAccessibleTree("t8_container", tree);
+
+        getNode(t8_container).setAttribute("aria-owns", "t8_span t8_button");
+      }
+
+      this.finalCheck = function test8_finalCheck()
+      {
+        var tree =
+          { SECTION: [
+            { TEXT: [
+              { ENTRY: [] },
+              { ENTRY: [] },
+              { ENTRY: [] }
+            ] },
+            { PUSHBUTTON: [] }
+          ] };
+        testAccessibleTree("t8_container", tree);
+      }
+
+      this.getID = function test8_getID()
+      {
+        return `Set ARIA owns on inaccessible span element that contains accessible children`;
+      }
+    }
 
     ////////////////////////////////////////////////////////////////////////////
     // Test
     ////////////////////////////////////////////////////////////////////////////
 
     //gA11yEventDumpToConsole = true;
     //enableLogging("tree,eventTree,verbose"); // debug stuff
 
@@ -575,16 +619,18 @@
         "t5_container", "t5_radio t5_button t5_checkbox",
         [ "t5_radio", "t5_button" ],
         [ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ]));
 
       gQueue.push(new removeNotARIAOwnedEl("t6_container", "t6_span"));
 
       gQueue.push(new setARIAOwnsOnElToRemove("t7_parent", "t7_child"));
 
+      gQueue.push(new test8());
+
       gQueue.invoke(); // SimpleTest.finish() will be called in the end
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
 
   </script>
 </head>
@@ -633,11 +679,15 @@
   </div>
   <div id="t6_fake" role="group"></div>
 
   <div id="t7_container">
     <div id="t7_parent">
       <div id="t7_child"></div>
     </div>
   </div>
+
+  <div id="t8_container">
+    <input id="t8_button" type="button"><span id="t8_span"><input><input><input></span>
+  </div>
 </body>
 
 </html>
--- a/accessible/windows/msaa/GeckoCustom.cpp
+++ b/accessible/windows/msaa/GeckoCustom.cpp
@@ -45,8 +45,15 @@ GeckoCustom::get_ID(uint64_t* aID)
 }
 
 STDMETHODIMP
 GeckoCustom::get_minimumIncrement(double* aIncrement)
 {
   *aIncrement = mAcc->Step();
   return S_OK;
 }
+
+STDMETHODIMP
+GeckoCustom::get_mozState(uint64_t* aState)
+{
+  *aState = mAcc->State();
+  return S_OK;
+}
--- a/accessible/windows/msaa/GeckoCustom.h
+++ b/accessible/windows/msaa/GeckoCustom.h
@@ -25,16 +25,17 @@ public:
 
   // IUnknown
   DECL_IUNKNOWN
 
   virtual STDMETHODIMP get_anchorCount(long* aCount);
   virtual STDMETHODIMP get_DOMNodeID(BSTR* aID);
   virtual STDMETHODIMP get_ID(uint64_t* aID);
   virtual STDMETHODIMP get_minimumIncrement(double* aIncrement);
+  virtual STDMETHODIMP get_mozState(uint64_t* aState);
 
 private:
   GeckoCustom() = delete;
   GeckoCustom& operator =(const GeckoCustom&) = delete;
   GeckoCustom(const GeckoCustom&) = delete;
   GeckoCustom(GeckoCustom&&) = delete;
   GeckoCustom& operator=(GeckoCustom&&) = delete;
 
--- a/addon-sdk/source/python-lib/cuddlefish/prefs.py
+++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py
@@ -228,15 +228,14 @@ DEFAULT_TEST_PREFS = {
     # download test runs first doesn't show the popup inconsistently.
     'browser.download.panel.shown': True,
     # Assume the about:newtab page's intro panels have been shown to not depend on
     # which test runs first and happens to open about:newtab
     'browser.newtabpage.introShown': True,
     # Disable useragent updates.
     'general.useragent.updates.enabled': False,
     'media.eme.enabled': True,
-    'media.eme.apiVisible': True,
-    # Don't forceably kill content processes after a timeout
+    # Don't forcibly kill content processes after a timeout
     'dom.ipc.tabs.shutdownTimeoutSecs': 0,
     'general.useragent.locale': "en-US",
     'intl.locale.matchOS': "en-US",
     'dom.indexedDB.experimental': True
 }
--- a/addon-sdk/source/test/preferences/test.json
+++ b/addon-sdk/source/test/preferences/test.json
@@ -32,14 +32,13 @@
   "layout.css.grid.enabled": true,
   "layout.spammy_warnings.enabled": false,
   "network.http.bypass-cachelock-threshold": 200000,
   "geo.provider.testing": true,
   "browser.pagethumbnails.capturing_disabled": true,
   "browser.download.panel.shown": true,
   "general.useragent.updates.enabled": false,
   "media.eme.enabled": true,
-  "media.eme.apiVisible": true,
   "dom.ipc.tabs.shutdownTimeoutSecs": 0,
   "general.useragent.locale": "en-US",
   "intl.locale.matchOS": "en-US",
   "dom.indexedDB.experimental": true
 }
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -319,17 +319,16 @@ pref("media.cache_readahead_limit", 30);
 
 #ifdef MOZ_FMP4
 // Enable/Disable Gonk Decoder Module
 pref("media.gonk.enabled", true);
 #endif
 
 //Encrypted media extensions.
 pref("media.eme.enabled", true);
-pref("media.eme.apiVisible", true);
 // The default number of decoded video frames that are enqueued in
 // MediaDecoderReader's mVideoQueue.
 pref("media.video-queue.default-size", 3);
 
 // optimize images' memory usage
 pref("image.downscale-during-decode.enabled", true);
 pref("image.mem.allow_locking_in_content_processes", true);
 // Limit the surface cache to 1/8 of main memory or 128MB, whichever is smaller.
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -493,18 +493,16 @@
 @RESPATH@/components/satchel.manifest
 @RESPATH@/components/nsFormAutoComplete.js
 @RESPATH@/components/nsFormHistory.js
 @RESPATH@/components/FormHistoryStartup.js
 @RESPATH@/components/nsInputListAutoComplete.js
 @RESPATH@/components/formautofill.manifest
 @RESPATH@/components/FormAutofillContentService.js
 @RESPATH@/components/FormAutofillStartup.js
-@RESPATH@/components/CSSUnprefixingService.js
-@RESPATH@/components/CSSUnprefixingService.manifest
 @RESPATH@/components/contentAreaDropListener.manifest
 @RESPATH@/components/contentAreaDropListener.js
 @RESPATH@/components/messageWakeupService.js
 @RESPATH@/components/messageWakeupService.manifest
 @RESPATH@/components/SettingsManager.js
 @RESPATH@/components/SettingsManager.manifest
 @RESPATH@/components/SettingsService.js
 @RESPATH@/components/SettingsService.manifest
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -529,17 +529,18 @@ pref("privacy.history.custom",          
 // 6 - Last 24 hours
 pref("privacy.sanitize.timeSpan", 1);
 pref("privacy.sanitize.sanitizeOnShutdown", false);
 
 pref("privacy.sanitize.migrateFx3Prefs",    false);
 
 pref("privacy.panicButton.enabled",         true);
 
-pref("privacy.firstparty.isolate",          false);
+pref("privacy.firstparty.isolate",                        false);
+pref("privacy.firstparty.isolate.restrict_opener_access", true);
 
 // Time until temporary permissions expire, in ms
 pref("privacy.temporary_permission_expire_time_ms",  3600000);
 
 pref("network.proxy.share_proxy_settings",  false); // use the same proxy settings for all protocols
 
 // simple gestures support
 pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
@@ -1370,17 +1371,16 @@ pref("ui.key.menuAccessKeyFocuses", true
 // users on an open source operating systems didn't opt into. The first
 // time a site using EME is encountered, the user will be prompted to
 // enable DRM, whereupon the EME plugin binaries will be downloaded if
 // permission is granted.
 pref("media.eme.enabled", false);
 #else
 pref("media.eme.enabled", true);
 #endif
-pref("media.eme.apiVisible", true);
 
 // Whether we should run a test-pattern through EME GMPs before assuming they'll
 // decode H.264.
 pref("media.gmp.trial-create.enabled", true);
 
 // Note: when media.gmp-*.visible is true, provided we're running on a
 // supported platform/OS version, the corresponding CDM appears in the
 // plugins list, Firefox will download the GMP/CDM if enabled, and our
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3395,18 +3395,20 @@ var PrintPreviewListener = {
                                                   { inBackground: false,
                                                     preferredRemoteType,
                                                     relatedBrowser: browser });
       gBrowser.selectedTab = this._printPreviewTab;
     }
     return gBrowser.getBrowserForTab(this._printPreviewTab);
   },
   createSimplifiedBrowser() {
+    let browser = this._tabBeforePrintPreview.linkedBrowser;
     this._simplifyPageTab = gBrowser.loadOneTab("about:blank",
-                                                { inBackground: true });
+                                                { inBackground: true,
+                                                  relatedBrowser: browser });
     return this.getSimplifiedSourceBrowser();
   },
   getSourceBrowser() {
     return this._tabBeforePrintPreview ?
       this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
   },
   getSimplifiedSourceBrowser() {
     return this._simplifyPageTab ?
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -306,20 +306,31 @@ Site.prototype = {
   },
 
   /**
    * Speculatively opens a connection to the current site.
    */
   _speculativeConnect: function Site_speculativeConnect() {
     let sc = Services.io.QueryInterface(Ci.nsISpeculativeConnect);
     let uri = Services.io.newURI(this.url);
+
+    if (!uri.schemeIs("http") && !uri.schemeIs("https")) {
+      return;
+    }
+
     try {
       // This can throw for certain internal URLs, when they wind up in
       // about:newtab. Be sure not to propagate the error.
-      sc.speculativeConnect(uri, null);
+
+      // We use the URI's codebase principal here to open its speculative
+      // connection.
+      let originAttributes = document.docShell.getOriginAttributes();
+      let principal = Services.scriptSecurityManager
+                              .createCodebasePrincipal(uri, originAttributes);
+      sc.speculativeConnect2(uri, principal, null);
     } catch (e) {}
   },
 
   /**
    * Record interaction with site using telemetry.
    */
   _recordSiteClicked: function Site_recordSiteClicked(aIndex) {
     if (Services.prefs.prefHasUserValue("browser.newtabpage.rows") ||
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -7104,24 +7104,16 @@
           let browser = this.linkedBrowser;
           let modifiedAttrs = [];
           let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
 
           if (browser.audioBlocked) {
             this.removeAttribute("blocked");
             modifiedAttrs.push("blocked");
 
-            // We don't want sound icon flickering between "blocked", "none" and
-            // "sound-playing", here adding the "soundplaying" is to keep the
-            // transition smoothly.
-            if (!this.hasAttribute("soundplaying")) {
-              this.setAttribute("soundplaying", true);
-              modifiedAttrs.push("soundplaying");
-            }
-
             browser.resumeMedia();
             hist.add(3 /* unblockByClickingIcon */);
             this.finishMediaBlockTimer();
           } else {
             if (browser.audioMuted) {
               browser.unmute();
               this.removeAttribute("muted");
               BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
--- a/browser/base/content/test/general/browser_selectpopup.js
+++ b/browser/base/content/test/general/browser_selectpopup.js
@@ -76,21 +76,29 @@ const PAGECONTENT_TRANSLATED =
   "<html><body>" +
   "<div id='div'>" +
   "<iframe id='frame' width='320' height='295' style='border: none;'" +
   "        src='data:text/html,<select id=select autofocus><option>he he he</option><option>boo boo</option><option>baz baz</option></select>'" +
   "</iframe>" +
   "</div></body></html>";
 
 const PAGECONTENT_COLORS =
-  "<html><head><style>.blue { color: #fff; background-color: #00f; } .green { color: #800080; background-color: green; }</style>" +
+  "<html><head><style>" +
+  "  .blue { color: #fff; background-color: #00f; }" +
+  "  .green { color: #800080; background-color: green; }" +
+  "  .defaultColor { color: -moz-ComboboxText; }" +
+  "  .defaultBackground { background-color: -moz-Combobox; }" +
+  "</style>" +
   "<body><select id='one'>" +
   '  <option value="One" style="color: #fff; background-color: #f00;">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(255, 0, 0)"}</option>' +
   '  <option value="Two" class="blue">{"color": "rgb(255, 255, 255)", "backgroundColor": "rgb(0, 0, 255)"}</option>' +
   '  <option value="Three" class="green">{"color": "rgb(128, 0, 128)", "backgroundColor": "rgb(0, 128, 0)"}</option>' +
+  '  <option value="Four" class="defaultColor defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
+  '  <option value="Five" class="defaultColor">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
+  '  <option value="Six" class="defaultBackground">{"color": "-moz-ComboboxText", "backgroundColor": "transparent", "unstyled": "true"}</option>' +
   "</select></body></html>";
 
 function openSelectPopup(selectPopup, mode = "key", selector = "select", win = window) {
   let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
 
   if (mode == "click" || mode == "mousedown") {
     let mousePromise;
     if (mode == "click") {
@@ -733,86 +741,58 @@ add_task(function* test_somehidden() {
     child = child.nextSibling;
   }
 
   yield hideSelectPopup(selectPopup, "escape");
   yield BrowserTestUtils.removeTab(tab);
 });
 
 add_task(function* test_colors_applied_to_popup() {
-  function inverseRGBString(rgbString) {
-    let [, r, g, b] = rgbString.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
-    return `rgb(${255 - r}, ${255 - g}, ${255 - b})`;
-  }
-
   const pageUrl = "data:text/html," + escape(PAGECONTENT_COLORS);
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
 
   let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
 
   let popupShownPromise = BrowserTestUtils.waitForEvent(selectPopup, "popupshown");
   yield BrowserTestUtils.synthesizeMouseAtCenter("#one", { type: "mousedown" }, gBrowser.selectedBrowser);
   yield popupShownPromise;
 
   // The label contains a JSON string of the expected colors for
   // `color` and `background-color`.
-  is(selectPopup.parentNode.itemCount, 3, "Correct number of items");
+  is(selectPopup.parentNode.itemCount, 6, "Correct number of items");
   let child = selectPopup.firstChild;
   let idx = 1;
 
   ok(child.selected, "The first child should be selected");
   while (child) {
-    let expectedColors = JSON.parse(child.label);
-
-    // We need to use Canvas here to get the actual pixel color
-    // because the computedStyle will only tell us the 'color' or
-    // 'backgroundColor' of the element, but not what the displayed
-    // color is due to composition of various CSS rules such as
-    // 'filter' which is applied when elements have custom background
-    // or foreground elements.
-    let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-    canvas = document.documentElement.appendChild(canvas);
-    let rect = child.getBoundingClientRect();
-    canvas.setAttribute("width", rect.width);
-    canvas.setAttribute("height", rect.height);
-    canvas.mozOpaque = true;
+    let expected = JSON.parse(child.label);
 
-    let ctx = canvas.getContext("2d");
-    ctx.drawWindow(window, rect.x + rect.left, rect.y + rect.top, rect.width, rect.height, "#000", ctx.DRAWWINDOW_USE_WIDGET_LAYERS);
-    let frame = ctx.getImageData(0, 0, rect.width, rect.height);
-
-    let pixels = frame.data.length / 4;
-    // Assume the inverse backgroundColor is the color of the first pixel.
-    let [inverseBgR, inverseBgG, inverseBgB] = frame.data;
-    let inverseBackgroundColor = `rgb(${inverseBgR}, ${inverseBgG}, ${inverseBgB})`;
-    // Use the next different pixel color as the foreground color, assuming
-    // no anti-aliasing.
-    let inverseColor = inverseBackgroundColor;
-    for (let i = 0; i < pixels; i++) {
-      if (inverseBgR != frame.data[i * 4 + 0] &&
-          inverseBgG != frame.data[i * 4 + 1] &&
-          inverseBgB != frame.data[i * 4 + 2]) {
-        inverseColor = `rgb(${frame.data[i * 4 + 0]}, ${frame.data[i * 4 + 1]}, ${frame.data[i * 4 + 2]})`;
+    for (let color of Object.keys(expected)) {
+      if (color.toLowerCase().includes("color") &&
+          !expected[color].startsWith("rgb")) {
+        // Need to convert system color to RGB color.
+        let textarea = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea");
+        textarea.style.color = expected[color];
+        expected[color] = getComputedStyle(textarea).color;
       }
     }
-    // The canvas code above isn't getting the right colors for the pixels,
-    // it always returns rgb(255,255,255).
-    todo_is(inverseColor, inverseRGBString(getComputedStyle(child).color),
-      "Item " + (idx) + " has correct inverse foreground color when selected");
-    todo_is(inverseBackgroundColor, inverseRGBString(getComputedStyle(child).backgroundColor),
-      "Item " + (idx) + " has correct inverse background color when selected");
-
-    canvas.remove();
 
     // Press Down to move the selected item to the next item in the
     // list and check the colors of this item when it's not selected.
     EventUtils.synthesizeKey("KEY_ArrowDown", { code: "ArrowDown" });
 
-    is(getComputedStyle(child).color, expectedColors.color,
-       "Item " + (idx) + " has correct foreground color");
-    is(getComputedStyle(child).backgroundColor, expectedColors.backgroundColor,
-       "Item " + (idx++) + " has correct background color");
+    if (expected.unstyled) {
+      ok(!child.hasAttribute("customoptionstyling"),
+        `Item ${idx} should not have any custom option styling`);
+    } else {
+      is(getComputedStyle(child).color, expected.color,
+         "Item " + (idx) + " has correct foreground color");
+      is(getComputedStyle(child).backgroundColor, expected.backgroundColor,
+         "Item " + (idx) + " has correct background color");
+    }
+
+    idx++;
     child = child.nextSibling;
   }
 
   yield hideSelectPopup(selectPopup, "escape");
   yield BrowserTestUtils.removeTab(tab);
 });
--- a/browser/components/contextualidentity/test/browser/browser_eme.js
+++ b/browser/components/contextualidentity/test/browser/browser_eme.js
@@ -81,17 +81,16 @@ function generateKeyInfo(aData) {
   return keyInfo;
 }
 
 add_task(function* setup() {
   // Make sure userContext is enabled.
   yield SpecialPowers.pushPrefEnv({"set": [
     [ "privacy.userContext.enabled", true ],
     [ "media.mediasource.enabled", true ],
-    [ "media.eme.apiVisible", true ],
     [ "media.mediasource.webm.enabled", true ],
     [ "media.clearkey.persistent-license.enabled", true ],
   ]});
 });
 
 add_task(function* test() {
   // Open a tab with the default container.
   let defaultContainer = yield openTabInUserContext(TEST_URL + "empty_file.html", USER_ID_DEFAULT);
--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
@@ -174,17 +174,16 @@ function* checkEMEKey(browser, emeSessio
 // Test functions.
 //
 
 add_task(function* setup() {
   // Make sure userContext is enabled.
   yield SpecialPowers.pushPrefEnv({"set": [
       [ "privacy.userContext.enabled", true ],
       [ "media.mediasource.enabled", true ],
-      [ "media.eme.apiVisible", true ],
       [ "media.mediasource.webm.enabled", true ],
       [ "media.clearkey.persistent-license.enabled", true ],
   ]});
 });
 
 add_task(function* test_EME_forgetThisSite() {
   let tabs = [];
   let emeSessionIds = [];
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -490,17 +490,19 @@
         // received focus, ignore the next focus event.
         this._ignoreFocus = (document.activeElement == this._textbox.inputField);
       ]]></handler>
 
       <handler event="focus">
       <![CDATA[
         // Speculatively connect to the current engine's search URI (and
         // suggest URI, if different) to reduce request latency
-        this.currentEngine.speculativeConnect({window});
+        this.currentEngine.speculativeConnect({window,
+                                               originAttributes: gBrowser.contentPrincipal
+                                                                         .originAttributes});
 
         if (this._ignoreFocus) {
           // This window has been re-focused, don't show the suggestions
           this._ignoreFocus = false;
           return;
         }
 
         // Don't open the suggestions if there is no text in the textbox.
--- a/browser/components/sessionstore/PrivacyFilter.jsm
+++ b/browser/components/sessionstore/PrivacyFilter.jsm
@@ -6,17 +6,17 @@
 
 this.EXPORTED_SYMBOLS = ["PrivacyFilter"];
 
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
-  "resource:///modules/sessionstore/PrivacyLevel.jsm");
+  "resource://gre/modules/sessionstore/PrivacyLevel.jsm");
 
 /**
  * A module that provides methods to filter various kinds of data collected
  * from a tab by the current privacy level as set by the user.
  */
 this.PrivacyFilter = Object.freeze({
   /**
    * Filters the given (serialized) session storage |data| according to the
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -10,17 +10,17 @@ const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm", this);
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
   "resource://gre/modules/sessionstore/Utils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivacyLevel",
-  "resource:///modules/sessionstore/PrivacyLevel.jsm");
+  "resource://gre/modules/sessionstore/PrivacyLevel.jsm");
 
 // MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision.
 const MAX_EXPIRY = Math.pow(2, 62);
 
 /**
  * The external API implemented by the SessionCookies module.
  */
 this.SessionCookies = Object.freeze({
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -24,17 +24,16 @@ EXTRA_COMPONENTS += [
 
 EXTRA_JS_MODULES.sessionstore = [
     'ContentRestore.jsm',
     'DocShellCapabilities.jsm',
     'FrameTree.jsm',
     'GlobalState.jsm',
     'PageStyle.jsm',
     'PrivacyFilter.jsm',
-    'PrivacyLevel.jsm',
     'RecentlyClosedTabsAndWindowsMenuUtils.jsm',
     'RunState.jsm',
     'SessionCookies.jsm',
     'SessionFile.jsm',
     'SessionHistory.jsm',
     'SessionMigration.jsm',
     'SessionSaver.jsm',
     'SessionStorage.jsm',
--- a/browser/config/mozconfigs/macosx64/debug-static-analysis
+++ b/browser/config/mozconfigs/macosx64/debug-static-analysis
@@ -1,12 +1,30 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_PACKAGE_TESTS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
+# The toolchain installed on our OSX 10.7 build machines is too old to support
+# MachO LC_DATA_IN_CODE load command, which newer LLVM generates, so we need to
+# use a newer toolchain that we build.
+#
+# Unfortunately setting $PATH is not enough, because the build system hardcodes
+# the default values for some of the build tools, which we also need to
+# override below.  The default value for host ar and host ranlib is also
+# hardcoded so we need to override those separately.
+CCTOOLS_DIR="$topsrcdir/cctools/bin"
+export PATH="$CCTOOLS_DIR:$PATH"
+export AR="$CCTOOLS_DIR/ar"
+export HOST_AR="$CCTOOLS_DIR/ar"
+export RANLIB="$CCTOOLS_DIR/ranlib"
+export HOST_RANLIB="$CCTOOLS_DIR/ranlib"
+export LIPO="$CCTOOLS_DIR/lipo"
+export OTOOL="$CCTOOLS_DIR/otool"
+export STRIP="$CCTOOLS_DIR/strip"
+
 . $topsrcdir/build/macosx/mozconfig.common
 
 ac_add_options --enable-debug
 ac_add_options --enable-dmd
 
 ac_add_options --enable-clang-plugin
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.8.0",
-"size": 133060926,
-"digest": "aff5ad3ac2d41db19d1ba0df5f97b189a7d7e1b6af8c56e22c2b0cced84d75fa98394ded6a4ba5713652e6684a0a46f47aeccf87991f9e849bf8d7d82e564f6f",
+"version": "clang 3.9.0",
+"size": 184678304,
+"digest": "cfde9a0f7f59823200f94422b4adb9a2fb5d4d07f240bbd1142c792434f6a1cbb4096d25c9853d77008fc40db0d827daa7003e78016f51241f621d6040ccc635",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "version": "rustc 1.14.0 (e8a012324 2016-12-16) repack",
 "size": 118864812,
 "digest": "649900813154b21c51c129050fde791e9974083936c9ef002dc18e1c6f8b9ad9ecfbdb5dd580e87743fc2bebed9b64ef9906461e60ec2a1e5277f3c213f417c9",
@@ -20,16 +20,16 @@
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "sccache2.tar.bz2",
 "unpack": true,
 "digest": "a285c7c6468ad7438262dfec90f65981e84abf2adbb1aa075c0ec1759b4f98ce5d5f14a3d555274f970704210a00738ba7d95db2fc320f7780e6b99bcb0ffb6c",
 "size": 1143715
 },
 {
-"version": "cctools port from commit hash db1f8d906cb28, ld only",
-"size": 634496,
-"digest": "037f31fcf29e7bb7fada0d2bdd5e95c7d4cb2692f2a5c98ed6f6a7561b9d81622d015f0d12b291d3667719655f1369e8ce8a0a4a4773aa0ee4753e04a8821173",
+"version": "cctools port from commit hash 84ce22dbb22a26ce7f392e9de0ee39c2efe6fd68",
+"size": 2174783,
+"digest": "8678348faff8f344b377075007975ae77a55a2a73488e36950a43c8ec27a79970cd8e34003e33e756a57d9cbf5c3e2e4461184102c6c03f793377a4d250a7f24",
 "algorithm": "sha512",
 "filename": "cctools.tar.bz2",
 "unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/cross-clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/cross-clang.manifest
@@ -11,22 +11,22 @@
 "version": "gcc 4.9.4 + PR64905",
 "size": 101297752,
 "digest": "42aa2e3fdd232b5e390472a788e7f7db71a1fee4221e260b6cb58c9a1d73e6cdd10afcbac137f7844290169cd6b561b424ecc92b159e9726b0ad5de3f478a8be",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz",
 "unpack": true
 },
 {
-"size": 3008804,
+"size": 1349196,
 "visibility": "public",
-"digest": "ba6937f14f3d8b26dcb2d39490dee6b0a8afb60f672f5debb71d7b62c1ec52103201b4b1a3d258f945567de531384b36ddb2ce4aa73dc63d72305b11c146847c",
+"digest": "438a36523a74cbc4a58226647e0501fa64a1b63f32931dc364a75d699df8accb45afdf26f4cb61c6ac7f58be530205acb6da22008bec19603c6f6fda3a12a8cc",
 "algorithm": "sha512",
 "unpack": true,
-"filename": "cctools.tar.gz"
+"filename": "cctools.tar.xz"
 },
 {
 "size": 30823112,
 "visibility": "internal",
 "digest": "0c58e06a3ea8f4641c991a7406fc8733c574f0b4aa773bce0feaa5468d2b8440fa33cea056e16fad2b8a7ef4409ca7228113eb12adc87c3e115129d8a3b3b565",
 "algorithm": "sha512",
 "unpack": true,
 "filename": "MacOSX10.10.sdk.tar.xz"
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -9,21 +9,23 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/UpdateUtils.jsm");
 
  // The amount of people to be part of e10s
 const TEST_THRESHOLD = {
   "beta"    : 0.5,  // 50%
   "release" : 1.0,  // 100%
+  "esr"     : 1.0,  // 100%
 };
 
 const ADDON_ROLLOUT_POLICY = {
   "beta"    : "51alladdons", // Any WebExtension or addon except with mpc = false
   "release" : "51set1",
+  "esr"     : "esrA", // WebExtensions and Addons with mpc=true
 };
 
 const PREF_COHORT_SAMPLE       = "e10s.rollout.cohortSample";
 const PREF_COHORT_NAME         = "e10s.rollout.cohort";
 const PREF_E10S_OPTED_IN       = "browser.tabs.remote.autostart";
 const PREF_E10S_FORCE_ENABLED  = "browser.tabs.remote.force-enable";
 const PREF_E10S_FORCE_DISABLED = "browser.tabs.remote.force-disable";
 const PREF_TOGGLE_E10S         = "browser.tabs.remote.autostart.2";
--- a/browser/extensions/e10srollout/install.rdf.in
+++ b/browser/extensions/e10srollout/install.rdf.in
@@ -5,17 +5,17 @@
 
 #filter substitution
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:id>e10srollout@mozilla.org</em:id>
-    <em:version>1.8</em:version>
+    <em:version>1.9</em:version>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:multiprocessCompatible>true</em:multiprocessCompatible>
 
     <!-- Target Application this theme can install into,
         with minimum and maximum supported versions. -->
     <em:targetApplication>
       <Description>
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,3 +1,3 @@
 This is the pdf.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 1.7.235
+Current extension version is: 1.7.242
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -18,18 +18,18 @@
   define('pdfjs-dist/build/pdf', ['exports'], factory);
  } else if (typeof exports !== 'undefined') {
   factory(exports);
  } else {
   factory(root['pdfjsDistBuildPdf'] = {});
  }
 }(this, function (exports) {
  'use strict';
- var pdfjsVersion = '1.7.235';
- var pdfjsBuild = '3f320f0b';
+ var pdfjsVersion = '1.7.242';
+ var pdfjsBuild = '6f0cf8c4';
  var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
  var pdfjsLibs = {};
  (function pdfjsWrapper() {
   (function (root, factory) {
    factory(root.pdfjsSharedUtil = {});
   }(this, function (exports) {
    var globalScope = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this;
    var FONT_IDENTITY_MATRIX = [
@@ -1783,16 +1783,17 @@
    factory(root.pdfjsDisplaySVG = {}, root.pdfjsSharedUtil);
   }(this, function (exports, sharedUtil) {
   }));
   (function (root, factory) {
    factory(root.pdfjsDisplayAnnotationLayer = {}, root.pdfjsSharedUtil, root.pdfjsDisplayDOMUtils);
   }(this, function (exports, sharedUtil, displayDOMUtils) {
    var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
    var AnnotationType = sharedUtil.AnnotationType;
+   var stringToPDFString = sharedUtil.stringToPDFString;
    var Util = sharedUtil.Util;
    var addLinkAttributes = displayDOMUtils.addLinkAttributes;
    var LinkTarget = displayDOMUtils.LinkTarget;
    var getFilenameFromUrl = displayDOMUtils.getFilenameFromUrl;
    var warn = sharedUtil.warn;
    var CustomStyle = displayDOMUtils.CustomStyle;
    var getDefaultSetting = displayDOMUtils.getDefaultSetting;
    function AnnotationElementFactory() {
@@ -2325,18 +2326,24 @@
       return this.container;
      }
     });
     return StrikeOutAnnotationElement;
    }();
    var FileAttachmentAnnotationElement = function FileAttachmentAnnotationElementClosure() {
     function FileAttachmentAnnotationElement(parameters) {
      AnnotationElement.call(this, parameters, true);
-     this.filename = getFilenameFromUrl(parameters.data.file.filename);
-     this.content = parameters.data.file.content;
+     var file = this.data.file;
+     this.filename = getFilenameFromUrl(file.filename);
+     this.content = file.content;
+     this.linkService.onFileAttachmentAnnotation({
+      id: stringToPDFString(file.filename),
+      filename: file.filename,
+      content: file.content
+     });
     }
     Util.inherit(FileAttachmentAnnotationElement, AnnotationElement, {
      render: function FileAttachmentAnnotationElement_render() {
       this.container.className = 'fileAttachmentAnnotation';
       var trigger = document.createElement('div');
       trigger.style.height = this.container.style.height;
       trigger.style.width = this.container.style.width;
       trigger.addEventListener('dblclick', this._download.bind(this));
@@ -2360,27 +2367,26 @@
     return {
      render: function AnnotationLayer_render(parameters) {
       var annotationElementFactory = new AnnotationElementFactory();
       for (var i = 0, ii = parameters.annotations.length; i < ii; i++) {
        var data = parameters.annotations[i];
        if (!data) {
         continue;
        }
-       var properties = {
+       var element = annotationElementFactory.create({
         data: data,
         layer: parameters.div,
         page: parameters.page,
         viewport: parameters.viewport,
         linkService: parameters.linkService,
         downloadManager: parameters.downloadManager,
         imageResourcesPath: parameters.imageResourcesPath || getDefaultSetting('imageResourcesPath'),
         renderInteractiveForms: parameters.renderInteractiveForms || false
-       };
-       var element = annotationElementFactory.create(properties);
+       });
        if (element.isRenderable) {
         parameters.div.appendChild(element.render());
        }
       }
      },
      update: function AnnotationLayer_update(parameters) {
       for (var i = 0, ii = parameters.annotations.length; i < ii; i++) {
        var data = parameters.annotations[i];
@@ -5560,17 +5566,16 @@
   }));
   (function (root, factory) {
    factory(root.pdfjsDisplayAPI = {}, root.pdfjsSharedUtil, root.pdfjsDisplayFontLoader, root.pdfjsDisplayCanvas, root.pdfjsDisplayMetadata, root.pdfjsDisplayDOMUtils);
   }(this, function (exports, sharedUtil, displayFontLoader, displayCanvas, displayMetadata, displayDOMUtils, amdRequire) {
    var InvalidPDFException = sharedUtil.InvalidPDFException;
    var MessageHandler = sharedUtil.MessageHandler;
    var MissingPDFException = sharedUtil.MissingPDFException;
    var PageViewport = sharedUtil.PageViewport;
-   var PasswordResponses = sharedUtil.PasswordResponses;
    var PasswordException = sharedUtil.PasswordException;
    var StatTimer = sharedUtil.StatTimer;
    var UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
    var UnknownErrorException = sharedUtil.UnknownErrorException;
    var Util = sharedUtil.Util;
    var createPromiseCapability = sharedUtil.createPromiseCapability;
    var error = sharedUtil.error;
    var deprecated = sharedUtil.deprecated;
@@ -6973,16 +6978,17 @@
  exports.build = pdfjsLibs.pdfjsDisplayAPI.build;
  exports.version = pdfjsLibs.pdfjsDisplayAPI.version;
  exports.getDocument = pdfjsLibs.pdfjsDisplayAPI.getDocument;
  exports.PDFDataRangeTransport = pdfjsLibs.pdfjsDisplayAPI.PDFDataRangeTransport;
  exports.PDFWorker = pdfjsLibs.pdfjsDisplayAPI.PDFWorker;
  exports.renderTextLayer = pdfjsLibs.pdfjsDisplayTextLayer.renderTextLayer;
  exports.AnnotationLayer = pdfjsLibs.pdfjsDisplayAnnotationLayer.AnnotationLayer;
  exports.CustomStyle = pdfjsLibs.pdfjsDisplayDOMUtils.CustomStyle;
+ exports.createPromiseCapability = pdfjsLibs.pdfjsSharedUtil.createPromiseCapability;
  exports.PasswordResponses = pdfjsLibs.pdfjsSharedUtil.PasswordResponses;
  exports.InvalidPDFException = pdfjsLibs.pdfjsSharedUtil.InvalidPDFException;
  exports.MissingPDFException = pdfjsLibs.pdfjsSharedUtil.MissingPDFException;
  exports.SVGGraphics = pdfjsLibs.pdfjsDisplaySVG.SVGGraphics;
  exports.UnexpectedResponseException = pdfjsLibs.pdfjsSharedUtil.UnexpectedResponseException;
  exports.OPS = pdfjsLibs.pdfjsSharedUtil.OPS;
  exports.UNSUPPORTED_FEATURES = pdfjsLibs.pdfjsSharedUtil.UNSUPPORTED_FEATURES;
  exports.isValidUrl = pdfjsLibs.pdfjsDisplayDOMUtils.isValidUrl;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -18,18 +18,18 @@
   define('pdfjs-dist/build/pdf.worker', ['exports'], factory);
  } else if (typeof exports !== 'undefined') {
   factory(exports);
  } else {
   factory(root['pdfjsDistBuildPdfWorker'] = {});
  }
 }(this, function (exports) {
  'use strict';
- var pdfjsVersion = '1.7.235';
- var pdfjsBuild = '3f320f0b';
+ var pdfjsVersion = '1.7.242';
+ var pdfjsBuild = '6f0cf8c4';
  var pdfjsFilePath = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : null;
  var pdfjsLibs = {};
  (function pdfjsWrapper() {
   (function (root, factory) {
    factory(root.pdfjsCoreArithmeticDecoder = {});
   }(this, function (exports) {
    var ArithmeticDecoder = function ArithmeticDecoderClosure() {
     var QeTable = [
@@ -6075,17 +6075,16 @@
       var end = pos;
       var raw = bytes.subarray(start, end);
       return new CFFCharset(false, format, charset, raw);
      },
      parseEncoding: function CFFParser_parseEncoding(pos, properties, strings, charset) {
       var encoding = Object.create(null);
       var bytes = this.bytes;
       var predefined = false;
-      var hasSupplement = false;
       var format, i, ii;
       var raw = null;
       function readSupplement() {
        var supplementsCount = bytes[pos++];
        for (i = 0; i < supplementsCount; i++) {
         var code = bytes[pos++];
         var sid = (bytes[pos++] << 8) + (bytes[pos++] & 0xff);
         encoding[code] = charset.indexOf(strings.get(sid));
@@ -6125,17 +6124,16 @@
        default:
         error('Unknown encoding format: ' + format + ' in CFF');
         break;
        }
        var dataEnd = pos;
        if (format & 0x80) {
         bytes[dataStart] &= 0x7f;
         readSupplement();
-        hasSupplement = true;
        }
        raw = bytes.subarray(dataStart, dataEnd);
       }
       format = format & 0x7f;
       return new CFFEncoding(predefined, format, encoding, raw);
      },
      parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
       var start = pos;
@@ -7439,18 +7437,17 @@
        }
        return missingChunks;
       };
       var subStream = new ChunkedStreamSubstream();
       subStream.pos = subStream.start = start;
       subStream.end = start + length || this.end;
       subStream.dict = dict;
       return subStream;
-     },
-     isStream: true
+     }
     };
     return ChunkedStream;
    }();
    var ChunkedStreamManager = function ChunkedStreamManagerClosure() {
     function ChunkedStreamManager(pdfNetworkStream, args) {
      var chunkSize = args.rangeChunkSize;
      var length = args.length;
      this.stream = new ChunkedStream(length, chunkSize, this);
@@ -12843,24 +12840,22 @@
      var currentHeight = 0;
      var symbolCodeLength = log2(symbols.length + numberOfNewSymbols);
      var decoder = decodingContext.decoder;
      var contextCache = decodingContext.contextCache;
      while (newSymbols.length < numberOfNewSymbols) {
       var deltaHeight = decodeInteger(contextCache, 'IADH', decoder);
       currentHeight += deltaHeight;
       var currentWidth = 0;
-      var totalWidth = 0;
       while (true) {
        var deltaWidth = decodeInteger(contextCache, 'IADW', decoder);
        if (deltaWidth === null) {
         break;
        }
        currentWidth += deltaWidth;
-       totalWidth += currentWidth;
        var bitmap;
        if (refinement) {
         var numberOfInstances = decodeInteger(contextCache, 'IAAI', decoder);
         if (numberOfInstances > 1) {
          bitmap = decodeTextRegion(huffman, refinement, currentWidth, currentHeight, 0, numberOfInstances, 1, symbols.concat(newSymbols), symbolCodeLength, 0, 0, 1, 0, huffmanTables, refinementTemplateIndex, refinementAt, decodingContext);
         } else {
          var symbolId = decodeIAID(contextCache, decoder, symbolCodeLength);
          var rdx = decodeInteger(contextCache, 'IARDX', decoder);
@@ -13260,17 +13255,17 @@
        height: readUint32(data, position + 4),
        resolutionX: readUint32(data, position + 8),
        resolutionY: readUint32(data, position + 12)
       };
       if (pageInfo.height === 0xFFFFFFFF) {
        delete pageInfo.height;
       }
       var pageSegmentFlags = data[position + 16];
-      var pageStripingInformation = readUint16(data, position + 17);
+      readUint16(data, position + 17);
       pageInfo.lossless = !!(pageSegmentFlags & 1);
       pageInfo.refinement = !!(pageSegmentFlags & 2);
       pageInfo.defaultPixelValue = pageSegmentFlags >> 2 & 1;
       pageInfo.combinationOperator = pageSegmentFlags >> 3 & 3;
       pageInfo.requiresBuffer = !!(pageSegmentFlags & 32);
       pageInfo.combinationOperatorOverride = !!(pageSegmentFlags & 64);
       args = [pageInfo];
       break;
@@ -13303,17 +13298,17 @@
      var header = {};
      position += 8;
      var flags = data[position++];
      header.randomAccess = !(flags & 1);
      if (!(flags & 2)) {
       header.numberOfPages = readUint32(data, position);
       position += 4;
      }
-     var segments = readSegments(header, data, position, end);
+     readSegments(header, data, position, end);
      error('Not implemented');
     }
     function parseJbig2Chunks(chunks) {
      var visitor = new SimpleSegmentVisitor();
      for (var i = 0, ii = chunks.length; i < ii; i++) {
       var chunk = chunks[i];
       var segments = readSegments({}, chunk.data, chunk.start, chunk.end);
       processSegments(segments, visitor);
@@ -14140,17 +14135,17 @@
          (huffmanTableSpec >> 4 === 0 ? huffmanTablesDC : huffmanTablesAC)[huffmanTableSpec & 15] = buildHuffmanTable(codeLengths, huffmanValues);
         }
         break;
        case 0xFFDD:
         readUint16();
         resetInterval = readUint16();
         break;
        case 0xFFDA:
-        var scanLength = readUint16();
+        readUint16();
         var selectorsCount = data[offset++];
         var components = [], component;
         for (i = 0; i < selectorsCount; i++) {
          var componentIndex = frame.componentIds[data[offset++]];
          component = frame.components[componentIndex];
          var tableSpec = data[offset++];
          component.huffmanTableDC = huffmanTablesDC[tableSpec >> 4];
          component.huffmanTableAC = huffmanTablesAC[tableSpec & 15];
@@ -22555,17 +22550,16 @@
   }(this, function (exports, sharedUtil, corePrimitives, coreJbig2, coreJpg, coreJpx) {
    var Util = sharedUtil.Util;
    var error = sharedUtil.error;
    var info = sharedUtil.info;
    var isInt = sharedUtil.isInt;
    var isArray = sharedUtil.isArray;
    var createObjectURL = sharedUtil.createObjectURL;
    var shadow = sharedUtil.shadow;
-   var warn = sharedUtil.warn;
    var isSpace = sharedUtil.isSpace;
    var Dict = corePrimitives.Dict;
    var isDict = corePrimitives.isDict;
    var isStream = corePrimitives.isStream;
    var Jbig2Image = coreJbig2.Jbig2Image;
    var JpegImage = coreJpg.JpegImage;
    var JpxImage = coreJpx.JpxImage;
    var Stream = function StreamClosure() {
@@ -22637,18 +22631,17 @@
      reset: function Stream_reset() {
       this.pos = this.start;
      },
      moveStart: function Stream_moveStart() {
       this.start = this.pos;
      },
      makeSubStream: function Stream_makeSubStream(start, length, dict) {
       return new Stream(this.bytes.buffer, start, length, dict);
-     },
-     isStream: true
+     }
     };
     return Stream;
    }();
    var StringStream = function StringStreamClosure() {
     function StringStream(str) {
      var length = str.length;
      var bytes = new Uint8Array(length);
      for (var n = 0; n < length; ++n) {
@@ -31215,274 +31208,16 @@
        output.set(result[i], j);
       }
       return output;
      }
     };
     return AES128Cipher;
    }();
    var AES256Cipher = function AES256CipherClosure() {
-    var rcon = new Uint8Array([
-     0x8d,
-     0x01,
-     0x02,
-     0x04,
-     0x08,
-     0x10,
-     0x20,
-     0x40,
-     0x80,
-     0x1b,
-     0x36,
-     0x6c,
-     0xd8,
-     0xab,
-     0x4d,
-     0x9a,
-     0x2f,
-     0x5e,
-     0xbc,
-     0x63,
-     0xc6,
-     0x97,
-     0x35,
-     0x6a,
-     0xd4,
-     0xb3,
-     0x7d,
-     0xfa,
-     0xef,
-     0xc5,
-     0x91,
-     0x39,
-     0x72,
-     0xe4,
-     0xd3,
-     0xbd,
-     0x61,
-     0xc2,
-     0x9f,
-     0x25,
-     0x4a,
-     0x94,
-     0x33,
-     0x66,
-     0xcc,
-     0x83,
-     0x1d,
-     0x3a,
-     0x74,
-     0xe8,
-     0xcb,
-     0x8d,
-     0x01,
-     0x02,
-     0x04,
-     0x08,
-     0x10,
-     0x20,
-     0x40,
-     0x80,
-     0x1b,
-     0x36,
-     0x6c,
-     0xd8,
-     0xab,
-     0x4d,
-     0x9a,
-     0x2f,
-     0x5e,
-     0xbc,
-     0x63,
-     0xc6,
-     0x97,
-     0x35,
-     0x6a,
-     0xd4,
-     0xb3,
-     0x7d,
-     0xfa,
-     0xef,
-     0xc5,
-     0x91,
-     0x39,
-     0x72,
-     0xe4,
-     0xd3,
-     0xbd,
-     0x61,
-     0xc2,
-     0x9f,
-     0x25,
-     0x4a,
-     0x94,
-     0x33,
-     0x66,
-     0xcc,
-     0x83,
-     0x1d,
-     0x3a,
-     0x74,
-     0xe8,
-     0xcb,
-     0x8d,
-     0x01,
-     0x02,
-     0x04,
-     0x08,
-     0x10,
-     0x20,
-     0x40,
-     0x80,
-     0x1b,
-     0x36,
-     0x6c,
-     0xd8,
-     0xab,
-     0x4d,
-     0x9a,
-     0x2f,
-     0x5e,
-     0xbc,
-     0x63,
-     0xc6,
-     0x97,
-     0x35,
-     0x6a,
-     0xd4,
-     0xb3,
-     0x7d,
-     0xfa,
-     0xef,
-     0xc5,
-     0x91,
-     0x39,
-     0x72,
-     0xe4,
-     0xd3,
-     0xbd,
-     0x61,
-     0xc2,
-     0x9f,
-     0x25,
-     0x4a,
-     0x94,
-     0x33,
-     0x66,
-     0xcc,
-     0x83,
-     0x1d,
-     0x3a,
-     0x74,
-     0xe8,
-     0xcb,
-     0x8d,
-     0x01,
-     0x02,
-     0x04,
-     0x08,
-     0x10,
-     0x20,
-     0x40,
-     0x80,
-     0x1b,
-     0x36,
-     0x6c,
-     0xd8,
-     0xab,
-     0x4d,
-     0x9a,
-     0x2f,
-     0x5e,
-     0xbc,
-     0x63,
-     0xc6,
-     0x97,
-     0x35,
-     0x6a,
-     0xd4,
-     0xb3,
-     0x7d,
-     0xfa,
-     0xef,
-     0xc5,
-     0x91,
-     0x39,
-     0x72,
-     0xe4,
-     0xd3,
-     0xbd,
-     0x61,
-     0xc2,
-     0x9f,
-     0x25,
-     0x4a,
-     0x94,
-     0x33,
-     0x66,
-     0xcc,
-     0x83,
-     0x1d,
-     0x3a,
-     0x74,
-     0xe8,
-     0xcb,
-     0x8d,
-     0x01,
-     0x02,
-     0x04,
-     0x08,
-     0x10,
-     0x20,
-     0x40,
-     0x80,
-     0x1b,
-     0x36,
-     0x6c,
-     0xd8,
-     0xab,
-     0x4d,
-     0x9a,
-     0x2f,
-     0x5e,
-     0xbc,
-     0x63,
-     0xc6,
-     0x97,
-     0x35,
-     0x6a,
-     0xd4,
-     0xb3,
-     0x7d,
-     0xfa,
-     0xef,
-     0xc5,
-     0x91,
-     0x39,
-     0x72,
-     0xe4,
-     0xd3,
-     0xbd,
-     0x61,
-     0xc2,
-     0x9f,
-     0x25,
-     0x4a,
-     0x94,
-     0x33,
-     0x66,
-     0xcc,
-     0x83,
-     0x1d,
-     0x3a,
-     0x74,
-     0xe8,
-     0xcb,
-     0x8d
-    ]);
     var s = new Uint8Array([
      0x63,
      0x7c,
      0x77,
      0x7b,
      0xf2,
      0x6b,
      0x6f,
@@ -33031,19 +32766,19 @@
      return data[offset] << 24 | data[offset + 1] << 16 | data[offset + 2] << 8 | data[offset + 3];
     }
     function getUshort(data, offset) {
      return data[offset] << 8 | data[offset + 1];
     }
     function parseCmap(data, start, end) {
      var offset = getUshort(data, start + 2) === 1 ? getLong(data, start + 8) : getLong(data, start + 16);
      var format = getUshort(data, start + offset);
-     var length, ranges, p, i;
+     var ranges, p, i;
      if (format === 4) {
-      length = getUshort(data, start + offset + 2);
+      getUshort(data, start + offset + 2);
       var segCount = getUshort(data, start + offset + 6) >> 1;
       p = start + offset + 14;
       ranges = [];
       for (i = 0; i < segCount; i++, p += 2) {
        ranges[i] = { end: getUshort(data, p) };
       }
       p += 2;
       for (i = 0; i < segCount; i++, p += 2) {
@@ -33060,17 +32795,17 @@
        ranges[i].ids = [];
        for (var j = 0, jj = ranges[i].end - ranges[i].start + 1; j < jj; j++) {
         ranges[i].ids[j] = getUshort(data, p + idOffset);
         idOffset += 2;
        }
       }
       return ranges;
      } else if (format === 12) {
-      length = getLong(data, start + offset + 4);
+      getLong(data, start + offset + 4);
       var groups = getLong(data, start + offset + 12);
       p = start + offset + 16;
       ranges = [];
       for (i = 0; i < groups; i++) {
        ranges.push({
         start: getLong(data, p),
         end: getLong(data, p + 4),
         idDelta: getLong(data, p + 8) - getLong(data, p)
@@ -36157,17 +35892,17 @@
           error = this.executeCommand(4, COMMAND_MAP.endchar);
          }
          break;
         case (12 << 8) + 7:
          if (this.stack.length < 4) {
           error = true;
           break;
          }
-         var wy = this.stack.pop();
+         this.stack.pop();
          wx = this.stack.pop();
          var sby = this.stack.pop();
          sbx = this.stack.pop();
          this.lsb = sbx;
          this.width = wx;
          this.stack.push(wx, sbx, sby);
          error = this.executeCommand(3, COMMAND_MAP.rmoveto);
          break;
@@ -36409,17 +36144,17 @@
          }
          charstrings.push({
           glyph: glyph,
           encoded: encoded
          });
         }
         break;
        case 'Subrs':
-        var num = this.readInt();
+        this.readInt();
         this.getToken();
         while ((token = this.getToken()) === 'dup') {
          var index = this.readInt();
          length = this.readInt();
          this.getToken();
          data = stream.makeSubStream(stream.pos, length);
          lenIV = program.properties.privateData['lenIV'];
          encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, lenIV);
@@ -39062,19 +38797,16 @@
     function Font(name, file, properties) {
      var charCode, glyphName, unicode;
      this.name = name;
      this.loadedName = properties.loadedName;
      this.isType3Font = properties.isType3Font;
      this.sizes = [];
      this.missingFile = false;
      this.glyphCache = Object.create(null);
-     var names = name.split('+');
-     names = names.length > 1 ? names[1] : names[0];
-     names = names.split(/[-,_]/g)[0];
      this.isSerifFont = !!(properties.flags & FontFlags.Serif);
      this.isSymbolicFont = !!(properties.flags & FontFlags.Symbolic);
      this.isMonospace = !!(properties.flags & FontFlags.FixedPitch);
      var type = properties.type;
      var subtype = properties.subtype;
      this.type = type;
      this.fallbackName = this.isMonospace ? 'monospace' : this.isSerifFont ? 'serif' : 'sans-serif';
      this.differences = properties.differences;
@@ -39652,17 +39384,17 @@
          encodingId: -1,
          mappings: [],
          hasShortCmap: false
         };
        }
        var segment;
        var start = (font.start ? font.start : 0) + cmap.offset;
        font.pos = start;
-       var version = font.getUint16();
+       font.getUint16();
        var numTables = font.getUint16();
        var potentialTable;
        var canBreak = false;
        for (var i = 0; i < numTables; i++) {
         var platformId = font.getUint16();
         var encodingId = font.getUint16();
         var offset = font.getInt32() >>> 0;
         var useTable = false;
@@ -39698,18 +39430,18 @@
         return {
          platformId: -1,
          encodingId: -1,
          mappings: [],
          hasShortCmap: false
         };
        }
        var format = font.getUint16();
-       var length = font.getUint16();
-       var language = font.getUint16();
+       font.getUint16();
+       font.getUint16();
        var hasShortCmap = false;
        var mappings = [];
        var j, glyphId;
        if (format === 0) {
         for (j = 0; j < 256; j++) {
          var index = font.getByte();
          if (!index) {
           continue;
@@ -45187,32 +44919,21 @@
        });
       }
       function buildTextContentItem(chars) {
        var font = textState.font;
        var textChunk = ensureTextContentItem();
        var width = 0;
        var height = 0;
        var glyphs = font.charsToGlyphs(chars);
-       var defaultVMetrics = font.defaultVMetrics;
        for (var i = 0; i < glyphs.length; i++) {
         var glyph = glyphs[i];
-        var vMetricX = null;
-        var vMetricY = null;
         var glyphWidth = null;
-        if (font.vertical) {
-         if (glyph.vmetric) {
-          glyphWidth = glyph.vmetric[0];
-          vMetricX = glyph.vmetric[1];
-          vMetricY = glyph.vmetric[2];
-         } else {
-          glyphWidth = glyph.width;
-          vMetricX = glyph.width * 0.5;
-          vMetricY = defaultVMetrics[2];
-         }
+        if (font.vertical && glyph.vmetric) {
+         glyphWidth = glyph.vmetric[0];
         } else {
          glyphWidth = glyph.width;
         }
         var glyphUnicode = glyph.unicode;
         var NormalizedUnicodes = getNormalizedUnicodes();
         if (NormalizedUnicodes[glyphUnicode] !== undefined) {
          glyphUnicode = NormalizedUnicodes[glyphUnicode];
         }
@@ -47115,17 +46836,16 @@
    factory(root.pdfjsCoreAnnotation = {}, root.pdfjsSharedUtil, root.pdfjsCorePrimitives, root.pdfjsCoreStream, root.pdfjsCoreColorSpace, root.pdfjsCoreObj, root.pdfjsCoreEvaluator);
   }(this, function (exports, sharedUtil, corePrimitives, coreStream, coreColorSpace, coreObj, coreEvaluator) {
    var AnnotationBorderStyleType = sharedUtil.AnnotationBorderStyleType;
    var AnnotationFieldFlag = sharedUtil.AnnotationFieldFlag;
    var AnnotationFlag = sharedUtil.AnnotationFlag;
    var AnnotationType = sharedUtil.AnnotationType;
    var OPS = sharedUtil.OPS;
    var Util = sharedUtil.Util;
-   var isString = sharedUtil.isString;
    var isArray = sharedUtil.isArray;
    var isInt = sharedUtil.isInt;
    var stringToBytes = sharedUtil.stringToBytes;
    var stringToPDFString = sharedUtil.stringToPDFString;
    var warn = sharedUtil.warn;
    var Dict = corePrimitives.Dict;
    var isDict = corePrimitives.isDict;
    var isName = corePrimitives.isName;
@@ -48468,17 +48188,16 @@
    factory(root.pdfjsCoreWorker = {}, root.pdfjsSharedUtil, root.pdfjsCorePrimitives, root.pdfjsCorePdfManager);
   }(this, function (exports, sharedUtil, corePrimitives, corePdfManager) {
    var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
    var InvalidPDFException = sharedUtil.InvalidPDFException;
    var MessageHandler = sharedUtil.MessageHandler;
    var MissingPDFException = sharedUtil.MissingPDFException;
    var UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
    var PasswordException = sharedUtil.PasswordException;
-   var PasswordResponses = sharedUtil.PasswordResponses;
    var UnknownErrorException = sharedUtil.UnknownErrorException;
    var XRefParseException = sharedUtil.XRefParseException;
    var arrayByteLength = sharedUtil.arrayByteLength;
    var arraysToBytes = sharedUtil.arraysToBytes;
    var assert = sharedUtil.assert;
    var createPromiseCapability = sharedUtil.createPromiseCapability;
    var error = sharedUtil.error;
    var info = sharedUtil.info;
@@ -48734,17 +48453,17 @@
        handler.send('test', 'main', false);
        return;
       }
       var supportTransfers = data[0] === 255;
       handler.postMessageTransfers = supportTransfers;
       var xhr = new XMLHttpRequest();
       var responseExists = 'response' in xhr;
       try {
-       var dummy = xhr.responseType;
+       xhr.responseType;
       } catch (e) {
        responseExists = false;
       }
       if (!responseExists) {
        handler.send('test', false);
        return;
       }
       handler.send('test', {
--- a/browser/extensions/pdfjs/content/web/viewer.css
+++ b/browser/extensions/pdfjs/content/web/viewer.css
@@ -1079,16 +1079,34 @@ html[dir="rtl"] #viewOutline.toolbarButt
 #viewAttachments.toolbarButton::before {
   content: url(images/toolbarButton-viewAttachments.png);
 }
 
 #viewFind.toolbarButton::before {
   content: url(images/toolbarButton-search.png);
 }
 
+.toolbarButton.pdfSidebarNotification::after {
+  position: absolute;
+  display: inline-block;
+  top: 1px;
+  /* Create a filled circle, with a diameter of 9 pixels, using only CSS: */
+  content: '';
+  background-color: #70DB55;
+  height: 9px;
+  width: 9px;
+  border-radius: 50%;
+}
+html[dir='ltr'] .toolbarButton.pdfSidebarNotification::after {
+  left: 17px;
+}
+html[dir='rtl'] .toolbarButton.pdfSidebarNotification::after {
+  right: 17px;
+}
+
 .secondaryToolbarButton {
   position: relative;
   margin: 0 0 4px 0;
   padding: 3px 0 1px 0;
   height: auto;
   min-height: 25px;
   width: auto;
   min-width: 100%;
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -556,42 +556,50 @@ var pdfjsWebLibs;
    factory(root.pdfjsWebPDFAttachmentViewer = {}, root.pdfjsWebPDFJS);
   }(this, function (exports, pdfjsLib) {
    var PDFAttachmentViewer = function PDFAttachmentViewerClosure() {
     function PDFAttachmentViewer(options) {
      this.attachments = null;
      this.container = options.container;
      this.eventBus = options.eventBus;
      this.downloadManager = options.downloadManager;
+     this._renderedCapability = pdfjsLib.createPromiseCapability();
+     this.eventBus.on('fileattachmentannotation', this._appendAttachment.bind(this));
     }
     PDFAttachmentViewer.prototype = {
-     reset: function PDFAttachmentViewer_reset() {
+     reset: function PDFAttachmentViewer_reset(keepRenderedCapability) {
       this.attachments = null;
       var container = this.container;
       while (container.firstChild) {
        container.removeChild(container.firstChild);
       }
+      if (!keepRenderedCapability) {
+       this._renderedCapability = pdfjsLib.createPromiseCapability();
+      }
      },
      _dispatchEvent: function PDFAttachmentViewer_dispatchEvent(attachmentsCount) {
       this.eventBus.dispatch('attachmentsloaded', {
        source: this,
        attachmentsCount: attachmentsCount
       });
+      this._renderedCapability.resolve();
      },
      _bindLink: function PDFAttachmentViewer_bindLink(button, content, filename) {
       button.onclick = function downloadFile(e) {
        this.downloadManager.downloadData(content, filename, '');
        return false;
       }.bind(this);
      },
      render: function PDFAttachmentViewer_render(params) {
-      var attachments = params && params.attachments || null;
+      params = params || {};
+      var attachments = params.attachments || null;
       var attachmentsCount = 0;
       if (this.attachments) {
-       this.reset();
+       var keepRenderedCapability = params.keepRenderedCapability === true;
+       this.reset(keepRenderedCapability);
       }
       this.attachments = attachments;
       if (!attachments) {
        this._dispatchEvent(attachmentsCount);
        return;
       }
       var names = Object.keys(attachments).sort(function (a, b) {
        return a.toLowerCase().localeCompare(b.toLowerCase());
@@ -604,16 +612,38 @@ var pdfjsWebLibs;
        div.className = 'attachmentsItem';
        var button = document.createElement('button');
        this._bindLink(button, item.content, filename);
        button.textContent = pdfjsLib.removeNullCharacters(filename);
        div.appendChild(button);
        this.container.appendChild(div);
       }
       this._dispatchEvent(attachmentsCount);
+     },
+     _appendAttachment: function PDFAttachmentViewer_appendAttachment(item) {
+      this._renderedCapability.promise.then(function (id, filename, content) {
+       var attachments = this.attachments;
+       if (!attachments) {
+        attachments = Object.create(null);
+       } else {
+        for (var name in attachments) {
+         if (id === name) {
+          return;
+         }
+        }
+       }
+       attachments[id] = {
+        filename: filename,
+        content: content
+       };
+       this.render({
+        attachments: attachments,
+        keepRenderedCapability: true
+       });
+      }.bind(this, item.id, item.filename, item.content));
      }
     };
     return PDFAttachmentViewer;
    }();
    exports.PDFAttachmentViewer = PDFAttachmentViewer;
   }));
   (function (root, factory) {
    factory(root.pdfjsWebPDFOutlineViewer = {}, root.pdfjsWebPDFJS);
@@ -748,244 +778,16 @@ var pdfjsWebLibs;
       this._dispatchEvent(outlineCount);
      }
     };
     return PDFOutlineViewer;
    }();
    exports.PDFOutlineViewer = PDFOutlineViewer;
   }));
   (function (root, factory) {
-   factory(root.pdfjsWebPDFSidebar = {}, root.pdfjsWebPDFRenderingQueue);
-  }(this, function (exports, pdfRenderingQueue) {
-   var RenderingStates = pdfRenderingQueue.RenderingStates;
-   var SidebarView = {
-    NONE: 0,
-    THUMBS: 1,
-    OUTLINE: 2,
-    ATTACHMENTS: 3
-   };
-   var PDFSidebar = function PDFSidebarClosure() {
-    function PDFSidebar(options) {
-     this.isOpen = false;
-     this.active = SidebarView.THUMBS;
-     this.isInitialViewSet = false;
-     this.onToggled = null;
-     this.pdfViewer = options.pdfViewer;
-     this.pdfThumbnailViewer = options.pdfThumbnailViewer;
-     this.pdfOutlineViewer = options.pdfOutlineViewer;
-     this.mainContainer = options.mainContainer;
-     this.outerContainer = options.outerContainer;
-     this.eventBus = options.eventBus;
-     this.toggleButton = options.toggleButton;
-     this.thumbnailButton = options.thumbnailButton;
-     this.outlineButton = options.outlineButton;
-     this.attachmentsButton = options.attachmentsButton;
-     this.thumbnailView = options.thumbnailView;
-     this.outlineView = options.outlineView;
-     this.attachmentsView = options.attachmentsView;
-     this._addEventListeners();
-    }
-    PDFSidebar.prototype = {
-     reset: function PDFSidebar_reset() {
-      this.isInitialViewSet = false;
-      this.close();
-      this.switchView(SidebarView.THUMBS);
-      this.outlineButton.disabled = false;
-      this.attachmentsButton.disabled = false;
-     },
-     get visibleView() {
-      return this.isOpen ? this.active : SidebarView.NONE;
-     },
-     get isThumbnailViewVisible() {
-      return this.isOpen && this.active === SidebarView.THUMBS;
-     },
-     get isOutlineViewVisible() {
-      return this.isOpen && this.active === SidebarView.OUTLINE;
-     },
-     get isAttachmentsViewVisible() {
-      return this.isOpen && this.active === SidebarView.ATTACHMENTS;
-     },
-     setInitialView: function PDFSidebar_setInitialView(view) {
-      if (this.isInitialViewSet) {
-       return;
-      }
-      this.isInitialViewSet = true;
-      if (this.isOpen && view === SidebarView.NONE) {
-       this._dispatchEvent();
-       return;
-      }
-      var isViewPreserved = view === this.visibleView;
-      this.switchView(view, true);
-      if (isViewPreserved) {
-       this._dispatchEvent();
-      }
-     },
-     switchView: function PDFSidebar_switchView(view, forceOpen) {
-      if (view === SidebarView.NONE) {
-       this.close();
-       return;
-      }
-      var isViewChanged = view !== this.active;
-      var shouldForceRendering = false;
-      switch (view) {
-      case SidebarView.THUMBS:
-       this.thumbnailButton.classList.add('toggled');
-       this.outlineButton.classList.remove('toggled');
-       this.attachmentsButton.classList.remove('toggled');
-       this.thumbnailView.classList.remove('hidden');
-       this.outlineView.classList.add('hidden');
-       this.attachmentsView.classList.add('hidden');
-       if (this.isOpen && isViewChanged) {
-        this._updateThumbnailViewer();
-        shouldForceRendering = true;
-       }
-       break;
-      case SidebarView.OUTLINE:
-       if (this.outlineButton.disabled) {
-        return;
-       }
-       this.thumbnailButton.classList.remove('toggled');
-       this.outlineButton.classList.add('toggled');
-       this.attachmentsButton.classList.remove('toggled');
-       this.thumbnailView.classList.add('hidden');
-       this.outlineView.classList.remove('hidden');
-       this.attachmentsView.classList.add('hidden');
-       break;
-      case SidebarView.ATTACHMENTS:
-       if (this.attachmentsButton.disabled) {
-        return;
-       }
-       this.thumbnailButton.classList.remove('toggled');
-       this.outlineButton.classList.remove('toggled');
-       this.attachmentsButton.classList.add('toggled');
-       this.thumbnailView.classList.add('hidden');
-       this.outlineView.classList.add('hidden');
-       this.attachmentsView.classList.remove('hidden');
-       break;
-      default:
-       console.error('PDFSidebar_switchView: "' + view + '" is an unsupported value.');
-       return;
-      }
-      this.active = view | 0;
-      if (forceOpen && !this.isOpen) {
-       this.open();
-       return;
-      }
-      if (shouldForceRendering) {
-       this._forceRendering();
-      }
-      if (isViewChanged) {
-       this._dispatchEvent();
-      }
-     },
-     open: function PDFSidebar_open() {
-      if (this.isOpen) {
-       return;
-      }
-      this.isOpen = true;
-      this.toggleButton.classList.add('toggled');
-      this.outerContainer.classList.add('sidebarMoving');
-      this.outerContainer.classList.add('sidebarOpen');
-      if (this.active === SidebarView.THUMBS) {
-       this._updateThumbnailViewer();
-      }
-      this._forceRendering();
-      this._dispatchEvent();
-     },
-     close: function PDFSidebar_close() {
-      if (!this.isOpen) {
-       return;
-      }
-      this.isOpen = false;
-      this.toggleButton.classList.remove('toggled');
-      this.outerContainer.classList.add('sidebarMoving');
-      this.outerContainer.classList.remove('sidebarOpen');
-      this._forceRendering();
-      this._dispatchEvent();
-     },
-     toggle: function PDFSidebar_toggle() {
-      if (this.isOpen) {
-       this.close();
-      } else {
-       this.open();
-      }
-     },
-     _dispatchEvent: function PDFSidebar_dispatchEvent() {
-      this.eventBus.dispatch('sidebarviewchanged', {
-       source: this,
-       view: this.visibleView
-      });
-     },
-     _forceRendering: function PDFSidebar_forceRendering() {
-      if (this.onToggled) {
-       this.onToggled();
-      } else {
-       this.pdfViewer.forceRendering();
-       this.pdfThumbnailViewer.forceRendering();
-      }
-     },
-     _updateThumbnailViewer: function PDFSidebar_updateThumbnailViewer() {
-      var pdfViewer = this.pdfViewer;
-      var thumbnailViewer = this.pdfThumbnailViewer;
-      var pagesCount = pdfViewer.pagesCount;
-      for (var pageIndex = 0; pageIndex < pagesCount; pageIndex++) {
-       var pageView = pdfViewer.getPageView(pageIndex);
-       if (pageView && pageView.renderingState === RenderingStates.FINISHED) {
-        var thumbnailView = thumbnailViewer.getThumbnail(pageIndex);
-        thumbnailView.setImage(pageView);
-       }
-      }
-      thumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
-     },
-     _addEventListeners: function PDFSidebar_addEventListeners() {
-      var self = this;
-      self.mainContainer.addEventListener('transitionend', function (evt) {
-       if (evt.target === this) {
-        self.outerContainer.classList.remove('sidebarMoving');
-       }
-      });
-      self.thumbnailButton.addEventListener('click', function () {
-       self.switchView(SidebarView.THUMBS);
-      });
-      self.outlineButton.addEventListener('click', function () {
-       self.switchView(SidebarView.OUTLINE);
-      });
-      self.outlineButton.addEventListener('dblclick', function () {
-       self.pdfOutlineViewer.toggleOutlineTree();
-      });
-      self.attachmentsButton.addEventListener('click', function () {
-       self.switchView(SidebarView.ATTACHMENTS);
-      });
-      self.eventBus.on('outlineloaded', function (e) {
-       var outlineCount = e.outlineCount;
-       self.outlineButton.disabled = !outlineCount;
-       if (!outlineCount && self.active === SidebarView.OUTLINE) {
-        self.switchView(SidebarView.THUMBS);
-       }
-      });
-      self.eventBus.on('attachmentsloaded', function (e) {
-       var attachmentsCount = e.attachmentsCount;
-       self.attachmentsButton.disabled = !attachmentsCount;
-       if (!attachmentsCount && self.active === SidebarView.ATTACHMENTS) {
-        self.switchView(SidebarView.THUMBS);
-       }
-      });
-      self.eventBus.on('presentationmodechanged', function (e) {
-       if (!e.active && !e.switchInProgress && self.isThumbnailViewVisible) {
-        self._updateThumbnailViewer();
-       }
-      });
-     }
-    };
-    return PDFSidebar;
-   }();
-   exports.SidebarView = SidebarView;
-   exports.PDFSidebar = PDFSidebar;
-  }));
-  (function (root, factory) {
    factory(root.pdfjsWebUIUtils = {}, root.pdfjsWebPDFJS);
   }(this, function (exports, pdfjsLib) {
    var CSS_UNITS = 96.0 / 72.0;
    var DEFAULT_SCALE_VALUE = 'auto';
    var DEFAULT_SCALE = 1.0;
    var MIN_SCALE = 0.25;
    var MAX_SCALE = 10.0;
    var UNKNOWN_SCALE = 0;
@@ -2414,16 +2216,299 @@ var pdfjsWebLibs;
       delete this.fullscreenChangeBind;
      }
     };
     return PDFPresentationMode;
    }();
    exports.PDFPresentationMode = PDFPresentationMode;
   }));
   (function (root, factory) {
+   factory(root.pdfjsWebPDFSidebar = {}, root.pdfjsWebPDFRenderingQueue, root.pdfjsWebUIUtils);
+  }(this, function (exports, pdfRenderingQueue, uiUtils) {
+   var RenderingStates = pdfRenderingQueue.RenderingStates;
+   var mozL10n = uiUtils.mozL10n;
+   var UI_NOTIFICATION_CLASS = 'pdfSidebarNotification';
+   var SidebarView = {
+    NONE: 0,
+    THUMBS: 1,
+    OUTLINE: 2,
+    ATTACHMENTS: 3
+   };
+   var PDFSidebar = function PDFSidebarClosure() {
+    function PDFSidebar(options) {
+     this.isOpen = false;
+     this.active = SidebarView.THUMBS;
+     this.isInitialViewSet = false;
+     this.onToggled = null;
+     this.pdfViewer = options.pdfViewer;
+     this.pdfThumbnailViewer = options.pdfThumbnailViewer;
+     this.pdfOutlineViewer = options.pdfOutlineViewer;
+     this.mainContainer = options.mainContainer;
+     this.outerContainer = options.outerContainer;
+     this.eventBus = options.eventBus;
+     this.toggleButton = options.toggleButton;
+     this.thumbnailButton = options.thumbnailButton;
+     this.outlineButton = options.outlineButton;
+     this.attachmentsButton = options.attachmentsButton;
+     this.thumbnailView = options.thumbnailView;
+     this.outlineView = options.outlineView;
+     this.attachmentsView = options.attachmentsView;
+     this.disableNotification = options.disableNotification || false;
+     this._addEventListeners();
+    }
+    PDFSidebar.prototype = {
+     reset: function PDFSidebar_reset() {
+      this.isInitialViewSet = false;
+      this._hideUINotification(null);
+      this.switchView(SidebarView.THUMBS);
+      this.outlineButton.disabled = false;
+      this.attachmentsButton.disabled = false;
+     },
+     get visibleView() {
+      return this.isOpen ? this.active : SidebarView.NONE;
+     },
+     get isThumbnailViewVisible() {
+      return this.isOpen && this.active === SidebarView.THUMBS;
+     },
+     get isOutlineViewVisible() {
+      return this.isOpen && this.active === SidebarView.OUTLINE;
+     },
+     get isAttachmentsViewVisible() {
+      return this.isOpen && this.active === SidebarView.ATTACHMENTS;
+     },
+     setInitialView: function PDFSidebar_setInitialView(view) {
+      if (this.isInitialViewSet) {
+       return;
+      }
+      this.isInitialViewSet = true;
+      if (this.isOpen && view === SidebarView.NONE) {
+       this._dispatchEvent();
+       return;
+      }
+      var isViewPreserved = view === this.visibleView;
+      this.switchView(view, true);
+      if (isViewPreserved) {
+       this._dispatchEvent();
+      }
+     },
+     switchView: function PDFSidebar_switchView(view, forceOpen) {
+      if (view === SidebarView.NONE) {
+       this.close();
+       return;
+      }
+      var isViewChanged = view !== this.active;
+      var shouldForceRendering = false;
+      switch (view) {
+      case SidebarView.THUMBS:
+       this.thumbnailButton.classList.add('toggled');
+       this.outlineButton.classList.remove('toggled');
+       this.attachmentsButton.classList.remove('toggled');
+       this.thumbnailView.classList.remove('hidden');
+       this.outlineView.classList.add('hidden');
+       this.attachmentsView.classList.add('hidden');
+       if (this.isOpen && isViewChanged) {
+        this._updateThumbnailViewer();
+        shouldForceRendering = true;
+       }
+       break;
+      case SidebarView.OUTLINE:
+       if (this.outlineButton.disabled) {
+        return;
+       }
+       this.thumbnailButton.classList.remove('toggled');
+       this.outlineButton.classList.add('toggled');
+       this.attachmentsButton.classList.remove('toggled');
+       this.thumbnailView.classList.add('hidden');
+       this.outlineView.classList.remove('hidden');
+       this.attachmentsView.classList.add('hidden');
+       break;
+      case SidebarView.ATTACHMENTS:
+       if (this.attachmentsButton.disabled) {
+        return;
+       }
+       this.thumbnailButton.classList.remove('toggled');
+       this.outlineButton.classList.remove('toggled');
+       this.attachmentsButton.classList.add('toggled');
+       this.thumbnailView.classList.add('hidden');
+       this.outlineView.classList.add('hidden');
+       this.attachmentsView.classList.remove('hidden');
+       break;
+      default:
+       console.error('PDFSidebar_switchView: "' + view + '" is an unsupported value.');
+       return;
+      }
+      this.active = view | 0;
+      if (forceOpen && !this.isOpen) {
+       this.open();
+       return;
+      }
+      if (shouldForceRendering) {
+       this._forceRendering();
+      }
+      if (isViewChanged) {
+       this._dispatchEvent();
+      }
+      this._hideUINotification(this.active);
+     },
+     open: function PDFSidebar_open() {
+      if (this.isOpen) {
+       return;
+      }
+      this.isOpen = true;
+      this.toggleButton.classList.add('toggled');
+      this.outerContainer.classList.add('sidebarMoving');
+      this.outerContainer.classList.add('sidebarOpen');
+      if (this.active === SidebarView.THUMBS) {
+       this._updateThumbnailViewer();
+      }
+      this._forceRendering();
+      this._dispatchEvent();
+      this._hideUINotification(this.active);
+     },
+     close: function PDFSidebar_close() {
+      if (!this.isOpen) {
+       return;
+      }
+      this.isOpen = false;
+      this.toggleButton.classList.remove('toggled');
+      this.outerContainer.classList.add('sidebarMoving');
+      this.outerContainer.classList.remove('sidebarOpen');
+      this._forceRendering();
+      this._dispatchEvent();
+     },
+     toggle: function PDFSidebar_toggle() {
+      if (this.isOpen) {
+       this.close();
+      } else {
+       this.open();
+      }
+     },
+     _dispatchEvent: function PDFSidebar_dispatchEvent() {
+      this.eventBus.dispatch('sidebarviewchanged', {
+       source: this,
+       view: this.visibleView
+      });
+     },
+     _forceRendering: function PDFSidebar_forceRendering() {
+      if (this.onToggled) {
+       this.onToggled();
+      } else {
+       this.pdfViewer.forceRendering();
+       this.pdfThumbnailViewer.forceRendering();
+      }
+     },
+     _updateThumbnailViewer: function PDFSidebar_updateThumbnailViewer() {
+      var pdfViewer = this.pdfViewer;
+      var thumbnailViewer = this.pdfThumbnailViewer;
+      var pagesCount = pdfViewer.pagesCount;
+      for (var pageIndex = 0; pageIndex < pagesCount; pageIndex++) {
+       var pageView = pdfViewer.getPageView(pageIndex);
+       if (pageView && pageView.renderingState === RenderingStates.FINISHED) {
+        var thumbnailView = thumbnailViewer.getThumbnail(pageIndex);
+        thumbnailView.setImage(pageView);
+       }
+      }
+      thumbnailViewer.scrollThumbnailIntoView(pdfViewer.currentPageNumber);
+     },
+     _showUINotification: function (view) {
+      if (this.disableNotification) {
+       return;
+      }
+      this.toggleButton.title = mozL10n.get('toggle_sidebar_notification.title', null, 'Toggle Sidebar (document contains outline/attachments)');
+      if (!this.isOpen) {
+       this.toggleButton.classList.add(UI_NOTIFICATION_CLASS);
+      } else if (view === this.active) {
+       return;
+      }
+      switch (view) {
+      case SidebarView.OUTLINE:
+       this.outlineButton.classList.add(UI_NOTIFICATION_CLASS);
+       break;
+      case SidebarView.ATTACHMENTS:
+       this.attachmentsButton.classList.add(UI_NOTIFICATION_CLASS);
+       break;
+      }
+     },
+     _hideUINotification: function (view) {
+      if (this.disableNotification) {
+       return;
+      }
+      var removeNotification = function (view) {
+       switch (view) {
+       case SidebarView.OUTLINE:
+        this.outlineButton.classList.remove(UI_NOTIFICATION_CLASS);
+        break;
+       case SidebarView.ATTACHMENTS:
+        this.attachmentsButton.classList.remove(UI_NOTIFICATION_CLASS);
+        break;
+       }
+      }.bind(this);
+      if (!this.isOpen && view !== null) {
+       return;
+      }
+      this.toggleButton.classList.remove(UI_NOTIFICATION_CLASS);
+      if (view !== null) {
+       removeNotification(view);
+       return;
+      }
+      for (view in SidebarView) {
+       removeNotification(SidebarView[view]);
+      }
+      this.toggleButton.title = mozL10n.get('toggle_sidebar.title', null, 'Toggle Sidebar');
+     },
+     _addEventListeners: function PDFSidebar_addEventListeners() {
+      var self = this;
+      self.mainContainer.addEventListener('transitionend', function (evt) {
+       if (evt.target === this) {
+        self.outerContainer.classList.remove('sidebarMoving');
+       }
+      });
+      self.thumbnailButton.addEventListener('click', function () {
+       self.switchView(SidebarView.THUMBS);
+      });
+      self.outlineButton.addEventListener('click', function () {
+       self.switchView(SidebarView.OUTLINE);
+      });
+      self.outlineButton.addEventListener('dblclick', function () {
+       self.pdfOutlineViewer.toggleOutlineTree();
+      });
+      self.attachmentsButton.addEventListener('click', function () {
+       self.switchView(SidebarView.ATTACHMENTS);
+      });
+      self.eventBus.on('outlineloaded', function (e) {
+       var outlineCount = e.outlineCount;
+       self.outlineButton.disabled = !outlineCount;
+       if (outlineCount) {
+        self._showUINotification(SidebarView.OUTLINE);
+       } else if (self.active === SidebarView.OUTLINE) {
+        self.switchView(SidebarView.THUMBS);
+       }
+      });
+      self.eventBus.on('attachmentsloaded', function (e) {
+       var attachmentsCount = e.attachmentsCount;
+       self.attachmentsButton.disabled = !attachmentsCount;
+       if (attachmentsCount) {
+        self._showUINotification(SidebarView.ATTACHMENTS);
+       } else if (self.active === SidebarView.ATTACHMENTS) {
+        self.switchView(SidebarView.THUMBS);
+       }
+      });
+      self.eventBus.on('presentationmodechanged', function (e) {
+       if (!e.active && !e.switchInProgress && self.isThumbnailViewVisible) {
+        self._updateThumbnailViewer();
+       }
+      });
+     }
+    };
+    return PDFSidebar;
+   }();
+   exports.SidebarView = SidebarView;
+   exports.PDFSidebar = PDFSidebar;
+  }));
+  (function (root, factory) {
    factory(root.pdfjsWebPDFThumbnailView = {}, root.pdfjsWebUIUtils, root.pdfjsWebPDFRenderingQueue);
   }(this, function (exports, uiUtils, pdfRenderingQueue) {
    var mozL10n = uiUtils.mozL10n;
    var getOutputScale = uiUtils.getOutputScale;
    var RenderingStates = pdfRenderingQueue.RenderingStates;
    var THUMBNAIL_WIDTH = 98;
    var THUMBNAIL_CANVAS_BORDER_WIDTH = 1;
    var PDFThumbnailView = function PDFThumbnailViewClosure() {
@@ -3740,16 +3825,24 @@ var pdfjsWebLibs;
       default:
        break;
       }
       this.eventBus.dispatch('namedaction', {
        source: this,
        action: action
       });
      },
+     onFileAttachmentAnnotation: function (params) {
+      this.eventBus.dispatch('fileattachmentannotation', {
+       source: this,
+       id: params.id,
+       filename: params.filename,
+       content: params.content
+      });
+     },
      cachePageRef: function PDFLinkService_cachePageRef(pageNum, pageRef) {
       var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
       this._pagesRefCache[refStr] = pageNum;
      },
      _cachedPageNumber: function PDFLinkService_cachedPageNumber(pageRef) {
       var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
       return this._pagesRefCache && this._pagesRefCache[refStr] || null;
      }
@@ -3822,16 +3915,18 @@ var pdfjsWebLibs;
      },
      getAnchorUrl: function (hash) {
       return '#';
      },
      setHash: function (hash) {
      },
      executeNamedAction: function (action) {
      },
+     onFileAttachmentAnnotation: function (params) {
+     },
      cachePageRef: function (pageNum, pageRef) {
      }
     };
     return SimpleLinkService;
    }();
    exports.PDFLinkService = PDFLinkService;
    exports.SimpleLinkService = SimpleLinkService;
   }));
@@ -4069,17 +4164,16 @@ var pdfjsWebLibs;
      draw: function PDFPageView_draw() {
       if (this.renderingState !== RenderingStates.INITIAL) {
        console.error('Must be in new state before drawing');
        this.reset();
       }
       this.renderingState = RenderingStates.RUNNING;
       var self = this;
       var pdfPage = this.pdfPage;
-      var viewport = this.viewport;
       var div = this.div;
       var canvasWrapper = document.createElement('div');
       canvasWrapper.style.width = div.style.width;
       canvasWrapper.style.height = div.style.height;
       canvasWrapper.classList.add('canvasWrapper');
       if (this.annotationLayer && this.annotationLayer.div) {
        div.insertBefore(canvasWrapper, this.annotationLayer.div);
       } else {
@@ -4189,18 +4283,16 @@ var pdfjsWebLibs;
        promise: promise,
        onRenderContinue: function (cont) {
         cont();
        },
        cancel: function () {
         renderTask.cancel();
        }
       };
-      var self = this;
-      var pdfPage = this.pdfPage;
       var viewport = this.viewport;
       var canvas = document.createElement('canvas');
       canvas.id = 'page' + this.id;
       canvas.setAttribute('hidden', 'hidden');
       var isCanvasHidden = true;
       var showCanvas = function () {
        if (isCanvasHidden) {
         canvas.removeAttribute('hidden');
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -466,18 +466,16 @@
 @RESPATH@/components/satchel.manifest
 @RESPATH@/components/nsFormAutoComplete.js
 @RESPATH@/components/nsFormHistory.js
 @RESPATH@/components/FormHistoryStartup.js
 @RESPATH@/components/nsInputListAutoComplete.js
 @RESPATH@/components/formautofill.manifest
 @RESPATH@/components/FormAutofillContentService.js
 @RESPATH@/components/FormAutofillStartup.js
-@RESPATH@/components/CSSUnprefixingService.js
-@RESPATH@/components/CSSUnprefixingService.manifest
 @RESPATH@/components/contentAreaDropListener.manifest
 @RESPATH@/components/contentAreaDropListener.js
 @RESPATH@/browser/components/BrowserProfileMigrators.manifest
 @RESPATH@/browser/components/ProfileMigrator.js
 @RESPATH@/browser/components/ChromeProfileMigrator.js
 @RESPATH@/browser/components/FirefoxProfileMigrator.js
 #ifdef XP_WIN
 @RESPATH@/browser/components/360seProfileMigrator.js
--- a/browser/installer/windows/nsis/installer.nsi
+++ b/browser/installer/windows/nsis/installer.nsi
@@ -833,26 +833,20 @@ Function LaunchApp
     Exec "$\"$INSTDIR\${FileMainEXE}$\""
   ${Else}
     GetFunctionAddress $0 LaunchAppFromElevatedProcess
     UAC::ExecCodeSegment $0
   ${EndIf}
 FunctionEnd
 
 Function LaunchAppFromElevatedProcess
-  ; Find the installation directory when launching using GetFunctionAddress
-  ; from an elevated installer since $INSTDIR will not be set in this installer
-  ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
-  ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
-  ${GetPathFromString} "$0" $0
-  ${GetParent} "$0" $1
   ; Set our current working directory to the application's install directory
   ; otherwise the 7-Zip temp directory will be in use and won't be deleted.
-  SetOutPath "$1"
-  Exec "$\"$0$\""
+  SetOutPath "$INSTDIR"
+  Exec "$\"$INSTDIR\${FileMainEXE}$\""
 FunctionEnd
 
 ################################################################################
 # Language
 
 !insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
 !verbose push
 !verbose 3
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -2060,25 +2060,19 @@ Function LaunchApp
     Exec "$\"$INSTDIR\${FileMainEXE}$\""
   ${Else}
     GetFunctionAddress $0 LaunchAppFromElevatedProcess
     UAC::ExecCodeSegment $0
   ${EndIf}
 FunctionEnd
 
 Function LaunchAppFromElevatedProcess
-  ; Find the installation directory when launching using GetFunctionAddress
-  ; from an elevated installer since $INSTDIR will not be set in this installer
-  ${StrFilter} "${FileMainEXE}" "+" "" "" $R9
-  ReadRegStr $0 HKLM "Software\Clients\StartMenuInternet\$R9\DefaultIcon" ""
-  ${GetPathFromString} "$0" $0
   ; Set the current working directory to the installation directory
-  ${GetParent} "$0" $1
-  SetOutPath "$1"
-  Exec "$\"$0$\""
+  SetOutPath "$INSTDIR"
+  Exec "$\"$INSTDIR\${FileMainEXE}$\""
 FunctionEnd
 
 Function CopyPostSigningData
   ${LineRead} "$EXEDIR\postSigningData" "1" $PostSigningData
   ${If} ${Errors}
     ClearErrors
     StrCpy $PostSigningData "0"
   ${Else}
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -96,16 +96,17 @@ print_progress_message=Preparing document for printing…
 # a numerical per cent value.
 print_progress_percent={{progress}}%
 print_progress_close=Cancel
 
 # Tooltips and alt text for side panel toolbar buttons
 # (the _label strings are alt text for the buttons, the .title strings are
 # tooltips)
 toggle_sidebar.title=Toggle Sidebar
+toggle_sidebar_notification.title=Toggle Sidebar (document contains outline/attachments)
 toggle_sidebar_label=Toggle Sidebar
 document_outline.title=Show Document Outline (double-click to expand/collapse all items)
 document_outline_label=Document Outline
 attachments.title=Show Attachments
 attachments_label=Attachments
 thumbs.title=Show Thumbnails
 thumbs_label=Thumbnails
 findbar.title=Find in Document
--- a/browser/locales/search/list.json
+++ b/browser/locales/search/list.json
@@ -1,31 +1,53 @@
 {
   "default": {
     "visibleDefaultEngines": [
       "google", "yahoo", "amazondotcom", "bing", "ddg", "twitter", "wikipedia"
     ]
   },
+  "regionOverrides": {
+    "US": {
+      "google": "google-nocodes"
+    },
+    "CA": {
+      "google": "google-nocodes"
+    },
+    "KZ": {
+      "google": "google-nocodes"
+    },
+    "BY": {
+      "google": "google-nocodes"
+    },
+    "RU": {
+      "google": "google-nocodes"
+    },
+    "TR": {
+      "google": "google-nocodes"
+    },
+    "UA": {
+      "google": "google-nocodes"
+    },
+    "CN": {
+      "google": "google-nocodes"
+    },
+    "TW": {
+      "google": "google-nocodes"
+    },
+    "HK": {
+      "google": "google-nocodes"
+    }
+  },
   "locales": {
     "en-US": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo", "amazondotcom", "bing", "ddg", "twitter", "wikipedia"
         ]
       },
-      "US": {
-        "visibleDefaultEngines": [
-          "yahoo", "google-nocodes", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
-        ]
-      },
-      "CA": {
-        "visibleDefaultEngines": [
-          "google-nocodes", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia"
-        ]
-      },
       "experimental-hidden": {
         "visibleDefaultEngines": [
           "yahoo-en-CA"
         ]
       }
     },
     "ach": {
       "default": {
@@ -391,41 +413,16 @@
         ]
       }
     },
     "kk": {
       "default": {
         "visibleDefaultEngines": [
           "yandex-kk", "google", "ddg", "flip", "kaz-kk", "twitter", "wikipedia-kk"
         ]
-      },
-      "KZ": {
-        "visibleDefaultEngines": [
-          "yandex-kk", "google-nocodes", "ddg", "flip", "kaz-kk", "twitter", "wikipedia-kk"
-        ]
-      },
-      "BY": {
-        "visibleDefaultEngines": [
-          "yandex-kk", "google-nocodes", "ddg", "flip", "kaz-kk", "twitter", "wikipedia-kk"
-        ]
-      },
-      "RU": {
-        "visibleDefaultEngines": [
-          "yandex-kk", "google-nocodes", "ddg", "flip", "kaz-kk", "twitter", "wikipedia-kk"
-        ]
-      },
-      "TR": {
-        "visibleDefaultEngines": [
-          "yandex-kk", "google-nocodes", "ddg", "flip", "kaz-kk", "twitter", "wikipedia-kk"
-        ]
-      },
-      "UA": {
-        "visibleDefaultEngines": [
-          "yandex-kk", "google-nocodes", "ddg", "flip", "kaz-kk", "twitter", "wikipedia-kk"
-        ]
       }
     },
     "km": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo", "bing", "amazondotcom", "ddg", "twitter", "wikipedia-km"
         ]
       }
@@ -591,41 +588,16 @@
         ]
       }
     },
     "ru": {
       "default": {
         "visibleDefaultEngines": [
           "yandex-ru", "google", "ddg", "ozonru", "priceru", "wikipedia-ru", "mailru"
         ]
-      },
-      "RU": {
-        "visibleDefaultEngines": [
-          "yandex-ru", "google-nocodes", "ddg", "ozonru", "priceru", "wikipedia-ru", "mailru"
-        ]
-      },
-      "BY": {
-        "visibleDefaultEngines": [
-          "yandex-ru", "google-nocodes", "ddg", "ozonru", "priceru", "wikipedia-ru", "mailru"
-        ]
-      },
-      "KZ": {
-        "visibleDefaultEngines": [
-          "yandex-ru", "google-nocodes", "ddg", "ozonru", "priceru", "wikipedia-ru", "mailru"
-        ]
-      },
-      "TR": {
-        "visibleDefaultEngines": [
-          "yandex-ru", "google-nocodes", "ddg", "ozonru", "priceru", "wikipedia-ru", "mailru"
-        ]
-      },
-      "UA": {
-        "visibleDefaultEngines": [
-          "yandex-ru", "google-nocodes", "ddg", "ozonru", "priceru", "wikipedia-ru", "mailru"
-        ]
       }
     },
     "si": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo", "amazondotcom", "ddg", "wikipedia-si"
         ]
       }
@@ -700,73 +672,23 @@
         ]
       }
     },
     "tr": {
       "default": {
         "visibleDefaultEngines": [
           "yandex-tr", "google", "ddg", "twitter", "wikipedia-tr"
         ]
-      },
-      "TR": {
-        "visibleDefaultEngines": [
-          "yandex-tr", "google-nocodes", "ddg", "twitter", "wikipedia-tr"
-        ]
-      },
-      "BY": {
-        "visibleDefaultEngines": [
-          "yandex-tr", "google-nocodes", "ddg", "twitter", "wikipedia-tr"
-        ]
-      },
-      "KZ": {
-        "visibleDefaultEngines": [
-          "yandex-tr", "google-nocodes", "ddg", "twitter", "wikipedia-tr"
-        ]
-      },
-      "RU": {
-        "visibleDefaultEngines": [
-          "yandex-tr", "google-nocodes", "ddg", "twitter", "wikipedia-tr"
-        ]
-      },
-      "UA": {
-        "visibleDefaultEngines": [
-          "yandex-tr", "google-nocodes", "ddg", "twitter", "wikipedia-tr"
-        ]
       }
     },
     "uk": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yandex-uk", "meta-ua", "ddg", "wikipedia-uk", "metamarket"
         ]
-      },
-      "UA": {
-        "visibleDefaultEngines": [
-          "google-nocodes", "yandex-uk", "meta-ua", "ddg", "wikipedia-uk", "metamarket"
-        ]
-      },
-      "TR": {
-        "visibleDefaultEngines": [
-          "google-nocodes", "yandex-uk", "meta-ua", "ddg", "wikipedia-uk", "metamarket"
-        ]
-      },
-      "BY": {
-        "visibleDefaultEngines": [
-          "google-nocodes", "yandex-uk", "meta-ua", "ddg", "wikipedia-uk", "metamarket"
-        ]
-      },
-      "KZ": {
-        "visibleDefaultEngines": [
-          "google-nocodes", "yandex-uk", "meta-ua", "ddg", "wikipedia-uk", "metamarket"
-        ]
-      },
-      "RU": {
-        "visibleDefaultEngines": [
-          "google-nocodes", "yandex-uk", "meta-ua", "ddg", "wikipedia-uk", "metamarket"
-        ]
       }
     },
     "ur": {
       "default": {
         "visibleDefaultEngines": [
           "google", "yahoo-in", "bing", "amazon-in", "ddg", "twitter", "wikipedia-ur"
         ]
       }
@@ -806,22 +728,12 @@
         ]
       }
     },
     "zh-TW": {
       "default": {
         "visibleDefaultEngines": [
           "yahoo-zh-TW", "google", "ddg", "findbook-zh-TW", "wikipedia-zh-TW", "yahoo-zh-TW-HK", "yahoo-bid-zh-TW", "yahoo-answer-zh-TW"
         ]
-      },
-      "TW": {
-        "visibleDefaultEngines": [
-          "yahoo-zh-TW", "google-nocodes", "ddg", "findbook-zh-TW", "wikipedia-zh-TW", "yahoo-zh-TW-HK", "yahoo-bid-zh-TW", "yahoo-answer-zh-TW"
-        ]
-      },
-      "HK": {
-        "visibleDefaultEngines": [
-          "yahoo-zh-TW-HK", "google-nocodes", "ddg", "findbook-zh-TW", "wikipedia-zh-TW", "yahoo-zh-TW", "yahoo-bid-zh-TW", "yahoo-answer-zh-TW"
-        ]
       }
     }
   }
 }
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -450,16 +450,17 @@ this.ContentSearch = {
   _onMessageSpeculativeConnect(msg, engineName) {
     let engine = Services.search.getEngineByName(engineName);
     if (!engine) {
       throw new Error("Unknown engine name: " + engineName);
     }
     if (msg.target.contentWindow) {
       engine.speculativeConnect({
         window: msg.target.contentWindow,
+        originAttributes: msg.target.contentPrincipal.originAttributes
       });
     }
   },
 
   _onObserve: Task.async(function* (data) {
     if (data === "engine-current") {
       let engine = yield this._currentEngineObj();
       this._broadcast("CurrentEngine", engine);
--- a/browser/themes/linux/places/organizer.css
+++ b/browser/themes/linux/places/organizer.css
@@ -15,16 +15,20 @@
 #placesToolbar > toolbarbutton {
   color: -moz-menubartext;
 }
 
 #placesToolbar > toolbarbutton:hover {
   color: ButtonText;
 }
 
+#placesToolbar > toolbarbutton[disabled=true] {
+  color: GrayText;
+}
+
 /* back button */
 
 #back-button {
   list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar");
 }
 #back-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled");
 }
deleted file mode 100644
--- a/browser/themes/shared/addons/addon-badge.svg
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
-<style type="text/css">
-path {
-  stroke: #bb3817;
-  fill: #FFFFFF;
-}
-</style>
-<path d="M6.6,9c0.3,0,0.5-0.3,0.6-0.6C7,7.7,7,6.9,7.1,6.2c0.1-0.3,0.3-0.4,0.6-0.4c0.3,0,0.3,0.4,1,0.4
-        c0.3,0,0.8-0.1,0.8-1.1S9,3.9,8.7,3.9c-0.6,0-0.7,0.5-1,0.5c-0.3,0-0.5-0.2-0.6-0.5c0-0.3,0-0.7,0-1c0-0.3-0.2-0.5-0.5-0.6
-        c0,0,0,0-0.1,0c-0.5,0-1,0.1-1.6,0C4.7,2.3,4.5,2.1,4.6,1.8c0-0.4,0.4-0.3,0.4-1C5,0.5,4.9,0,3.8,0S2.7,0.5,2.7,0.8
-        c0,0.6,0.5,0.7,0.5,1c0,0.3-0.2,0.5-0.5,0.5C2.1,2.4,1.6,2.4,1,2.4c-0.3,0-0.5,0.2-0.6,0.5c0,0,0,0,0,0.1v0.7c0,0-0.1,0.8,0.6,0.8
-        C1.5,4.5,1.6,4,2.2,4c0.3,0,0.7,0.7,0.7,1.3S2.4,6.6,2.2,6.6C1.6,6.6,1.5,6,1.1,6C0.4,5.9,0.5,6.7,0.5,6.7v1.7C0.4,8.7,0.7,9,1,9
-        c0,0,0,0,0,0h2.1C3.1,9,4,9,4,8.4c0-0.4-0.7-0.6-0.7-1.2C3.5,6.7,4,6.3,4.5,6.3c0.6,0,1.2,0.6,1.2,0.8c0,0.6-0.6,0.8-0.6,1.2
-        C5.1,9,5.9,9,5.9,9L6.6,9z"/>
-</svg>
--- a/browser/themes/shared/customizableui/panelUI.inc.css
+++ b/browser/themes/shared/customizableui/panelUI.inc.css
@@ -152,17 +152,17 @@
 
 #PanelUI-menu-button[badge-status="download-warning"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive,
 #PanelUI-menu-button[badge-status="fxa-needs-authentication"] > .toolbarbutton-badge-stack > .toolbarbutton-badge:-moz-window-inactive {
   filter: none;
 }
 
 #PanelUI-menu-button[badge-status="addon-alert"] > .toolbarbutton-badge-stack > .toolbarbutton-badge {
   height: 13px;
-  background: transparent url(chrome://browser/skin/addons/addon-badge.svg) no-repeat center;
+  background: #FFBF00 url(chrome://browser/skin/update-badge-failed.svg) no-repeat center;
 }
 
 .panel-subviews {
   padding: 4px;
   background-clip: padding-box;
   border-left: 1px solid var(--arrowpanel-border-color);
   box-shadow: 0 3px 5px hsla(210,4%,10%,.1),
               0 0 7px hsla(210,4%,10%,.1);
@@ -584,31 +584,39 @@ toolbarpaletteitem[place="palette"] > to
 }
 
 #PanelUI-update-status[update-status="failed"]::after {
   background-image: url(chrome://browser/skin/update-badge-failed.svg);
   background-color: #D90000;
 }
 
 #PanelUI-footer-addons > toolbarbutton {
-  background-color: #C7F5FF;
+  background-color: #FFEFBF;
   display: flex;
   flex: 1 1 0%;
   width: calc(@menuPanelWidth@ + 30px);
   padding-inline-start: 15px;
   border-inline-start-style: none;
 }
 
+#PanelUI-footer-addons > toolbarbutton:hover {
+  background-color: #FFE8A2;
+}
+
+#PanelUI-footer-addons > toolbarbutton:active {
+  background-color: #FFE38F;
+}
+
 #PanelUI-footer-addons > toolbarbutton > .toolbarbutton-icon {
   width: 14px;
   height: 14px;
 }
 
 #PanelUI-footer-addons > toolbarbutton::after {
-  background-image: url(chrome://browser/skin/addons/addon-badge.svg);
+  background: #FFBF00 url(chrome://browser/skin/update-badge-failed.svg) no-repeat center;
 }
 
 #PanelUI-fxa-status {
   display: flex;
   flex: 1 1 0%;
   width: 1px;
 }
 
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -11,17 +11,16 @@
   skin/classic/browser/blockedSite.css                         (../shared/blockedSite.css)
   skin/classic/browser/error-pages.css                         (../shared/error-pages.css)
 * skin/classic/browser/aboutProviderDirectory.css              (../shared/aboutProviderDirectory.css)
 * skin/classic/browser/aboutSessionRestore.css                 (../shared/aboutSessionRestore.css)
   skin/classic/browser/aboutSocialError.css                    (../shared/aboutSocialError.css)
   skin/classic/browser/aboutTabCrashed.css                     (../shared/aboutTabCrashed.css)
   skin/classic/browser/aboutWelcomeBack.css                    (../shared/aboutWelcomeBack.css)
   skin/classic/browser/content-contextmenu.svg                 (../shared/content-contextmenu.svg)
-  skin/classic/browser/addons/addon-badge.svg                  (../shared/addons/addon-badge.svg)
   skin/classic/browser/addons/addon-install-blocked.svg        (../shared/addons/addon-install-blocked.svg)
   skin/classic/browser/addons/addon-install-confirm.svg        (../shared/addons/addon-install-confirm.svg)
   skin/classic/browser/addons/addon-install-downloading.svg    (../shared/addons/addon-install-downloading.svg)
   skin/classic/browser/addons/addon-install-error.svg          (../shared/addons/addon-install-error.svg)
   skin/classic/browser/addons/addon-install-installed.svg      (../shared/addons/addon-install-installed.svg)
   skin/classic/browser/addons/addon-install-restart.svg        (../shared/addons/addon-install-restart.svg)
   skin/classic/browser/addons/addon-install-warning.svg        (../shared/addons/addon-install-warning.svg)
 * skin/classic/browser/addons/addon-install-anchor.svg         (../shared/addons/addon-install-anchor.svg)
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -741,17 +741,17 @@ toolbar[brighttext] .toolbarbutton-1 > .
 #nav-bar .toolbarbutton-1[type=panel] > .toolbarbutton-badge-stack,
 #nav-bar .toolbarbutton-1[type=menu]:not(#PanelUI-menu-button):not(#back-button):not(#forward-button):not(#new-tab-button) > .toolbarbutton-icon,
 #nav-bar .toolbarbutton-1[type=menu]:not(#PanelUI-menu-button) > .toolbarbutton-badge-stack,
 #nav-bar .toolbarbutton-1[type=menu] > .toolbarbutton-text /* hack for add-ons that forcefully display the label */ {
   padding-inline-end: 17px;
 }
 
 #nav-bar .toolbarbutton-1[type=panel] > .toolbarbutton-icon,
-#nav-bar .toolbarbutton-1[type=menu]:not(#PanelUI-menu-button):not(#back-button):not(#forward-button) > .toolbarbutton-icon {
+#nav-bar .toolbarbutton-1[type=menu]:not(#PanelUI-menu-button):not(#back-button):not(#forward-button):not(#new-tab-button) > .toolbarbutton-icon {
   /* horizontal padding + border + icon width */
   max-width: 43px;
 }
 
 #nav-bar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker {
   margin-inline-start: -15px;
 }
 
--- a/build/autoconf/clang-plugin.m4
+++ b/build/autoconf/clang-plugin.m4
@@ -37,29 +37,34 @@ if test -n "$ENABLE_CLANG_PLUGIN"; then
     if test -z "$LLVMCONFIG"; then
         AC_MSG_ERROR([Cannot find an llvm-config binary for building a clang plugin])
     fi
     dnl For some reason the llvm-config downloaded from clang.llvm.org for clang3_8
     dnl produces a -isysroot flag for a sysroot which might not ship when passed
     dnl --cxxflags. We use sed to remove this argument so that builds work on OSX
     LLVM_CXXFLAGS=`$LLVMCONFIG --cxxflags | sed -e 's/-isysroot [[^ ]]*//'`
 
-    dnl We are loaded into clang, so we don't need to link to very many things,
-    dnl we just need to link to clangASTMatchers because it is not used by clang
     LLVM_LDFLAGS=`$LLVMCONFIG --ldflags | tr '\n' ' '`
 
     if test "${HOST_OS_ARCH}" = "Darwin"; then
         dnl We need to make sure that we use the symbols coming from the clang
         dnl binary. In order to do this, we need to pass -flat_namespace and
         dnl -undefined suppress to the linker. This makes sure that we link the
         dnl symbols into the flat namespace provided by clang, and thus get
         dnl access to all of the symbols which are undefined in our dylib as we
         dnl are building it right now, and also that we don't fail the build
         dnl due to undefined symbols (which will be provided by clang).
-        CLANG_LDFLAGS="-Wl,-flat_namespace -Wl,-undefined,suppress -lclangASTMatchers"
+        CLANG_LDFLAGS="-Wl,-flat_namespace -Wl,-undefined,suppress"
+        dnl We are loaded into clang, so we don't need to link to very many things,
+        dnl we just need to link to clangASTMatchers because it is not used by clang
+        CLANG_LDFLAGS="$CLANG_LDFLAGS `$LLVMCONFIG --prefix`/lib/libclangASTMatchers.a"
+        dnl We need to remove -L/path/to/clang/lib from LDFLAGS to ensure that we
+        dnl don't accidentally link against the libc++ there which is a newer
+        dnl version that what our build machines have installed.
+        LLVM_LDFLAGS=`echo "$LLVM_LDFLAGS" | sed -E 's/-L[[^ ]]+\/clang\/lib//'`
     elif test "${HOST_OS_ARCH}" = "WINNT"; then
         CLANG_LDFLAGS="clang.lib"
     else
         CLANG_LDFLAGS="-lclangASTMatchers"
     fi
 
     if test -n "$CLANG_CL"; then
         dnl The llvm-config coming with clang-cl may give us arguments in the
--- a/build/build-clang/README
+++ b/build/build-clang/README
@@ -34,20 +34,22 @@ build-clang.py accepts a JSON config for
 * clang_repo: SVN path to the Clang repo.
 * compiler_repo: SVN path to the compiler-rt repo.
 * libcxx_repo: SVN path to the libcxx repo.
 * libcxxabi_repo: SVN path to the libcxxabi repo.
 * python_path: Path to the Python 2.7 installation on the machine building clang.
 * gcc_dir: Path to the gcc toolchain installation, only required on Linux.
 * cc: Path to the bootsraping C Compiler.
 * cxx: Path to the bootsraping C++ Compiler.
+* as: Path to the assembler tool.
 * ar: Path to the library archiver tool.
-* ranlib: Path to the ranlib tool.
+* ranlib: Path to the ranlib tool (optional).
+* libtool: Path to the libtool tool (optional).
 * ld: Path to the linker.
-* patches: Optional list of patches to apply per platform.  Supported platforms: macosx64, linux32, linux64.  The default is Release.
+* patches: Optional list of patches to apply.
 * build_type: The type of build to make.  Supported types: Release, Debug, RelWithDebInfo or MinSizeRel.
 * build_libcxx: Whether to build with libcxx.  The default is false.
 * build_clang_tidy: Whether to build clang-tidy with the Mozilla checks imported.  The default is false.
 * osx_cross_compile: Whether to invoke CMake for OS X cross compile builds.
 * assertions: Whether to enable LLVM assertions.  The default is false.
 
 Environment Variables
 ---------------------
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -165,47 +165,29 @@ def svn_co(source_dir, url, directory, r
     run_in(source_dir, ["svn", "co", "-q", "-r", revision, url, directory])
 
 
 def svn_update(directory, revision):
     run_in(directory, ["svn", "update", "-q", "-r", revision])
     run_in(directory, ["svn", "revert", "-q", "-R", revision])
 
 
-def get_platform():
-    p = platform.system()
-    if p == "Darwin":
-        return "macosx64"
-    elif p == "Linux":
-        if platform.architecture() == "AMD64":
-            return "linux64"
-        else:
-            return "linux32"
-    elif p == "Windows":
-        if platform.architecture() == "AMD64":
-            return "win64"
-        else:
-            return "win32"
-    else:
-        raise NotImplementedError("Not supported platform")
-
-
 def is_darwin():
     return platform.system() == "Darwin"
 
 
 def is_linux():
     return platform.system() == "Linux"
 
 
 def is_windows():
     return platform.system() == "Windows"
 
 
-def build_one_stage(cc, cxx, ld, ar, ranlib,
+def build_one_stage(cc, cxx, asm, ld, ar, ranlib, libtool,
                     src_dir, stage_dir, build_libcxx,
                     osx_cross_compile, build_type, assertions,
                     python_path, gcc_dir, libcxx_include_dir):
     if not os.path.exists(stage_dir):
         os.mkdir(stage_dir)
 
     build_dir = stage_dir + "/build"
     inst_dir = stage_dir + "/clang"
@@ -220,50 +202,53 @@ def build_one_stage(cc, cxx, ld, ar, ran
 
     # cmake doesn't deal well with backslashes in paths.
     def slashify_path(path):
         return path.replace('\\', '/')
 
     cmake_args = ["-GNinja",
                   "-DCMAKE_C_COMPILER=%s" % slashify_path(cc[0]),
                   "-DCMAKE_CXX_COMPILER=%s" % slashify_path(cxx[0]),
-                  "-DCMAKE_ASM_COMPILER=%s" % slashify_path(cc[0]),
+                  "-DCMAKE_ASM_COMPILER=%s" % slashify_path(asm[0]),
                   "-DCMAKE_LINKER=%s" % slashify_path(ld[0]),
                   "-DCMAKE_AR=%s" % slashify_path(ar),
                   "-DCMAKE_C_FLAGS=%s" % ' '.join(cc[1:]),
                   "-DCMAKE_CXX_FLAGS=%s" % ' '.join(cxx[1:]),
+                  "-DCMAKE_ASM_FLAGS=%s" % ' '.join(asm[1:]),
                   "-DCMAKE_EXE_LINKER_FLAGS=%s" % ' '.join(ld[1:]),
                   "-DCMAKE_SHARED_LINKER_FLAGS=%s" % ' '.join(ld[1:]),
                   "-DCMAKE_BUILD_TYPE=%s" % build_type,
                   "-DLLVM_TARGETS_TO_BUILD=X86;ARM",
                   "-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions else "OFF"),
                   "-DPYTHON_EXECUTABLE=%s" % slashify_path(python_path),
                   "-DCMAKE_INSTALL_PREFIX=%s" % inst_dir,
                   "-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"),
                   "-DLIBCXX_LIBCPPABI_VERSION=\"\"",
                   src_dir];
     if is_windows():
         cmake_args.insert(-1, "-DLLVM_EXPORT_SYMBOLS_FOR_PLUGINS=ON")
         cmake_args.insert(-1, "-DLLVM_USE_CRT_RELEASE=MT")
     if ranlib is not None:
         cmake_args += ["-DCMAKE_RANLIB=%s" % slashify_path(ranlib)]
+    if libtool is not None:
+        cmake_args += ["-DCMAKE_LIBTOOL=%s" % slashify_path(libtool)]
     if osx_cross_compile:
         cmake_args += ["-DCMAKE_SYSTEM_NAME=Darwin",
                        "-DCMAKE_SYSTEM_VERSION=10.10",
                        "-DLLVM_ENABLE_THREADS=OFF",
                        "-DLIBCXXABI_LIBCXX_INCLUDES=%s" % libcxx_include_dir,
                        "-DCMAKE_OSX_SYSROOT=%s" % slashify_path(os.getenv("CROSS_SYSROOT")),
                        "-DCMAKE_FIND_ROOT_PATH=%s" % slashify_path(os.getenv("CROSS_CCTOOLS_PATH")),
                        "-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER",
                        "-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY",
                        "-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY",
                        "-DCMAKE_MACOSX_RPATH=@executable_path",
                        "-DCMAKE_OSX_ARCHITECTURES=x86_64",
                        "-DDARWIN_osx_ARCHS=x86_64",
-                       "-DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-apple-darwin10"]
+                       "-DLLVM_DEFAULT_TARGET_TRIPLE=x86_64-apple-darwin11"]
     build_package(build_dir, cmake_args)
 
     if is_linux():
         install_libgcc(gcc_dir, inst_dir)
     # For some reasons the import library clang.lib of clang.exe is not
     # installed, so we copy it by ourselves.
     if is_windows():
         install_import_library(build_dir, inst_dir)
@@ -459,19 +444,23 @@ if __name__ == "__main__":
     if "gcc_dir" in config:
         gcc_dir = config["gcc_dir"]
         if not os.path.exists(gcc_dir):
             raise ValueError("gcc_dir must point to an existing path")
     if is_linux() and gcc_dir is None:
         raise ValueError("Config file needs to set gcc_dir")
     cc = get_tool(config, "cc")
     cxx = get_tool(config, "cxx")
+    asm = get_tool(config, "ml" if is_windows() else "as")
     ld = get_tool(config, "link" if is_windows() else "ld")
     ar = get_tool(config, "lib" if is_windows() else "ar")
     ranlib = None if is_windows() else get_tool(config, "ranlib")
+    libtool = None
+    if "libtool" in config:
+        libtool = get_tool(config, "libtool")
 
     if not os.path.exists(source_dir):
         os.makedirs(source_dir)
 
     def checkout_or_update(repo, checkout_dir):
         if os.path.exists(checkout_dir):
             svn_update(checkout_dir, llvm_revision)
         else:
@@ -480,17 +469,17 @@ if __name__ == "__main__":
     checkout_or_update(llvm_repo, llvm_source_dir)
     checkout_or_update(clang_repo, clang_source_dir)
     checkout_or_update(compiler_repo, compiler_rt_source_dir)
     checkout_or_update(libcxx_repo, libcxx_source_dir)
     if libcxxabi_repo:
         checkout_or_update(libcxxabi_repo, libcxxabi_source_dir)
     if extra_repo:
         checkout_or_update(extra_repo, extra_source_dir)
-    for p in config.get("patches", {}).get(get_platform(), []):
+    for p in config.get("patches", []):
         patch(p, source_dir)
 
     symlinks = [(source_dir + "/clang",
                  llvm_source_dir + "/tools/clang"),
                 (source_dir + "/extra",
                  llvm_source_dir + "/tools/clang/tools/extra"),
                 (source_dir + "/compiler-rt",
                  llvm_source_dir + "/projects/compiler-rt"),
@@ -520,91 +509,100 @@ if __name__ == "__main__":
 
     final_stage_dir = stage1_dir
 
     if is_darwin():
         extra_cflags = []
         extra_cxxflags = ["-stdlib=libc++"]
         extra_cflags2 = []
         extra_cxxflags2 = ["-stdlib=libc++"]
+        extra_asmflags = []
         extra_ldflags = []
     elif is_linux():
         extra_cflags = ["-static-libgcc"]
         extra_cxxflags = ["-static-libgcc", "-static-libstdc++"]
         extra_cflags2 = ["-fPIC"]
         extra_cxxflags2 = ["-fPIC", "-static-libstdc++"]
+        extra_asmflags = []
         extra_ldflags = []
 
         if os.environ.has_key('LD_LIBRARY_PATH'):
             os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']);
         else:
             os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
     elif is_windows():
         extra_cflags = []
         extra_cxxflags = []
         # clang-cl would like to figure out what it's supposed to be emulating
         # by looking at an MSVC install, but we don't really have that here.
         # Force things on.
         extra_cflags2 = []
         extra_cxxflags2 = ['-fms-compatibility-version=19.00.24213', '-Xclang', '-std=c++14']
+        extra_asmflags = []
         extra_ldflags = []
 
     if osx_cross_compile:
         # undo the damage done in the is_linux() block above, and also simulate
         # the is_darwin() block above.
         extra_cflags = []
         extra_cxxflags = ["-stdlib=libc++"]
         extra_cxxflags2 = ["-stdlib=libc++"]
 
-        extra_flags = ["-target", "x86_64-apple-darwin10", "-mlinker-version=136",
+        extra_flags = ["-target", "x86_64-apple-darwin11", "-mlinker-version=137",
                        "-B", "%s/bin" %  os.getenv("CROSS_CCTOOLS_PATH"),
                        "-isysroot", os.getenv("CROSS_SYSROOT"),
                        # technically the sysroot flag there should be enough to deduce this,
                        # but clang needs some help to figure this out.
                        "-I%s/usr/include" % os.getenv("CROSS_SYSROOT"),
                        "-iframework", "%s/System/Library/Frameworks" % os.getenv("CROSS_SYSROOT")]
         extra_cflags += extra_flags
         extra_cxxflags += extra_flags
         extra_cflags2 += extra_flags
         extra_cxxflags2 += extra_flags
+        extra_asmflags += extra_flags
         extra_ldflags = ["-Wl,-syslibroot,%s" % os.getenv("CROSS_SYSROOT"),
                          "-Wl,-dead_strip"]
 
     build_one_stage(
         [cc] + extra_cflags,
         [cxx] + extra_cxxflags,
+        [asm] + extra_asmflags,
         [ld] + extra_ldflags,
-        ar, ranlib,
+        ar, ranlib, libtool,
         llvm_source_dir, stage1_dir, build_libcxx, osx_cross_compile,
         build_type, assertions, python_path, gcc_dir, libcxx_include_dir)
 
     if stages > 1:
         stage2_dir = build_dir + '/stage2'
         stage2_inst_dir = stage2_dir + '/clang'
         final_stage_dir = stage2_dir
         build_one_stage(
             [stage1_inst_dir + "/bin/%s%s" %
                 (cc_name, exe_ext)] + extra_cflags2,
             [stage1_inst_dir + "/bin/%s%s" %
                 (cxx_name, exe_ext)] + extra_cxxflags2,
+            [stage1_inst_dir + "/bin/%s%s" %
+                (cc_name, exe_ext)] + extra_asmflags,
             [ld] + extra_ldflags,
-            ar, ranlib,
+            ar, ranlib, libtool,
             llvm_source_dir, stage2_dir, build_libcxx, osx_cross_compile,
             build_type, assertions, python_path, gcc_dir, libcxx_include_dir)
 
     if stages > 2:
         stage3_dir = build_dir + '/stage3'
         final_stage_dir = stage3_dir
         build_one_stage(
             [stage2_inst_dir + "/bin/%s%s" %
                 (cc_name, exe_ext)] + extra_cflags2,
             [stage2_inst_dir + "/bin/%s%s" %
                 (cxx_name, exe_ext)] + extra_cxxflags2,
+            [stage2_inst_dir + "/bin/%s%s" %
+                (cc_name, exe_ext)] + extra_asmflags,
             [ld] + extra_ldflags,
-            ar, ranlib,
+            ar, ranlib, libtool,
             llvm_source_dir, stage3_dir, build_libcxx, osx_cross_compile,
             build_type, assertions, python_path, gcc_dir, libcxx_include_dir)
 
     package_name = "clang"
     if build_clang_tidy:
         prune_final_dir_for_clang_tidy(os.path.join(final_stage_dir, "clang"))
         package_name = "clang-tidy"
 
--- a/build/build-clang/clang-static-analysis-linux64.json
+++ b/build/build-clang/clang-static-analysis-linux64.json
@@ -8,20 +8,13 @@
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_390/final",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_390/final",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_390/final",
     "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_390/final",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/home/worker/workspace/build/src/gcc",
     "cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
     "cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
-    "patches": {
-        "macosx64": [
-          "llvm-debug-frame.patch"
-        ],
-        "linux64": [
-          "llvm-debug-frame.patch"
-        ],
-        "linux32": [
-          "llvm-debug-frame.patch"
-        ]
-    }
+    "as": "/home/worker/workspace/build/src/gcc/bin/gcc",
+    "patches": [
+      "llvm-debug-frame.patch"
+    ]
 }
--- a/build/build-clang/clang-static-analysis-macosx64.json
+++ b/build/build-clang/clang-static-analysis-macosx64.json
@@ -1,29 +1,29 @@
 {
-    "llvm_revision": "262557",
-    "stages": "3",
+    "llvm_revision": "290136",
+    "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_380/final",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_380/final",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_380/final",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_380/final",
-    "python_path": "/usr/local/bin/python2.7",
-    "cc": "/Users/cltbld/clang/bin/clang",
-    "cxx": "/Users/cltbld/clang/bin/clang++",
-    "patches": {
-        "macosx64": [
-          "disable-mac-tsan.patch",
-          "llvm-debug-frame.patch",
-          "return-empty-string-non-mangled.patch"
-        ],
-        "linux64": [
-          "llvm-debug-frame.patch",
-          "return-empty-string-non-mangled.patch"
-        ],
-        "linux32": [
-          "llvm-debug-frame.patch",
-          "return-empty-string-non-mangled.patch"
-        ]
-    }
+    "osx_cross_compile": true,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_390/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_390/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_390/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_390/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_390/final",
+    "python_path": "/usr/bin/python2.7",
+    "gcc_dir": "/home/worker/workspace/build/src/gcc",
+    "cc": "/home/worker/workspace/build/src/clang/bin/clang",
+    "cxx": "/home/worker/workspace/build/src/clang/bin/clang++",
+    "as": "/home/worker/workspace/build/src/clang/bin/clang",
+    "ar": "/home/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ar",
+    "ranlib": "/home/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ranlib",
+    "libtool": "/home/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-libtool",
+    "ld": "/home/worker/workspace/build/src/clang/bin/clang",
+    "patches":[
+      "llvm-debug-frame.patch",
+      "compiler-rt-cross-compile.patch",
+      "pr28831-r280042.patch",
+      "r277806.patch",
+      "r285657.patch"
+    ]
 }
--- a/build/build-clang/clang-static-analysis-win32.json
+++ b/build/build-clang/clang-static-analysis-win32.json
@@ -5,12 +5,10 @@
     "build_type": "Release",
     "assertions": false,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
-    "cxx": "cl.exe",
-    "patches": {
-    }
+    "cxx": "cl.exe"
 }
--- a/build/build-clang/clang-static-analysis-win64.json
+++ b/build/build-clang/clang-static-analysis-win64.json
@@ -6,11 +6,10 @@
     "assertions": false,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe",
-    "patches": {
-    }
+    "ml": "ml64.exe"
 }
--- a/build/build-clang/clang-tidy-linux64.json
+++ b/build/build-clang/clang-tidy-linux64.json
@@ -9,10 +9,11 @@
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/home/worker/workspace/build/src/gcc",
     "cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
-    "cxx": "/home/worker/workspace/build/src/gcc/bin/g++"
+    "cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
+    "as": "/home/worker/workspace/build/src/gcc/bin/gcc"
 }
--- a/build/build-clang/clang-tidy-macosx64.json
+++ b/build/build-clang/clang-tidy-macosx64.json
@@ -11,12 +11,17 @@
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/home/worker/workspace/build/src/gcc",
     "cc": "/home/worker/workspace/build/src/clang/bin/clang",
     "cxx": "/home/worker/workspace/build/src/clang/bin/clang++",
-    "ar": "/home/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin10-ar",
-    "ranlib": "/home/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin10-ranlib",
-    "ld": "/home/worker/workspace/build/src/clang/bin/clang"
+    "as": "/home/worker/workspace/build/src/clang/bin/clang",
+    "ar": "/home/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ar",
+    "ranlib": "/home/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ranlib",
+    "ld": "/home/worker/workspace/build/src/clang/bin/clang",
+    "patches": [
+      "llvm-debug-frame.patch",
+      "compiler-rt-cross-compile.patch"
+    ]
 }
--- a/build/build-clang/clang-tidy-win64.json
+++ b/build/build-clang/clang-tidy-win64.json
@@ -7,10 +7,11 @@
     "build_clang_tidy": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
-    "cxx": "cl.exe"
+    "cxx": "cl.exe",
+    "ml": "ml64.exe"
 }
new file mode 100644
--- /dev/null
+++ b/build/build-clang/compiler-rt-cross-compile.patch
@@ -0,0 +1,15 @@
+Add `-target x86_64-apple-darwin11' to the compiler-rt overridden CFLAGS
+
+diff --git a/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake b/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake
+index 28d398672..aac68bf36 100644
+--- a/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake
++++ b/compiler-rt/cmake/Modules/CompilerRTDarwinUtils.cmake
+@@ -265,7 +265,7 @@ endfunction()
+ macro(darwin_add_builtin_libraries)
+   set(DARWIN_EXCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Darwin-excludes)
+ 
+-  set(CFLAGS "-fPIC -O3 -fvisibility=hidden -DVISIBILITY_HIDDEN -Wall -fomit-frame-pointer")
++  set(CFLAGS "-fPIC -O3 -fvisibility=hidden -DVISIBILITY_HIDDEN -Wall -fomit-frame-pointer -target x86_64-apple-darwin11 -isysroot ${CMAKE_OSX_SYSROOT} -I${CMAKE_OSX_SYSROOT}/usr/include")
+   set(CMAKE_C_FLAGS "")
+   set(CMAKE_CXX_FLAGS "")
+   set(CMAKE_ASM_FLAGS "")
deleted file mode 100644
--- a/build/build-clang/disable-mac-tsan.patch
+++ /dev/null
@@ -1,11 +0,0 @@
---- a/compiler-rt/cmake/config-ix.cmake
-+++ b/compiler-rt/cmake/config-ix.cmake
-@@ -617,7 +617,7 @@
- endif()
- 
- if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH AND
--    OS_NAME MATCHES "Darwin|Linux|FreeBSD")
-+    OS_NAME MATCHES "Linux|FreeBSD")
-   set(COMPILER_RT_HAS_TSAN TRUE)
- else()
-   set(COMPILER_RT_HAS_TSAN FALSE)
new file mode 100644
--- /dev/null
+++ b/build/build-clang/pr28831-r280042.patch
@@ -0,0 +1,19 @@
+Backport the fix to PR28831 plus its follow-up (r280042)
+
+diff --git a/libcxx/lib/CMakeLists.txt b/libcxx/lib/CMakeLists.txt
+index afc388e76..4f43f3711 100644
+--- a/libcxx/lib/CMakeLists.txt
++++ b/libcxx/lib/CMakeLists.txt
+@@ -115,9 +115,9 @@ if ( APPLE AND (LIBCXX_CXX_ABI_LIBNAME STREQUAL "libcxxabi" OR
+       "-Wl,-unexported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/libc++unexp.exp"
+       "/usr/lib/libSystem.B.dylib")
+   else()
+-    if ( ${CMAKE_OSX_SYSROOT} )
+-      list(FIND ${CMAKE_OSX_ARCHITECTURES} "armv7" OSX_HAS_ARMV7)
+-      if (OSX_HAS_ARMV7)
++    if (DEFINED CMAKE_OSX_SYSROOT)
++      list(FIND CMAKE_OSX_ARCHITECTURES "armv7" OSX_HAS_ARMV7)
++      if (NOT OSX_HAS_ARMV7 EQUAL -1)
+         set(OSX_RE_EXPORT_LINE
+           "${CMAKE_OSX_SYSROOT}/usr/lib/libc++abi.dylib"
+           "-Wl,-reexported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/libc++sjlj-abi.exp")
deleted file mode 100644
--- a/build/build-clang/query-selector-visibility.patch
+++ /dev/null
@@ -1,79 +0,0 @@
-commit 865b9340996f9f9d04b73b187248737dc6fd845e
-Author: Michael Wu <mwu@mozilla.com>
-Date:   Mon Sep 14 17:47:21 2015 -0400
-
-    Add support for querying the visibility of a cursor
-
-diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h
-index fad9cfa..311bfcb 100644
---- a/clang/include/clang-c/Index.h
-+++ b/clang/include/clang-c/Index.h
-@@ -2440,6 +2440,24 @@ enum CXLinkageKind {
- CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor);
- 
- /**
-+ * \brief Describe the visibility of the entity referred to by a cursor.
-+ */
-+enum CXVisibilityKind {
-+  /** \brief This value indicates that no visibility information is available
-+   * for a provided CXCursor. */
-+  CXVisibility_Invalid,
-+
-+  /** \brief Symbol not seen by the linker. */
-+  CXVisibility_Hidden,
-+  /** \brief Symbol seen by the linker but resolves to a symbol inside this object. */
-+  CXVisibility_Protected,
-+  /** \brief Symbol seen by the linker and acts like a normal symbol. */
-+  CXVisibility_Default,
-+};
-+
-+CINDEX_LINKAGE enum CXVisibilityKind clang_getCursorVisibility(CXCursor cursor);
-+
-+/**
-  * \brief Determine the availability of the entity that this cursor refers to,
-  * taking the current target platform into account.
-  *
-diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
-index 8225a6c..9fa18d3 100644
---- a/clang/tools/libclang/CIndex.cpp
-+++ b/clang/tools/libclang/CIndex.cpp
-@@ -6361,6 +6361,27 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) {
- } // end: extern "C"
- 
- //===----------------------------------------------------------------------===//
-+// Operations for querying visibility of a cursor.
-+//===----------------------------------------------------------------------===//
-+
-+extern "C" {
-+CXVisibilityKind clang_getCursorVisibility(CXCursor cursor) {
-+  if (!clang_isDeclaration(cursor.kind))
-+    return CXVisibility_Invalid;
-+
-+  const Decl *D = cxcursor::getCursorDecl(cursor);
-+  if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D))
-+    switch (ND->getVisibility()) {
-+      case HiddenVisibility: return CXVisibility_Hidden;
-+      case ProtectedVisibility: return CXVisibility_Protected;
-+      case DefaultVisibility: return CXVisibility_Default;
-+    };
-+
-+  return CXVisibility_Invalid;
-+}
-+} // end: extern "C"
-+
-+//===----------------------------------------------------------------------===//
- // Operations for querying language of a cursor.
- //===----------------------------------------------------------------------===//
- 
-diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports
-index f6a7175..a919a8e 100644
---- a/clang/tools/libclang/libclang.exports
-+++ b/clang/tools/libclang/libclang.exports
-@@ -173,6 +173,7 @@ clang_getCursorSemanticParent
- clang_getCursorSpelling
- clang_getCursorType
- clang_getCursorUSR
-+clang_getCursorVisibility
- clang_getDeclObjCTypeEncoding
- clang_getDefinitionSpellingAndExtent
- clang_getDiagnostic
new file mode 100644
--- /dev/null
+++ b/build/build-clang/r277806.patch
@@ -0,0 +1,321 @@
+commit eca7f4535bffb1c86cb1620b9d9425ff3ce31ab9
+Author: John Brawn <john.brawn@arm.com>
+Date:   Fri Aug 5 11:01:08 2016 +0000
+
+    Reapply r276973 "Adjust Registry interface to not require plugins to export a registry"
+    
+    This differs from the previous version by being more careful about template
+    instantiation/specialization in order to prevent errors when building with
+    clang -Werror. Specifically:
+     * begin is not defined in the template and is instead instantiated when Head
+       is. I think the warning when we don't do that is wrong (PR28815) but for now
+       at least do it this way to avoid the warning.
+     * Instead of performing template specializations in LLVM_INSTANTIATE_REGISTRY
+       instead provide a template definition then do explicit instantiation. No
+       compiler I've tried has problems with doing it the other way, but strictly
+       speaking it's not permitted by the C++ standard so better safe than sorry.
+    
+    Original commit message:
+    
+    Currently the Registry class contains the vestiges of a previous attempt to
+    allow plugins to be used on Windows without using BUILD_SHARED_LIBS, where a
+    plugin would have its own copy of a registry and export it to be imported by
+    the tool that's loading the plugin. This only works if the plugin is entirely
+    self-contained with the only interface between the plugin and tool being the
+    registry, and in particular this conflicts with how IR pass plugins work.
+    
+    This patch changes things so that instead the add_node function of the registry
+    is exported by the tool and then imported by the plugin, which solves this
+    problem and also means that instead of every plugin having to export every
+    registry they use instead LLVM only has to export the add_node functions. This
+    allows plugins that use a registry to work on Windows if
+    LLVM_EXPORT_SYMBOLS_FOR_PLUGINS is used.
+    
+    
+    git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@277806 91177308-0d34-0410-b5e6-96231b3b80d8
+
+diff --git a/llvm/include/llvm/Support/Registry.h b/llvm/include/llvm/Support/Registry.h
+index 27f025fcd08..9557f56093b 100644
+--- a/llvm/include/llvm/Support/Registry.h
++++ b/llvm/include/llvm/Support/Registry.h
+@@ -44,6 +44,7 @@ namespace llvm {
+   template <typename T>
+   class Registry {
+   public:
++    typedef T type;
+     typedef SimpleRegistryEntry<T> entry;
+ 
+     class node;
+@@ -69,13 +70,14 @@ namespace llvm {
+       node(const entry &V) : Next(nullptr), Val(V) {}
+     };
+ 
+-    static void add_node(node *N) {
+-      if (Tail)
+-        Tail->Next = N;
+-      else
+-        Head = N;
+-      Tail = N;
+-    }
++    /// Add a node to the Registry: this is the interface between the plugin and
++    /// the executable.
++    ///
++    /// This function is exported by the executable and called by the plugin to
++    /// add a node to the executable's registry. Therefore it's not defined here
++    /// to avoid it being instantiated in the plugin and is instead defined in
++    /// the executable (see LLVM_INSTANTIATE_REGISTRY below).
++    static void add_node(node *N);
+ 
+     /// Iterators for registry entries.
+     ///
+@@ -92,7 +94,9 @@ namespace llvm {
+       const entry *operator->() const { return &Cur->Val; }
+     };
+ 
+-    static iterator begin() { return iterator(Head); }
++    // begin is not defined here in order to avoid usage of an undefined static
++    // data member, instead it's instantiated by LLVM_INSTANTIATE_REGISTRY.
++    static iterator begin();
+     static iterator end()   { return iterator(nullptr); }
+ 
+     static iterator_range<iterator> entries() {
+@@ -120,61 +124,37 @@ namespace llvm {
+         add_node(&Node);
+       }
+     };
+-
+-    /// A dynamic import facility.  This is used on Windows to
+-    /// import the entries added in the plugin.
+-    static void import(sys::DynamicLibrary &DL, const char *RegistryName) {
+-      typedef void *(*GetRegistry)();
+-      std::string Name("LLVMGetRegistry_");
+-      Name.append(RegistryName);
+-      GetRegistry Getter =
+-          (GetRegistry)(intptr_t)DL.getAddressOfSymbol(Name.c_str());
+-      if (Getter) {
+-        // Call the getter function in order to get the full copy of the
+-        // registry defined in the plugin DLL, and copy them over to the
+-        // current Registry.
+-        typedef std::pair<const node *, const node *> Info;
+-        Info *I = static_cast<Info *>(Getter());
+-        iterator begin(I->first);
+-        iterator end(I->second);
+-        for (++end; begin != end; ++begin) {
+-          // This Node object needs to remain alive for the
+-          // duration of the program.
+-          add_node(new node(*begin));
+-        }
+-      }
+-    }
+-
+-    /// Retrieve the data to be passed across DLL boundaries when
+-    /// importing registries from another DLL on Windows.
+-    static void *exportRegistry() {
+-      static std::pair<const node *, const node *> Info(Head, Tail);
+-      return &Info;
+-    }
+   };
+-
+-  
+-  // Since these are defined in a header file, plugins must be sure to export
+-  // these symbols.
+-  template <typename T>
+-  typename Registry<T>::node *Registry<T>::Head;
+-
+-  template <typename T>
+-  typename Registry<T>::node *Registry<T>::Tail;
+ } // end namespace llvm
+ 
+-#ifdef LLVM_ON_WIN32
+-#define LLVM_EXPORT_REGISTRY(REGISTRY_CLASS)                                   \
+-  extern "C" {                                                                 \
+-  __declspec(dllexport) void *__cdecl LLVMGetRegistry_##REGISTRY_CLASS() {     \
+-    return REGISTRY_CLASS::exportRegistry();                                   \
+-  }                                                                            \
++/// Instantiate a registry class.
++///
++/// This provides template definitions of add_node, begin, and the Head and Tail
++/// pointers, then explicitly instantiates them. We could explicitly specialize
++/// them, instead of the two-step process of define then instantiate, but
++/// strictly speaking that's not allowed by the C++ standard (we would need to
++/// have explicit specialization declarations in all translation units where the
++/// specialization is used) so we don't.
++#define LLVM_INSTANTIATE_REGISTRY(REGISTRY_CLASS) \
++  namespace llvm { \
++  template<typename T> typename Registry<T>::node *Registry<T>::Head = nullptr;\
++  template<typename T> typename Registry<T>::node *Registry<T>::Tail = nullptr;\
++  template<typename T> \
++  void Registry<T>::add_node(typename Registry<T>::node *N) { \
++    if (Tail) \
++      Tail->Next = N; \
++    else \
++      Head = N; \
++    Tail = N; \
++  } \
++  template<typename T> typename Registry<T>::iterator Registry<T>::begin() { \
++    return iterator(Head); \
++  } \
++  template REGISTRY_CLASS::node *Registry<REGISTRY_CLASS::type>::Head; \
++  template REGISTRY_CLASS::node *Registry<REGISTRY_CLASS::type>::Tail; \
++  template \
++  void Registry<REGISTRY_CLASS::type>::add_node(REGISTRY_CLASS::node*); \
++  template REGISTRY_CLASS::iterator Registry<REGISTRY_CLASS::type>::begin(); \
+   }
+-#define LLVM_IMPORT_REGISTRY(REGISTRY_CLASS, DL)                               \
+-  REGISTRY_CLASS::import(DL, #REGISTRY_CLASS)
+-#else
+-#define LLVM_EXPORT_REGISTRY(REGISTRY_CLASS)
+-#define LLVM_IMPORT_REGISTRY(REGISTRY_CLASS, DL)
+-#endif
+ 
+ #endif // LLVM_SUPPORT_REGISTRY_H
+diff --git a/llvm/lib/CodeGen/GCMetadataPrinter.cpp b/llvm/lib/CodeGen/GCMetadataPrinter.cpp
+index bb8cfa1cc80..d183c7f2980 100644
+--- a/llvm/lib/CodeGen/GCMetadataPrinter.cpp
++++ b/llvm/lib/CodeGen/GCMetadataPrinter.cpp
+@@ -14,6 +14,8 @@
+ #include "llvm/CodeGen/GCMetadataPrinter.h"
+ using namespace llvm;
+ 
++LLVM_INSTANTIATE_REGISTRY(GCMetadataPrinterRegistry)
++
+ GCMetadataPrinter::GCMetadataPrinter() {}
+ 
+ GCMetadataPrinter::~GCMetadataPrinter() {}
+diff --git a/llvm/lib/CodeGen/GCStrategy.cpp b/llvm/lib/CodeGen/GCStrategy.cpp
+index 554d326942e..31ab86fdf27 100644
+--- a/llvm/lib/CodeGen/GCStrategy.cpp
++++ b/llvm/lib/CodeGen/GCStrategy.cpp
+@@ -16,6 +16,8 @@
+ 
+ using namespace llvm;
+ 
++LLVM_INSTANTIATE_REGISTRY(GCRegistry)
++
+ GCStrategy::GCStrategy()
+     : UseStatepoints(false), NeededSafePoints(0), CustomReadBarriers(false),
+       CustomWriteBarriers(false), CustomRoots(false), InitRoots(true),
+
+commit 0cfb8c87dfc0a8366d6db83f93aa50e9514dbf9d
+Author: John Brawn <john.brawn@arm.com>
+Date:   Fri Aug 5 11:01:08 2016 +0000
+
+    Reapply r276973 "Adjust Registry interface to not require plugins to export a registry"
+    
+    This differs from the previous version by being more careful about template
+    instantiation/specialization in order to prevent errors when building with
+    clang -Werror. Specifically:
+     * begin is not defined in the template and is instead instantiated when Head
+       is. I think the warning when we don't do that is wrong (PR28815) but for now
+       at least do it this way to avoid the warning.
+     * Instead of performing template specializations in LLVM_INSTANTIATE_REGISTRY
+       instead provide a template definition then do explicit instantiation. No
+       compiler I've tried has problems with doing it the other way, but strictly
+       speaking it's not permitted by the C++ standard so better safe than sorry.
+    
+    Original commit message:
+    
+    Currently the Registry class contains the vestiges of a previous attempt to
+    allow plugins to be used on Windows without using BUILD_SHARED_LIBS, where a
+    plugin would have its own copy of a registry and export it to be imported by
+    the tool that's loading the plugin. This only works if the plugin is entirely
+    self-contained with the only interface between the plugin and tool being the
+    registry, and in particular this conflicts with how IR pass plugins work.
+    
+    This patch changes things so that instead the add_node function of the registry
+    is exported by the tool and then imported by the plugin, which solves this
+    problem and also means that instead of every plugin having to export every
+    registry they use instead LLVM only has to export the add_node functions. This
+    allows plugins that use a registry to work on Windows if
+    LLVM_EXPORT_SYMBOLS_FOR_PLUGINS is used.
+    
+    
+    git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@277806 91177308-0d34-0410-b5e6-96231b3b80d8
+
+diff --git a/clang/examples/AnnotateFunctions/CMakeLists.txt b/clang/examples/AnnotateFunctions/CMakeLists.txt
+index cf564d527d..5684abf238 100644
+--- a/clang/examples/AnnotateFunctions/CMakeLists.txt
++++ b/clang/examples/AnnotateFunctions/CMakeLists.txt
+@@ -1,4 +1,4 @@
+-add_llvm_loadable_module(AnnotateFunctions AnnotateFunctions.cpp)
++add_llvm_loadable_module(AnnotateFunctions AnnotateFunctions.cpp PLUGIN_TOOL clang)
+ 
+ if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
+   target_link_libraries(AnnotateFunctions PRIVATE
+diff --git a/clang/examples/PrintFunctionNames/CMakeLists.txt b/clang/examples/PrintFunctionNames/CMakeLists.txt
+index 5a00d5036f..f5f818866c 100644
+--- a/clang/examples/PrintFunctionNames/CMakeLists.txt
++++ b/clang/examples/PrintFunctionNames/CMakeLists.txt
+@@ -9,7 +9,7 @@ if( NOT MSVC ) # MSVC mangles symbols differently, and
+   endif()
+ endif()
+ 
+-add_llvm_loadable_module(PrintFunctionNames PrintFunctionNames.cpp)
++add_llvm_loadable_module(PrintFunctionNames PrintFunctionNames.cpp PLUGIN_TOOL clang)
+ 
+ if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN))
+   target_link_libraries(PrintFunctionNames PRIVATE
+diff --git a/clang/include/clang/Frontend/FrontendPluginRegistry.h b/clang/include/clang/Frontend/FrontendPluginRegistry.h
+index ecab630c12..9d7ee08d95 100644
+--- a/clang/include/clang/Frontend/FrontendPluginRegistry.h
++++ b/clang/include/clang/Frontend/FrontendPluginRegistry.h
+@@ -13,9 +13,6 @@
+ #include "clang/Frontend/FrontendAction.h"
+ #include "llvm/Support/Registry.h"
+ 
+-// Instantiated in FrontendAction.cpp.
+-extern template class llvm::Registry<clang::PluginASTAction>;
+-
+ namespace clang {
+ 
+ /// The frontend plugin registry.
+diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h
+index c9b712504e..000df6647f 100644
+--- a/clang/include/clang/Lex/Preprocessor.h
++++ b/clang/include/clang/Lex/Preprocessor.h
+@@ -1972,6 +1972,4 @@ typedef llvm::Registry<PragmaHandler> PragmaHandlerRegistry;
+ 
+ }  // end namespace clang
+ 
+-extern template class llvm::Registry<clang::PragmaHandler>;
+-
+ #endif
+diff --git a/clang/lib/Frontend/FrontendAction.cpp b/clang/lib/Frontend/FrontendAction.cpp
+index d2c2a80394..2945b8925f 100644
+--- a/clang/lib/Frontend/FrontendAction.cpp
++++ b/clang/lib/Frontend/FrontendAction.cpp
+@@ -33,7 +33,7 @@
+ #include <system_error>
+ using namespace clang;
+ 
+-template class llvm::Registry<clang::PluginASTAction>;
++LLVM_INSTANTIATE_REGISTRY(FrontendPluginRegistry)
+ 
+ namespace {
+ 
+diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp
+index 8832c7f80c..f0d6872546 100644
+--- a/clang/lib/Lex/Preprocessor.cpp
++++ b/clang/lib/Lex/Preprocessor.cpp
+@@ -54,7 +54,7 @@
+ #include <utility>
+ using namespace clang;
+ 
+-template class llvm::Registry<clang::PragmaHandler>;
++LLVM_INSTANTIATE_REGISTRY(PragmaHandlerRegistry)
+ 
+ //===----------------------------------------------------------------------===//
+ ExternalPreprocessorSource::~ExternalPreprocessorSource() { }
+diff --git a/clang/lib/Tooling/CompilationDatabase.cpp b/clang/lib/Tooling/CompilationDatabase.cpp
+index 8fc4a1fe5b..6f95bf01f6 100644
+--- a/clang/lib/Tooling/CompilationDatabase.cpp
++++ b/clang/lib/Tooling/CompilationDatabase.cpp
+@@ -32,6 +32,8 @@
+ using namespace clang;
+ using namespace tooling;
+ 
++LLVM_INSTANTIATE_REGISTRY(CompilationDatabasePluginRegistry)
++
+ CompilationDatabase::~CompilationDatabase() {}
+ 
+ std::unique_ptr<CompilationDatabase>
new file mode 100644
--- /dev/null
+++ b/build/build-clang/r285657.patch
@@ -0,0 +1,64 @@
+commit 783f98e4a55266c40b6ecee9b41381353f37013b
+Author: Tim Shen <timshen91@gmail.com>
+Date:   Tue Nov 1 00:19:04 2016 +0000
+
+    [ReachableCode] Skip over ExprWithCleanups in isConfigurationValue
+    
+    Summary: Fixes pr29152.
+    
+    Reviewers: rsmith, pirama, krememek
+    
+    Subscribers: cfe-commits
+    
+    Differential Revision: https://reviews.llvm.org/D24010
+    
+    git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@285657 91177308-0d34-0410-b5e6-96231b3b80d8
+
+diff --git a/clang/include/clang/AST/Stmt.h a/clang/include/clang/AST/Stmt.h
+index 9381a44985..e28675d6a8 100644
+--- a/clang/include/clang/AST/Stmt.h
++++ a/clang/include/clang/AST/Stmt.h
+@@ -387,6 +387,9 @@ public:
+   /// Skip past any implicit AST nodes which might surround this
+   /// statement, such as ExprWithCleanups or ImplicitCastExpr nodes.
+   Stmt *IgnoreImplicit();
++  const Stmt *IgnoreImplicit() const {
++    return const_cast<Stmt *>(this)->IgnoreImplicit();
++  }
+ 
+   /// \brief Skip no-op (attributed, compound) container stmts and skip captured
+   /// stmt at the top, if \a IgnoreCaptured is true.
+diff --git a/clang/lib/Analysis/ReachableCode.cpp a/clang/lib/Analysis/ReachableCode.cpp
+index 8165b09f40..69d000c03b 100644
+--- a/clang/lib/Analysis/ReachableCode.cpp
++++ a/clang/lib/Analysis/ReachableCode.cpp
+@@ -164,6 +164,8 @@ static bool isConfigurationValue(const Stmt *S,
+   if (!S)
+     return false;
+ 
++  S = S->IgnoreImplicit();
++
+   if (const Expr *Ex = dyn_cast<Expr>(S))
+     S = Ex->IgnoreCasts();
+ 
+diff --git a/clang/test/SemaCXX/PR29152.cpp a/clang/test/SemaCXX/PR29152.cpp
+new file mode 100644
+index 0000000000..63c9c9bed5
+--- /dev/null
++++ a/clang/test/SemaCXX/PR29152.cpp
+@@ -0,0 +1,15 @@
++// RUN: %clang_cc1 -fsyntax-only -Wunreachable-code -verify %s
++
++static const bool False = false;
++
++struct A {
++  ~A();
++  operator bool();
++};
++void Bar();
++
++void Foo() {
++  if (False && A()) {
++    Bar(); // expected-no-diagnostics
++  }
++}
--- a/build/gyp.mozbuild
+++ b/build/gyp.mozbuild
@@ -13,16 +13,17 @@ gyp_vars.update({
     'build_with_chromium': 0,
     'use_official_google_api_keys': 0,
     'have_clock_monotonic': 1 if CONFIG['HAVE_CLOCK_MONOTONIC'] else 0,
     'have_ethtool_cmd_speed_hi': 1 if CONFIG['MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI'] else 0,
     'include_alsa_audio': 1 if CONFIG['MOZ_ALSA'] else 0,
     'include_pulse_audio': 1 if CONFIG['MOZ_PULSEAUDIO'] else 0,
     # basic stuff for everything
     'include_internal_video_render': 0,
+    'clang': 1 if CONFIG['CLANG_CXX'] else 0,
     'clang_use_chrome_plugins': 0,
     'enable_protobuf': 0,
     'include_tests': 0,
     'enable_android_opensl': 1,
     'enable_android_opensl_output': 0,
     # use_system_lib* still seems to be in use in trunk/build
     'use_system_libjpeg': 0,
     'use_system_libvpx': 0,
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -281,16 +281,41 @@ OriginAttributes::IsFirstPartyEnabled()
     Preferences::AddBoolVarCache(&sFirstPartyIsolation, "privacy.firstparty.isolate");
   }
 
   return sFirstPartyIsolation;
 }
 
 /* static */
 bool
+OriginAttributes::IsRestrictOpenerAccessForFPI()
+{
+  bool isFirstPartyEnabled = IsFirstPartyEnabled();
+
+  // Cache the privacy.firstparty.isolate.restrict_opener_access pref.
+  static bool sRestrictedOpenerAccess = false;
+  static bool sCachedRestrictedAccessPref = false;
+  if (!sCachedRestrictedAccessPref) {
+    MOZ_ASSERT(NS_IsMainThread());
+    sCachedRestrictedAccessPref = true;
+    Preferences::AddBoolVarCache(&sRestrictedOpenerAccess,
+                                 "privacy.firstparty.isolate.restrict_opener_access");
+  }
+
+  // We always want to restrict window.opener if first party isolation is
+  // disabled.
+  if (!isFirstPartyEnabled) {
+    return true;
+  }
+
+  return isFirstPartyEnabled && sRestrictedOpenerAccess;
+}
+
+/* static */
+bool
 OriginAttributes::IsPrivateBrowsing(const nsACString& aOrigin)
 {
   nsAutoCString dummy;
   OriginAttributes attrs;
   if (NS_WARN_IF(!attrs.PopulateFromOrigin(aOrigin, dummy))) {
     return false;
   }
 
@@ -384,16 +409,34 @@ NS_IMETHODIMP
 BasePrincipal::SubsumesConsideringDomain(nsIPrincipal *aOther, bool *aResult)
 {
   NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
   *aResult = Subsumes(aOther, ConsiderDocumentDomain);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+BasePrincipal::SubsumesConsideringDomainIgnoringFPD(nsIPrincipal *aOther,
+                                                    bool *aResult)
+{
+  NS_ENSURE_TRUE(aOther, NS_ERROR_INVALID_ARG);
+
+  if (Kind() == eCodebasePrincipal &&
+      !dom::ChromeUtils::IsOriginAttributesEqualIgnoringFPD(
+            OriginAttributesRef(), aOther->OriginAttributesRef())) {
+    *aResult = false;
+    return NS_OK;
+  }
+
+  *aResult = SubsumesInternal(aOther, ConsiderDocumentDomain);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 BasePrincipal::CheckMayLoad(nsIURI* aURI, bool aReport, bool aAllowIfInheritsPrincipal)
 {
   // Check the internal method first, which allows us to quickly approve loads
   // for the System Principal.
   if (MayLoadInternal(aURI)) {
     return NS_OK;
   }
 
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -98,16 +98,21 @@ public:
 
   // Helper function to match mIsPrivateBrowsing to existing private browsing
   // flags. Once all other flags are removed, this can be removed too.
   void SyncAttributesWithPrivateBrowsing(bool aInPrivateBrowsing);
 
   // check if "privacy.firstparty.isolate" is enabled.
   static bool IsFirstPartyEnabled();
 
+  // check if the access of window.opener across different FPDs is restricted.
+  // We only restrict the access of window.opener when first party isolation
+  // is enabled and "privacy.firstparty.isolate.restrict_opener_access" is on.
+  static bool IsRestrictOpenerAccessForFPI();
+
   // returns true if the originAttributes suffix has mPrivateBrowsingId value
   // different than 0.
   static bool IsPrivateBrowsing(const nsACString& aOrigin);
 };
 
 class OriginAttributesPattern : public dom::OriginAttributesPatternDictionary
 {
 public:
@@ -205,16 +210,17 @@ public:
   bool Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration);
 
   NS_IMETHOD GetOrigin(nsACString& aOrigin) final;
   NS_IMETHOD GetOriginNoSuffix(nsACString& aOrigin) final;
   NS_IMETHOD Equals(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD EqualsConsideringDomain(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other, bool* _retval) final;
+  NS_IMETHOD SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD CheckMayLoad(nsIURI* uri, bool report, bool allowIfInheritsPrincipal) final;
   NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
   NS_IMETHOD EnsureCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
   NS_IMETHOD GetPreloadCsp(nsIContentSecurityPolicy** aPreloadCSP) override;
   NS_IMETHOD EnsurePreloadCSP(nsIDOMDocument* aDocument, nsIContentSecurityPolicy** aCSP) override;
   NS_IMETHOD GetCspJSON(nsAString& outCSPinJSON) override;
   NS_IMETHOD GetIsNullPrincipal(bool* aResult) override;
   NS_IMETHOD GetIsCodebasePrincipal(bool* aResult) override;
@@ -229,18 +235,16 @@ public:
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
   NS_IMETHOD GetUserContextId(uint32_t* aUserContextId) final;
   NS_IMETHOD GetPrivateBrowsingId(uint32_t* aPrivateBrowsingId) final;
 
   bool EqualsIgnoringAddonId(nsIPrincipal *aOther);
 
   virtual bool AddonHasPermission(const nsAString& aPerm);
 
-  virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
-
   virtual bool IsCodebasePrincipal() const { return false; };
 
   static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
   static already_AddRefed<BasePrincipal>
   CreateCodebasePrincipal(nsIURI* aURI, const OriginAttributes& aAttrs);
   static already_AddRefed<BasePrincipal> CreateCodebasePrincipal(const nsACString& aOrigin);
 
   const OriginAttributes& OriginAttributesRef() override { return mOriginAttributes; }
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -20,17 +20,17 @@ interface nsIURI;
 interface nsIContentSecurityPolicy;
 interface nsIDOMDocument;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
 [ref] native const_OriginAttributes(const mozilla::OriginAttributes);
 
-[scriptable, builtinclass, uuid(3da7b133-f1a0-4de9-a2bc-5c49014c1077)]
+[scriptable, builtinclass, uuid(f75f502d-79fd-48be-a079-e5a7b8f80c8b)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Returns whether the other principal is equivalent to this principal.
      * Principals are considered equal if they are the same principal, or
      * they have the same origin.
      */
     boolean equals(in nsIPrincipal other);
@@ -89,26 +89,37 @@ interface nsIPrincipal : nsISerializable
     boolean subsumes(in nsIPrincipal other);
 
     /**
      * Same as the previous method, subsumes(), but takes document.domain into
      * account.
      */
     boolean subsumesConsideringDomain(in nsIPrincipal other);
 
+    /**
+     * Same as the subsumesConsideringDomain(), but ignores the first party
+     * domain in its originAttributes.
+     */
+    boolean subsumesConsideringDomainIgnoringFPD(in nsIPrincipal other);
+
     %{C++
     inline bool Subsumes(nsIPrincipal* aOther) {
       bool subsumes = false;
       return NS_SUCCEEDED(Subsumes(aOther, &subsumes)) && subsumes;
     }
 
     inline bool SubsumesConsideringDomain(nsIPrincipal* aOther) {
       bool subsumes = false;
       return NS_SUCCEEDED(SubsumesConsideringDomain(aOther, &subsumes)) && subsumes;
     }
+
+    inline bool SubsumesConsideringDomainIgnoringFPD(nsIPrincipal* aOther) {
+      bool subsumes = false;
+      return NS_SUCCEEDED(SubsumesConsideringDomainIgnoringFPD(aOther, &subsumes)) && subsumes;
+    }
     %}
 
     /**
      * Checks whether this principal is allowed to load the network resource
      * located at the given URI under the same-origin policy. This means that
      * codebase principals are only allowed to load resources from the same
      * domain, the system principal is allowed to load anything, and null
      * principals can only load URIs where they are the principal. This is
@@ -334,25 +345,16 @@ interface nsIPrincipal : nsISerializable
      * Returns true iff this is an expanded principal.
      */
     [infallible] readonly attribute boolean isExpandedPrincipal;
 
     /**
      * Returns true iff this is the system principal.
      */
     [infallible] readonly attribute boolean isSystemPrincipal;
-
-    /**
-     * Returns true if this principal's origin is recognized as being on the
-     * whitelist of sites that can use the CSS Unprefixing Service.
-     *
-     * (This interface provides a trivial implementation, just returning false;
-     * subclasses can implement something more complex as-needed.)
-     */
-    [noscript,notxpcom,nostdcall] bool IsOnCSSUnprefixingWhitelist();
 };
 
 /**
  * If nsSystemPrincipal is too risky to use, but we want a principal to access
  * more than one origin, nsExpandedPrincipals letting us define an array of
  * principals it subsumes. So script with an nsExpandedPrincipals will gain
  * same origin access when at least one of its principals it contains gained
  * sameorigin acccess. An nsExpandedPrincipal will be subsumed by the system
--- a/caps/nsPrincipal.cpp
+++ b/caps/nsPrincipal.cpp
@@ -27,17 +27,16 @@
 
 #include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/HashFunctions.h"
 
 using namespace mozilla;
 
-static bool gIsWhitelistingTestDomains = false;
 static bool gCodeBasePrincipalSupport = false;
 
 static bool URIIsImmutable(nsIURI* aURI)
 {
   nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
   bool isMutable;
   return
     mutableObj &&
@@ -53,20 +52,16 @@ NS_IMPL_QUERY_INTERFACE_CI(nsPrincipal,
 NS_IMPL_CI_INTERFACE_GETTER(nsPrincipal,
                             nsIPrincipal,
                             nsISerializable)
 
 // Called at startup:
 /* static */ void
 nsPrincipal::InitializeStatics()
 {
-  Preferences::AddBoolVarCache(
-    &gIsWhitelistingTestDomains,
-    "layout.css.unprefixing-service.include-test-domains");
-
   Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport,
                                "signed.applets.codebase_principal_support",
                                false);
 }
 
 nsPrincipal::nsPrincipal()
   : mCodebaseImmutable(false)
   , mDomainImmutable(false)
@@ -459,206 +454,16 @@ nsPrincipal::Write(nsIObjectOutputStream
   }
 
   // mCodebaseImmutable and mDomainImmutable will be recomputed based
   // on the deserialized URIs in Read().
 
   return NS_OK;
 }
 
-// Helper-function to indicate whether the CSS Unprefixing Service
-// whitelist should include dummy domains that are only intended for
-// use in testing. (Controlled by a pref.)
-static inline bool
-IsWhitelistingTestDomains()
-{
-  return gIsWhitelistingTestDomains;
-}
-
-// Checks if the given URI's host is on our "full domain" whitelist
-// (i.e. if it's an exact match against a domain that needs unprefixing)
-static bool
-IsOnFullDomainWhitelist(nsIURI* aURI)
-{
-  nsAutoCString hostStr;
-  nsresult rv = aURI->GetHost(hostStr);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  // NOTE: This static whitelist is expected to be short. If that changes,
-  // we should consider a different representation; e.g. hash-set, prefix tree.
-  static const nsLiteralCString sFullDomainsOnWhitelist[] = {
-    // 0th entry only active when testing:
-    NS_LITERAL_CSTRING("test1.example.org"),
-    NS_LITERAL_CSTRING("map.baidu.com"),
-    NS_LITERAL_CSTRING("3g.163.com"),
-    NS_LITERAL_CSTRING("3glogo.gtimg.com"), // for 3g.163.com
-    NS_LITERAL_CSTRING("info.3g.qq.com"), // for 3g.qq.com
-    NS_LITERAL_CSTRING("3gimg.qq.com"), // for 3g.qq.com
-    NS_LITERAL_CSTRING("img.m.baidu.com"), // for [shucheng|ks].baidu.com
-    NS_LITERAL_CSTRING("m.mogujie.com"),
-    NS_LITERAL_CSTRING("touch.qunar.com"),
-    NS_LITERAL_CSTRING("mjs.sinaimg.cn"), // for sina.cn
-    NS_LITERAL_CSTRING("static.qiyi.com"), // for m.iqiyi.com
-    NS_LITERAL_CSTRING("cdn.kuaidi100.com"), // for m.kuaidi100.com
-    NS_LITERAL_CSTRING("m.pc6.com"),
-    NS_LITERAL_CSTRING("m.haosou.com"),
-    NS_LITERAL_CSTRING("m.mi.com"),
-    NS_LITERAL_CSTRING("wappass.baidu.com"),
-    NS_LITERAL_CSTRING("m.video.baidu.com"),
-    NS_LITERAL_CSTRING("m.video.baidu.com"),
-    NS_LITERAL_CSTRING("imgcache.gtimg.cn"), // for m.v.qq.com
-    NS_LITERAL_CSTRING("s.tabelog.jp"),
-    NS_LITERAL_CSTRING("s.yimg.jp"), // for s.tabelog.jp
-    NS_LITERAL_CSTRING("i.yimg.jp"), // for *.yahoo.co.jp
-    NS_LITERAL_CSTRING("ai.yimg.jp"), // for *.yahoo.co.jp
-    NS_LITERAL_CSTRING("m.finance.yahoo.co.jp"),
-    NS_LITERAL_CSTRING("daily.c.yimg.jp"), // for sp.daily.co.jp
-    NS_LITERAL_CSTRING("stat100.ameba.jp"), // for ameblo.jp
-    NS_LITERAL_CSTRING("user.ameba.jp"), // for ameblo.jp
-    NS_LITERAL_CSTRING("www.goo.ne.jp"),
-    NS_LITERAL_CSTRING("x.gnst.jp"), // for mobile.gnavi.co.jp
-    NS_LITERAL_CSTRING("c.x.gnst.jp"), // for mobile.gnavi.co.jp
-    NS_LITERAL_CSTRING("www.smbc-card.com"),
-    NS_LITERAL_CSTRING("static.card.jp.rakuten-static.com"), // for rakuten-card.co.jp
-    NS_LITERAL_CSTRING("img.travel.rakuten.co.jp"), // for travel.rakuten.co.jp
-    NS_LITERAL_CSTRING("img.mixi.net"), // for mixi.jp
-    NS_LITERAL_CSTRING("girlschannel.net"),
-    NS_LITERAL_CSTRING("www.fancl.co.jp"),
-    NS_LITERAL_CSTRING("s.cosme.net"),
-    NS_LITERAL_CSTRING("www.sapporobeer.jp"),
-    NS_LITERAL_CSTRING("www.mapion.co.jp"),
-    NS_LITERAL_CSTRING("touch.navitime.co.jp"),
-    NS_LITERAL_CSTRING("sp.mbga.jp"),
-    NS_LITERAL_CSTRING("ava-a.sp.mbga.jp"), // for sp.mbga.jp
-    NS_LITERAL_CSTRING("www.ntv.co.jp"),
-    NS_LITERAL_CSTRING("mobile.suntory.co.jp"), // for suntory.jp
-    NS_LITERAL_CSTRING("www.aeonsquare.net"),
-    NS_LITERAL_CSTRING("mw.nikkei.com"),
-    NS_LITERAL_CSTRING("www.nhk.or.jp"),
-    NS_LITERAL_CSTRING("www.tokyo-sports.co.jp"),
-    NS_LITERAL_CSTRING("www.bellemaison.jp"),
-    NS_LITERAL_CSTRING("www.kuronekoyamato.co.jp"),
-    NS_LITERAL_CSTRING("formassist.jp"), // for orico.jp
-    NS_LITERAL_CSTRING("sp.m.reuters.co.jp"),
-    NS_LITERAL_CSTRING("www.atre.co.jp"),
-    NS_LITERAL_CSTRING("www.jtb.co.jp"),
-    NS_LITERAL_CSTRING("www.sharp.co.jp"),
-    NS_LITERAL_CSTRING("www.biccamera.com"),
-    NS_LITERAL_CSTRING("weathernews.jp"),
-    NS_LITERAL_CSTRING("cache.ymail.jp"), // for www.yamada-denkiweb.com
-  };
-  static const size_t sNumFullDomainsOnWhitelist =
-    MOZ_ARRAY_LENGTH(sFullDomainsOnWhitelist);
-
-  // Skip 0th (dummy) entry in whitelist, unless a pref is enabled.
-  const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1;
-
-  for (size_t i = firstWhitelistIdx; i < sNumFullDomainsOnWhitelist; ++i) {
-    if (hostStr == sFullDomainsOnWhitelist[i]) {
-      return true;
-    }
-  }
-  return false;
-}
-
-// Checks if the given URI's host is on our "base domain" whitelist
-// (i.e. if it's a subdomain of some host that we've whitelisted as needing
-// unprefixing for all its subdomains)
-static bool
-IsOnBaseDomainWhitelist(nsIURI* aURI)
-{
-  static const nsLiteralCString sBaseDomainsOnWhitelist[] = {
-    // 0th entry only active when testing:
-    NS_LITERAL_CSTRING("test2.example.org"),
-    NS_LITERAL_CSTRING("tbcdn.cn"), // for m.taobao.com
-    NS_LITERAL_CSTRING("alicdn.com"), // for m.taobao.com
-    NS_LITERAL_CSTRING("dpfile.com"), // for m.dianping.com
-    NS_LITERAL_CSTRING("hao123img.com"), // for hao123.com
-    NS_LITERAL_CSTRING("tabelog.k-img.com"), // for s.tabelog.com
-    NS_LITERAL_CSTRING("tsite.jp"), // for *.tsite.jp
-  };
-  static const size_t sNumBaseDomainsOnWhitelist =
-    MOZ_ARRAY_LENGTH(sBaseDomainsOnWhitelist);
-
-  nsCOMPtr<nsIEffectiveTLDService> tldService =
-    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
-
-  if (tldService) {
-    // Skip 0th test-entry in whitelist, unless the testing pref is enabled.
-    const size_t firstWhitelistIdx = IsWhitelistingTestDomains() ? 0 : 1;
-
-    // Right now, the test base-domain "test2.example.org" is the only entry in
-    // its whitelist with a nonzero "depth". So we'll only bother going beyond
-    // 0 depth (to 1) if that entry is enabled. (No point in slowing down the
-    // normal codepath, for the benefit of a disabled test domain.)  If we add a
-    // "real" base-domain with a depth of >= 1 to our whitelist, we can get rid
-    // of this conditional & just make this a static variable.
-    const uint32_t maxSubdomainDepth = IsWhitelistingTestDomains() ? 1 : 0;
-
-    for (uint32_t subdomainDepth = 0;
-         subdomainDepth <= maxSubdomainDepth; ++subdomainDepth) {
-
-      // Get the base domain (to depth |subdomainDepth|) from passed-in URI:
-      nsAutoCString baseDomainStr;
-      nsresult rv = tldService->GetBaseDomain(aURI, subdomainDepth,
-                                              baseDomainStr);
-      if (NS_FAILED(rv)) {
-        // aURI doesn't have |subdomainDepth| levels of subdomains. If we got
-        // here without a match yet, then aURI is not on our whitelist.
-        return false;
-      }
-
-      // Compare the base domain against each entry in our whitelist:
-      for (size_t i = firstWhitelistIdx; i < sNumBaseDomainsOnWhitelist; ++i) {
-        if (baseDomainStr == sBaseDomainsOnWhitelist[i]) {
-          return true;
-        }
-      }
-    }
-  }
-
-  return false;
-}
-
-// The actual (non-cached) implementation of IsOnCSSUnprefixingWhitelist():
-static bool
-IsOnCSSUnprefixingWhitelistImpl(nsIURI* aURI)
-{
-  // Check scheme, so we can drop any non-HTTP/HTTPS URIs right away
-  nsAutoCString schemeStr;
-  nsresult rv = aURI->GetScheme(schemeStr);
-  NS_ENSURE_SUCCESS(rv, false);
-
-  // Only proceed if scheme is "http" or "https"
-  if (!(StringBeginsWith(schemeStr, NS_LITERAL_CSTRING("http")) &&
-        (schemeStr.Length() == 4 ||
-         (schemeStr.Length() == 5 && schemeStr[4] == 's')))) {
-    return false;
-  }
-
-  return (IsOnFullDomainWhitelist(aURI) ||
-          IsOnBaseDomainWhitelist(aURI));
-}
-
-
-bool
-nsPrincipal::IsOnCSSUnprefixingWhitelist()
-{
-  if (mIsOnCSSUnprefixingWhitelist.isNothing()) {
-    // Value not cached -- perform our lazy whitelist-check.
-    // (NOTE: If our URI is mutable, we just assume it's not on the whitelist,
-    // since our caching strategy won't work. This isn't expected to be common.)
-    mIsOnCSSUnprefixingWhitelist.emplace(
-      mCodebaseImmutable &&
-      IsOnCSSUnprefixingWhitelistImpl(mCodebase));
-  }
-
-  return *mIsOnCSSUnprefixingWhitelist;
-}
-
 /************************************************************************************************************************/
 
 NS_IMPL_CLASSINFO(nsExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
                   NS_EXPANDEDPRINCIPAL_CID)
 NS_IMPL_QUERY_INTERFACE_CI(nsExpandedPrincipal,
                            nsIPrincipal,
                            nsIExpandedPrincipal)
 NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal,
@@ -813,25 +618,16 @@ nsExpandedPrincipal::AddonHasPermission(
   for (size_t i = 0; i < mPrincipals.Length(); ++i) {
     if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
       return true;
     }
   }
   return false;
 }
 
-bool
-nsExpandedPrincipal::IsOnCSSUnprefixingWhitelist()
-{
-  // CSS Unprefixing Whitelist is a per-origin thing; doesn't really make sense
-  // for an expanded principal. (And probably shouldn't be needed.)
-  return false;
-}
-
-
 nsresult
 nsExpandedPrincipal::GetScriptLocation(nsACString& aStr)
 {
   aStr.Assign("[Expanded Principal [");
   for (size_t i = 0; i < mPrincipals.Length(); ++i) {
     if (i != 0) {
       aStr.AppendLiteral(", ");
     }
--- a/caps/nsPrincipal.h
+++ b/caps/nsPrincipal.h
@@ -20,17 +20,16 @@ class nsPrincipal final : public mozilla
 public:
   NS_DECL_NSISERIALIZABLE
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
-  virtual bool IsOnCSSUnprefixingWhitelist() override;
   bool IsCodebasePrincipal() const override { return true; }
   nsresult GetOriginInternal(nsACString& aOrigin) override;
 
   nsPrincipal();
 
   // Init() must be called before the principal is in a usable state.
   nsresult Init(nsIURI* aCodebase,
                 const mozilla::OriginAttributes& aOriginAttributes);
@@ -46,17 +45,16 @@ public:
   PrincipalKind Kind() override { return eCodebasePrincipal; }
 
   nsCOMPtr<nsIURI> mDomain;
   nsCOMPtr<nsIURI> mCodebase;
   // If mCodebaseImmutable is true, mCodebase is non-null and immutable
   bool mCodebaseImmutable;
   bool mDomainImmutable;
   bool mInitialized;
-  mozilla::Maybe<bool> mIsOnCSSUnprefixingWhitelist; // Lazily-computed
 
 protected:
   virtual ~nsPrincipal();
 
   bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration) override;
   bool MayLoadInternal(nsIURI* aURI) override;
 };
 
@@ -72,17 +70,16 @@ public:
   NS_IMETHOD_(MozExternalRefCountType) Release() override { return nsJSPrincipals::Release(); };
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
   NS_IMETHOD GetHashValue(uint32_t* aHashValue) override;
   NS_IMETHOD GetURI(nsIURI** aURI) override;
   NS_IMETHOD GetDomain(nsIURI** aDomain) override;
   NS_IMETHOD SetDomain(nsIURI* aDomain) override;
   NS_IMETHOD GetBaseDomain(nsACString& aBaseDomain) override;
   virtual bool AddonHasPermission(const nsAString& aPerm) override;
-  virtual bool IsOnCSSUnprefixingWhitelist() override;
   virtual nsresult GetScriptLocation(nsACString &aStr) override;
   nsresult GetOriginInternal(nsACString& aOrigin) override;
 
   PrincipalKind Kind() override { return eExpandedPrincipal; }
 
 protected:
   virtual ~nsExpandedPrincipal();
 
--- a/devtools/client/inspector/rules/test/browser.ini
+++ b/devtools/client/inspector/rules/test/browser.ini
@@ -207,16 +207,17 @@ skip-if = (os == 'linux' && bits == 32 &
 [browser_rules_select-and-copy-styles.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_rules_selector-highlighter-on-navigate.js]
 [browser_rules_selector-highlighter_01.js]
 [browser_rules_selector-highlighter_02.js]
 [browser_rules_selector-highlighter_03.js]
 [browser_rules_selector-highlighter_04.js]
+[browser_rules_selector-highlighter_05.js]
 [browser_rules_selector_highlight.js]
 [browser_rules_strict-search-filter-computed-list_01.js]
 [browser_rules_strict-search-filter_01.js]
 [browser_rules_strict-search-filter_02.js]
 [browser_rules_strict-search-filter_03.js]
 [browser_rules_style-editor-link.js]
 [browser_rules_urls-clickable.js]
 [browser_rules_user-agent-styles.js]
--- a/devtools/client/inspector/rules/test/browser_rules_content_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_content_02.js
@@ -17,16 +17,18 @@ const CONTENT = `
   </body>
 `;
 
 add_task(function* () {
   let tab = yield addTab("data:text/html;charset=utf-8," + CONTENT);
 
   let testActor = yield getTestActorWithoutToolbox(tab);
   let inspector = yield clickOnInspectMenuItem(testActor, "span");
+  yield getRuleViewSelectorHighlighterIcon(inspector.ruleview.view,
+                                           "element", 3);
 
   checkRuleViewContent(inspector.ruleview.view);
 });
 
 function checkRuleViewContent({styleDocument}) {
   info("Making sure the rule-view contains the expected content");
 
   let headers = [...styleDocument.querySelectorAll(".ruleview-header")];
@@ -52,9 +54,8 @@ function checkRuleViewContent({styleDocu
 
     let propertyNames = [...rule.querySelectorAll(".ruleview-propertyname")];
     is(propertyNames.length, 1, "There's only one property name, as expected");
 
     let propertyValues = [...rule.querySelectorAll(".ruleview-propertyvalue")];
     is(propertyValues.length, 1, "There's only one property value, as expected");
   }
 }
-
--- a/devtools/client/inspector/rules/test/browser_rules_edit-selector_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_edit-selector_04.js
@@ -28,17 +28,17 @@ add_task(function* () {
   yield testEditSelector(view, "body");
   yield testSelectorHighlight(view, "body");
 });
 
 function* testSelectorHighlight(view, name) {
   info("Test creating selector highlighter");
 
   info("Clicking on a selector icon");
-  let icon = getRuleViewSelectorHighlighterIcon(view, name);
+  let icon = yield getRuleViewSelectorHighlighterIcon(view, name);
 
   let onToggled = view.once("ruleview-selectorhighlighter-toggled");
   EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
   let isVisible = yield onToggled;
 
   ok(view.selectorHighlighter, "The selectorhighlighter instance was created");
   ok(isVisible, "The toggle event says the highlighter is visible");
 }
--- a/devtools/client/inspector/rules/test/browser_rules_inherited-properties_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_inherited-properties_03.js
@@ -13,16 +13,17 @@ const TEST_URI = `
     <div id="test1">Styled Node</div>
   </div>
 `;
 
 add_task(function* () {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("#test1", inspector);
+  yield getRuleViewSelectorHighlighterIcon(view, "element", 1);
   yield elementStyleInherit(inspector, view);
 });
 
 function* elementStyleInherit(inspector, view) {
   let elementStyle = view._elementStyle;
   is(elementStyle.rules.length, 2, "Should have 2 rules.");
 
   let elementRule = elementStyle.rules[0];
--- a/devtools/client/inspector/rules/test/browser_rules_inherited-properties_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_inherited-properties_04.js
@@ -15,16 +15,17 @@ const TEST_URI = `
     </div>
   </div>
 `;
 
 add_task(function* () {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   yield selectNode("a", inspector);
+  yield getRuleViewSelectorHighlighterIcon(view, "element", 2);
   yield elementStyleInherit(inspector, view);
 });
 
 function* elementStyleInherit(inspector, view) {
   let gutters = view.element.querySelectorAll(".theme-gutter");
   is(gutters.length, 2,
     "Gutters should contains 2 sections");
   ok(gutters[0].textContent, "Inherited from div");
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter-on-navigate.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter-on-navigate.js
@@ -18,17 +18,17 @@ const TEST_URI = `
 const TEST_URI_2 = "data:text/html,<html><body>test</body></html>";
 
 add_task(function* () {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {inspector, view} = yield openRuleView();
   let highlighters = view.highlighters;
 
   info("Clicking on a selector icon");
-  let icon = getRuleViewSelectorHighlighterIcon(view, "body, p, td");
+  let icon = yield getRuleViewSelectorHighlighterIcon(view, "body, p, td");
 
   let onToggled = view.once("ruleview-selectorhighlighter-toggled");
   EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
   let isVisible = yield onToggled;
 
   ok(highlighters.selectorHighlighterShown, "The selectorHighlighterShown is set.");
   ok(view.selectorHighlighter, "The selectorhighlighter instance was created");
   ok(isVisible, "The toggle event says the highlighter is visible");
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_01.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_01.js
@@ -19,17 +19,17 @@ const TEST_URI = `
 add_task(function* () {
   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
   let {view} = yield openRuleView();
 
   ok(!view.selectorHighlighter,
     "No selectorhighlighter exist in the rule-view");
 
   info("Clicking on a selector icon");
-  let icon = getRuleViewSelectorHighlighterIcon(view, "body, p, td");
+  let icon = yield getRuleViewSelectorHighlighterIcon(view, "body, p, td");
 
   let onToggled = view.once("ruleview-selectorhighlighter-toggled");
   EventUtils.synthesizeMouseAtCenter(icon, {}, view.styleWindow);
   let isVisible = yield onToggled;
 
   ok(view.selectorHighlighter, "The selectorhighlighter instance was created");
   ok(isVisible, "The toggle event says the highlighter is visible");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_02.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_02.js
@@ -41,38 +41,38 @@ add_task(function* () {
       this.options = null;
       this.isShown = false;
     }
   };
 
   // Inject the mock highlighter in the rule-view
   view.selectorHighlighter = HighlighterFront;
 
-  let icon = getRuleViewSelectorHighlighterIcon(view, "body");
+  let icon = yield getRuleViewSelectorHighlighterIcon(view, "body");
 
   info("Checking that the HighlighterFront's show/hide methods are called");
 
   info("Clicking once on the body selector highlighter icon");
   yield clickSelectorIcon(icon, view);
   ok(HighlighterFront.isShown, "The highlighter is shown");
 
   info("Clicking once again on the body selector highlighter icon");
   yield clickSelectorIcon(icon, view);
   ok(!HighlighterFront.isShown, "The highlighter is hidden");
 
   info("Checking that the right NodeFront reference and options are passed");
   yield selectNode("p", inspector);
-  icon = getRuleViewSelectorHighlighterIcon(view, "p");
+  icon = yield getRuleViewSelectorHighlighterIcon(view, "p");
 
   yield clickSelectorIcon(icon, view);
   is(HighlighterFront.nodeFront.tagName, "P",
     "The right NodeFront is passed to the highlighter (1)");
   is(HighlighterFront.options.selector, "p",
     "The right selector option is passed to the highlighter (1)");
 
   yield selectNode("body", inspector);
-  icon = getRuleViewSelectorHighlighterIcon(view, "body");
+  icon = yield getRuleViewSelectorHighlighterIcon(view, "body");
   yield clickSelectorIcon(icon, view);
   is(HighlighterFront.nodeFront.tagName, "BODY",
     "The right NodeFront is passed to the highlighter (2)");
   is(HighlighterFront.options.selector, "body",
     "The right selector option is passed to the highlighter (2)");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_03.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_03.js
@@ -34,45 +34,45 @@ add_task(function* () {
     }
   };
 
   // Inject the mock highlighter in the rule-view
   view.selectorHighlighter = HighlighterFront;
 
   info("Select .node-1 and click on the .node-1 selector icon");
   yield selectNode(".node-1", inspector);
-  let icon = getRuleViewSelectorHighlighterIcon(view, ".node-1");
+  let icon = yield getRuleViewSelectorHighlighterIcon(view, ".node-1");
   yield clickSelectorIcon(icon, view);
   ok(HighlighterFront.isShown, "The highlighter is shown");
 
   info("With .node-1 still selected, click again on the .node-1 selector icon");
   yield clickSelectorIcon(icon, view);
   ok(!HighlighterFront.isShown, "The highlighter is now hidden");
 
   info("With .node-1 still selected, click on the div selector icon");
-  icon = getRuleViewSelectorHighlighterIcon(view, "div");
+  icon = yield getRuleViewSelectorHighlighterIcon(view, "div");
   yield clickSelectorIcon(icon, view);
   ok(HighlighterFront.isShown, "The highlighter is shown again");
 
   info("With .node-1 still selected, click again on the .node-1 selector icon");
-  icon = getRuleViewSelectorHighlighterIcon(view, ".node-1");
+  icon = yield getRuleViewSelectorHighlighterIcon(view, ".node-1");
   yield clickSelectorIcon(icon, view);
   ok(HighlighterFront.isShown,
     "The highlighter is shown again since the clicked selector was different");
 
   info("Selecting .node-2");
   yield selectNode(".node-2", inspector);
   ok(HighlighterFront.isShown,
     "The highlighter is still shown after selection");
 
   info("With .node-2 selected, click on the div selector icon");
-  icon = getRuleViewSelectorHighlighterIcon(view, "div");
+  icon = yield getRuleViewSelectorHighlighterIcon(view, "div");
   yield clickSelectorIcon(icon, view);
   ok(HighlighterFront.isShown,
     "The highlighter is shown still since the selected was different");
 
   info("Switching back to .node-1 and clicking on the div selector");
   yield selectNode(".node-1", inspector);
-  icon = getRuleViewSelectorHighlighterIcon(view, "div");
+  icon = yield getRuleViewSelectorHighlighterIcon(view, "div");
   yield clickSelectorIcon(icon, view);
   ok(!HighlighterFront.isShown,
     "The highlighter is hidden now that the same selector was clicked");
 });
--- a/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_04.js
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_04.js
@@ -34,17 +34,17 @@ add_task(function* () {
       this.isShown = false;
     }
   };
   // Inject the mock highlighter in the rule-view
   view.selectorHighlighter = HighlighterFront;
 
   info("Checking that the right NodeFront reference and options are passed");
   yield selectNode("p", inspector);
-  let icon = getRuleViewSelectorHighlighterIcon(view, "element");
+  let icon = yield getRuleViewSelectorHighlighterIcon(view, "element");
 
   yield clickSelectorIcon(icon, view);
   is(HighlighterFront.nodeFront.tagName, "P",
      "The right NodeFront is passed to the highlighter (1)");
   is(HighlighterFront.options.selector, "body > p:nth-child(1)",
      "The right selector option is passed to the highlighter (1)");
   ok(HighlighterFront.isShown, "The toggle event says the highlighter is visible");
 
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/rules/test/browser_rules_selector-highlighter_05.js
@@ -0,0 +1,64 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the selector highlighter is correctly shown when clicking on a
+// inherited element
+
+// Note that in this test, we mock the highlighter front, merely testing the
+// behavior of the style-inspector UI for now
+
+const TEST_URI = `
+<div style="cursor:pointer">
+  A
+  <div style="cursor:pointer">
+    B<a>Cursor</a>
+  </div>
+</div>
+`;
+
+add_task(function* () {
+  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+  let {inspector, view} = yield openRuleView();
+
+  // Mock the highlighter front to get the reference of the NodeFront
+  let HighlighterFront = {
+    isShown: false,
+    nodeFront: null,
+    options: null,
+    show: function (nodeFront, options) {
+      this.nodeFront = nodeFront;
+      this.options = options;
+      this.isShown = true;
+    },
+    hide: function () {
+      this.nodeFront = null;
+      this.options = null;
+      this.isShown = false;
+    }
+  };
+  // Inject the mock highlighter in the rule-view
+  view.selectorHighlighter = HighlighterFront;
+
+  info("Checking that the right NodeFront reference and options are passed");
+  yield selectNode("a", inspector);
+
+  let icon = yield getRuleViewSelectorHighlighterIcon(view, "element");
+  yield clickSelectorIcon(icon, view);
+  is(HighlighterFront.options.selector,
+     "body > div:nth-child(1) > div:nth-child(1) > a:nth-child(1)",
+     "The right selector option is passed to the highlighter (1)");
+
+  icon = yield getRuleViewSelectorHighlighterIcon(view, "element", 1);
+  yield clickSelectorIcon(icon, view);
+  is(HighlighterFront.options.selector,
+     "body > div:nth-child(1) > div:nth-child(1)",
+     "The right selector option is passed to the highlighter (1)");
+
+  icon = yield getRuleViewSelectorHighlighterIcon(view, "element", 2);
+  yield clickSelectorIcon(icon, view);
+  is(HighlighterFront.options.selector, "body > div:nth-child(1)",
+     "The right selector option is passed to the highlighter (1)");
+});
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -40,144 +40,31 @@ addTab = function (url) {
     info("Loading the helper frame script " + FRAME_SCRIPT_URL);
     let browser = tab.linkedBrowser;
     browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
     return tab;
   });
 };
 
 /**
- * Wait for a content -> chrome message on the message manager (the window
- * messagemanager is used).
- *
- * @param {String} name
- *        The message name
- * @return {Promise} A promise that resolves to the response data when the
- * message has been received
- */
-function waitForContentMessage(name) {
-  info("Expecting message " + name + " from content");
-
-  let mm = gBrowser.selectedBrowser.messageManager;
-
-  let def = defer();
-  mm.addMessageListener(name, function onMessage(msg) {
-    mm.removeMessageListener(name, onMessage);
-    def.resolve(msg.data);
-  });
-  return def.promise;
-}
-
-/**
- * Send an async message to the frame script (chrome -> content) and wait for a
- * response message with the same name (content -> chrome).
- *
- * @param {String} name
- *        The message name. Should be one of the messages defined
- *        in doc_frame_script.js
- * @param {Object} data
- *        Optional data to send along
- * @param {Object} objects
- *        Optional CPOW objects to send along
- * @param {Boolean} expectResponse
- *        If set to false, don't wait for a response with the same name
- *        from the content script. Defaults to true.
- * @return {Promise} Resolves to the response data if a response is expected,
- * immediately resolves otherwise
- */
-function executeInContent(name, data = {}, objects = {},
-                          expectResponse = true) {
-  info("Sending message " + name + " to content");
-  let mm = gBrowser.selectedBrowser.messageManager;
-
-  mm.sendAsyncMessage(name, data, objects);
-  if (expectResponse) {
-    return waitForContentMessage(name);
-  }
-
-  return promise.resolve();
-}
-
-/**
- * Send an async message to the frame script and get back the requested
- * computed style property.
- *
- * @param {String} selector
- *        The selector used to obtain the element.
- * @param {String} pseudo
- *        pseudo id to query, or null.
- * @param {String} name
- *        name of the property.
- */
-function* getComputedStyleProperty(selector, pseudo, propName) {
-  return yield executeInContent("Test:GetComputedStylePropertyValue",
-    {selector,
-     pseudo,
-     name: propName});
-}
-
-/**
  * Get an element's inline style property value.
  * @param {TestActor} testActor
  * @param {String} selector
  *        The selector used to obtain the element.
  * @param {String} name
  *        name of the property.
  */
 function getStyle(testActor, selector, propName) {
   return testActor.eval(`
     content.document.querySelector("${selector}")
                     .style.getPropertyValue("${propName}");
   `);
 }
 
 /**
- * Send an async message to the frame script and wait until the requested
- * computed style property has the expected value.
- *
- * @param {String} selector
- *        The selector used to obtain the element.
- * @param {String} pseudo
- *        pseudo id to query, or null.
- * @param {String} prop
- *        name of the property.
- * @param {String} expected
- *        expected value of property
- * @param {String} name
- *        the name used in test message
- */
-function* waitForComputedStyleProperty(selector, pseudo, name, expected) {
-  return yield executeInContent("Test:WaitForComputedStylePropertyValue",
-    {selector,
-     pseudo,
-     expected,
-     name});
-}
-
-/**
- * Given an inplace editable element, click to switch it to edit mode, wait for
- * focus
- *
- * @return a promise that resolves to the inplace-editor element when ready
- */
-var focusEditableField = Task.async(function* (ruleView, editable, xOffset = 1,
-    yOffset = 1, options = {}) {
-  let onFocus = once(editable.parentNode, "focus", true);
-  info("Clicking on editable field to turn to edit mode");
-  EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
-    editable.ownerDocument.defaultView);
-  yield onFocus;
-
-  info("Editable field gained focus, returning the input field now");
-  let onEdit = inplaceEditor(editable.ownerDocument.activeElement);
-
-  return onEdit;
-});
-
-/**
  * When a tooltip is closed, this ends up "commiting" the value changed within
  * the tooltip (e.g. the color in case of a colorpicker) which, in turn, ends up
  * setting the value of the corresponding css property in the rule-view.
  * Use this function to close the tooltip and make sure the test waits for the
  * ruleview-changed event.
  * @param {SwatchBasedEditorTooltip} editorTooltip
  * @param {CSSRuleView} view
  */
@@ -215,119 +102,16 @@ var waitForSuccess = Task.async(function
       ok(false, "Failure: " + desc);
       break;
     }
     yield new Promise(r => setTimeout(r, 200));
   }
 });
 
 /**
- * Get the DOMNode for a css rule in the rule-view that corresponds to the given
- * selector
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view for which the rule
- *        object is wanted
- * @return {DOMNode}
- */
-function getRuleViewRule(view, selectorText) {
-  let rule;
-  for (let r of view.styleDocument.querySelectorAll(".ruleview-rule")) {
-    let selector = r.querySelector(".ruleview-selectorcontainer, " +
-                                   ".ruleview-selector-matched");
-    if (selector && selector.textContent === selectorText) {
-      rule = r;
-      break;
-    }
-  }
-
-  return rule;
-}
-
-/**
- * Get references to the name and value span nodes corresponding to a given
- * selector and property name in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for the property in
- * @param {String} propertyName
- *        The name of the property
- * @return {Object} An object like {nameSpan: DOMNode, valueSpan: DOMNode}
- */
-function getRuleViewProperty(view, selectorText, propertyName) {
-  let prop;
-
-  let rule = getRuleViewRule(view, selectorText);
-  if (rule) {
-    // Look for the propertyName in that rule element
-    for (let p of rule.querySelectorAll(".ruleview-property")) {
-      let nameSpan = p.querySelector(".ruleview-propertyname");
-      let valueSpan = p.querySelector(".ruleview-propertyvalue");
-
-      if (nameSpan.textContent === propertyName) {
-        prop = {nameSpan: nameSpan, valueSpan: valueSpan};
-        break;
-      }
-    }
-  }
-  return prop;
-}
-
-/**
- * Get the text value of the property corresponding to a given selector and name
- * in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for the property in
- * @param {String} propertyName
- *        The name of the property
- * @return {String} The property value
- */
-function getRuleViewPropertyValue(view, selectorText, propertyName) {
-  return getRuleViewProperty(view, selectorText, propertyName)
-    .valueSpan.textContent;
-}
-
-/**
- * Get a reference to the selector DOM element corresponding to a given selector
- * in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for
- * @return {DOMNode} The selector DOM element
- */
-function getRuleViewSelector(view, selectorText) {
-  let rule = getRuleViewRule(view, selectorText);
-  return rule.querySelector(".ruleview-selector, .ruleview-selector-matched");
-}
-
-/**
- * Get a reference to the selectorhighlighter icon DOM element corresponding to
- * a given selector in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for
- * @return {DOMNode} The selectorhighlighter icon DOM element
- */
-function getRuleViewSelectorHighlighterIcon(view, selectorText) {
-  let rule = getRuleViewRule(view, selectorText);
-  return rule.querySelector(".ruleview-selectorhighlighter");
-}
-
-/**
  * Simulate a color change in a given color picker tooltip, and optionally wait
  * for a given element in the page to have its style changed as a result.
  * Note that this function assumes that the colorpicker popup is already open
  * and it won't close it after having selected the new color.
  *
  * @param {RuleView} ruleView
  *        The related rule view instance
  * @param {SwatchColorPickerTooltip} colorPicker
@@ -448,44 +232,16 @@ var openCubicBezierAndChangeCoords = Tas
     let {selector, name, value} = expectedChange;
     yield waitForComputedStyleProperty(selector, null, name, value);
   }
 
   return {propEditor, swatch, bezierTooltip};
 });
 
 /**
- * Get a rule-link from the rule-view given its index
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {Number} index
- *        The index of the link to get
- * @return {DOMNode} The link if any at this index
- */
-function getRuleViewLinkByIndex(view, index) {
-  let links = view.styleDocument.querySelectorAll(".ruleview-rule-source");
-  return links[index];
-}
-
-/**
- * Get rule-link text from the rule-view given its index
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {Number} index
- *        The index of the link to get
- * @return {String} The string at this index
- */
-function getRuleViewLinkTextByIndex(view, index) {
-  let link = getRuleViewLinkByIndex(view, index);
-  return link.querySelector(".ruleview-rule-source-label").textContent;
-}
-
-/**
  * Simulate adding a new property in an existing rule in the rule-view.
  *
  * @param {CssRuleView} view
  *        The instance of the rule-view panel
  * @param {Number} ruleIndex
  *        The index of the rule to use. Note that if ruleIndex is 0, you might
  *        want to also listen to markupmutation events in your test since
  *        that's going to change the style attribute of the selected node.
@@ -621,84 +377,16 @@ var removeProperty = Task.async(function
  */
 var togglePropStatus = Task.async(function* (view, textProp) {
   let onRuleViewRefreshed = view.once("ruleview-changed");
   textProp.editor.enable.click();
   yield onRuleViewRefreshed;
 });
 
 /**
- * Click on a rule-view's close brace to focus a new property name editor
- *
- * @param {RuleEditor} ruleEditor
- *        An instance of RuleEditor that will receive the new property
- * @return a promise that resolves to the newly created editor when ready and
- * focused
- */
-var focusNewRuleViewProperty = Task.async(function* (ruleEditor) {
-  info("Clicking on a close ruleEditor brace to start editing a new property");
-
-  // Use bottom alignment to avoid scrolling out of the parent element area.
-  ruleEditor.closeBrace.scrollIntoView(false);
-  let editor = yield focusEditableField(ruleEditor.ruleView,
-    ruleEditor.closeBrace);
-
-  is(inplaceEditor(ruleEditor.newPropSpan), editor,
-    "Focused editor is the new property editor.");
-
-  return editor;
-});
-
-/**
- * Create a new property name in the rule-view, focusing a new property editor
- * by clicking on the close brace, and then entering the given text.
- * Keep in mind that the rule-view knows how to handle strings with multiple
- * properties, so the input text may be like: "p1:v1;p2:v2;p3:v3".
- *
- * @param {RuleEditor} ruleEditor
- *        The instance of RuleEditor that will receive the new property(ies)
- * @param {String} inputValue
- *        The text to be entered in the new property name field
- * @return a promise that resolves when the new property name has been entered
- * and once the value field is focused
- */
-var createNewRuleViewProperty = Task.async(function* (ruleEditor, inputValue) {
-  info("Creating a new property editor");
-  let editor = yield focusNewRuleViewProperty(ruleEditor);
-
-  info("Entering the value " + inputValue);
-  editor.input.value = inputValue;
-
-  info("Submitting the new value and waiting for value field focus");
-  let onFocus = once(ruleEditor.element, "focus", true);
-  EventUtils.synthesizeKey("VK_RETURN", {},
-    ruleEditor.element.ownerDocument.defaultView);
-  yield onFocus;
-});
-
-/**
- * Set the search value for the rule-view filter styles search box.
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} searchValue
- *        The filter search value
- * @return a promise that resolves when the rule-view is filtered for the
- * search term
- */
-var setSearchFilter = Task.async(function* (view, searchValue) {
-  info("Setting filter text to \"" + searchValue + "\"");
-  let win = view.styleWindow;
-  let searchField = view.searchField;
-  searchField.focus();
-  synthesizeKeys(searchValue, win);
-  yield view.inspector.once("ruleview-filtered");
-});
-
-/**
  * Reload the current page and wait for the inspector to be initialized after
  * the navigation
  *
  * @param {InspectorPanel} inspector
  *        The instance of InspectorPanel currently loaded in the toolbox
  * @param {TestActor} testActor
  *        The current instance of the TestActor
  */
@@ -774,36 +462,16 @@ function* sendKeysAndWaitForFocus(view, 
   let onFocus = once(element, "focus", true);
   for (let key of keys) {
     EventUtils.sendKey(key, view.styleWindow);
   }
   yield onFocus;
 }
 
 /**
- * Open the style editor context menu and return all of it's items in a flat array
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @return An array of MenuItems
- */
-function openStyleContextMenuAndGetAllItems(view, target) {
-  let menu = view._contextmenu._openMenu({target: target});
-
-  // Flatten all menu items into a single array to make searching through it easier
-  let allItems = [].concat.apply([], menu.items.map(function addItem(item) {
-    if (item.submenu) {
-      return addItem(item.submenu.items);
-    }
-    return item;
-  }));
-
-  return allItems;
-}
-
-/**
  * Wait for a markupmutation event on the inspector that is for a style modification.
  * @param {InspectorPanel} inspector
  * @return {Promise}
  */
 function waitForStyleModification(inspector) {
   return new Promise(function (resolve) {
     function checkForStyleModification(name, mutations) {
       for (let mutation of mutations) {
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -144,29 +144,44 @@ RuleEditor.prototype = {
         element: this.selectorText,
         done: this._onSelectorDone,
         cssProperties: this.rule.cssProperties,
         contextMenu: this.ruleView.inspector.onTextBoxContextMenu
       });
     }
 
     if (this.rule.domRule.type !== CSSRule.KEYFRAME_RULE) {
-      let selector = this.rule.domRule.selectors
-               ? this.rule.domRule.selectors.join(", ")
-               : this.ruleView.inspector.selectionCssSelector;
+      Task.spawn(function* () {
+        let selector;
 
-      let selectorHighlighter = createChild(header, "span", {
-        class: "ruleview-selectorhighlighter" +
-               (this.ruleView.highlighters.selectorHighlighterShown === selector ?
-                " highlighted" : ""),
-        title: l10n("rule.selectorHighlighter.tooltip")
-      });
-      selectorHighlighter.addEventListener("click", () => {
-        this.ruleView.toggleSelectorHighlighter(selectorHighlighter, selector);
-      });
+        if (this.rule.domRule.selectors) {
+          // This is a "normal" rule with a selector.
+          selector = this.rule.domRule.selectors.join(", ");
+        } else if (this.rule.inherited) {
+          // This is an inline style from an inherited rule. Need to resolve the unique
+          // selector from the node which rule this is inherited from.
+          selector = yield this.rule.inherited.getUniqueSelector();
+        } else {
+          // This is an inline style from the current node.
+          selector = this.ruleView.inspector.selectionCssSelector;
+        }
+
+        let selectorHighlighter = createChild(header, "span", {
+          class: "ruleview-selectorhighlighter" +
+                 (this.ruleView.highlighters.selectorHighlighterShown === selector ?
+                  " highlighted" : ""),
+          title: l10n("rule.selectorHighlighter.tooltip")
+        });
+        selectorHighlighter.addEventListener("click", () => {
+          this.ruleView.toggleSelectorHighlighter(selectorHighlighter, selector);
+        });
+
+        this.uniqueSelector = selector;
+        this.emit("selector-icon-created");
+      }.bind(this));
     }
 
     this.openBrace = createChild(header, "span", {
       class: "ruleview-ruleopen",
       textContent: " {"
     });
 
     this.propertyList = createChild(code, "ul", {
--- a/devtools/client/inspector/shared/test/head.js
+++ b/devtools/client/inspector/shared/test/head.js
@@ -84,133 +84,16 @@ addTab = function (url) {
     info("Loading the helper frame script " + FRAME_SCRIPT_URL);
     let browser = tab.linkedBrowser;
     browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
     return tab;
   });
 };
 
 /**
- * Wait for a content -> chrome message on the message manager (the window
- * messagemanager is used).
- *
- * @param {String} name
- *        The message name
- * @return {Promise} A promise that resolves to the response data when the
- * message has been received
- */
-function waitForContentMessage(name) {
-  info("Expecting message " + name + " from content");
-
-  let mm = gBrowser.selectedBrowser.messageManager;
-
-  let def = defer();
-  mm.addMessageListener(name, function onMessage(msg) {
-    mm.removeMessageListener(name, onMessage);
-    def.resolve(msg.data);
-  });
-  return def.promise;
-}
-
-/**
- * Send an async message to the frame script (chrome -> content) and wait for a
- * response message with the same name (content -> chrome).
- *
- * @param {String} name
- *        The message name. Should be one of the messages defined
- *        in doc_frame_script.js
- * @param {Object} data
- *        Optional data to send along
- * @param {Object} objects
- *        Optional CPOW objects to send along
- * @param {Boolean} expectResponse
- *        If set to false, don't wait for a response with the same name
- *        from the content script. Defaults to true.
- * @return {Promise} Resolves to the response data if a response is expected,
- * immediately resolves otherwise
- */
-function executeInContent(name, data = {}, objects = {},
-                          expectResponse = true) {
-  info("Sending message " + name + " to content");
-  let mm = gBrowser.selectedBrowser.messageManager;
-
-  mm.sendAsyncMessage(name, data, objects);
-  if (expectResponse) {
-    return waitForContentMessage(name);
-  }
-
-  return promise.resolve();
-}
-
-/**
- * Send an async message to the frame script and get back the requested
- * computed style property.
- *
- * @param {String} selector
- *        The selector used to obtain the element.
- * @param {String} pseudo
- *        pseudo id to query, or null.
- * @param {String} name
- *        name of the property.
- */
-function* getComputedStyleProperty(selector, pseudo, propName) {
-  let data = {
-    selector,
-    pseudo,
-    name: propName
-  };
-  return yield executeInContent("Test:GetComputedStylePropertyValue", data);
-}
-
-/**
- * Send an async message to the frame script and wait until the requested
- * computed style property has the expected value.
- *
- * @param {String} selector
- *        The selector used to obtain the element.
- * @param {String} pseudo
- *        pseudo id to query, or null.
- * @param {String} prop
- *        name of the property.
- * @param {String} expected
- *        expected value of property
- * @param {String} name
- *        the name used in test message
- */
-function* waitForComputedStyleProperty(selector, pseudo, name, expected) {
-  let data = {
-    selector,
-    pseudo,
-    expected,
-    name
-  };
-  return yield executeInContent("Test:WaitForComputedStylePropertyValue", data);
-}
-
-/**
- * Given an inplace editable element, click to switch it to edit mode, wait for
- * focus
- *
- * @return a promise that resolves to the inplace-editor element when ready
- */
-var focusEditableField = Task.async(function* (ruleView, editable, xOffset = 1,
-                                               yOffset = 1, options = {}) {
-  let onFocus = once(editable.parentNode, "focus", true);
-  info("Clicking on editable field to turn to edit mode");
-  EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
-    editable.ownerDocument.defaultView);
-  yield onFocus;
-
-  info("Editable field gained focus, returning the input field now");
-  let onEdit = inplaceEditor(editable.ownerDocument.activeElement);
-
-  return onEdit;
-});
-
-/**
  * Polls a given function waiting for it to return true.
  *
  * @param {Function} validatorFn
  *        A validator function that returns a boolean.
  *        This is called every few milliseconds to check if the result is true.
  *        When it is true, the promise resolves.
  * @param {String} name
  *        Optional name of the test. This is used to generate
@@ -254,119 +137,16 @@ var getFontFamilyDataURL = Task.async(fu
 /* *********************************************
  * RULE-VIEW
  * *********************************************
  * Rule-view related test utility functions
  * This object contains functions to get rules, get properties, ...
  */
 
 /**
- * Get the DOMNode for a css rule in the rule-view that corresponds to the given
- * selector
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view for which the rule
- *        object is wanted
- * @return {DOMNode}
- */
-function getRuleViewRule(view, selectorText) {
-  let rule;
-  for (let r of view.styleDocument.querySelectorAll(".ruleview-rule")) {
-    let selector = r.querySelector(".ruleview-selectorcontainer, " +
-                                   ".ruleview-selector-matched");
-    if (selector && selector.textContent === selectorText) {
-      rule = r;
-      break;
-    }
-  }
-
-  return rule;
-}
-
-/**
- * Get references to the name and value span nodes corresponding to a given
- * selector and property name in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for the property in
- * @param {String} propertyName
- *        The name of the property
- * @return {Object} An object like {nameSpan: DOMNode, valueSpan: DOMNode}
- */
-function getRuleViewProperty(view, selectorText, propertyName) {
-  let prop;
-
-  let rule = getRuleViewRule(view, selectorText);
-  if (rule) {
-    // Look for the propertyName in that rule element
-    for (let p of rule.querySelectorAll(".ruleview-property")) {
-      let nameSpan = p.querySelector(".ruleview-propertyname");
-      let valueSpan = p.querySelector(".ruleview-propertyvalue");
-
-      if (nameSpan.textContent === propertyName) {
-        prop = {nameSpan: nameSpan, valueSpan: valueSpan};
-        break;
-      }
-    }
-  }
-  return prop;
-}
-
-/**
- * Get the text value of the property corresponding to a given selector and name
- * in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for the property in
- * @param {String} propertyName
- *        The name of the property
- * @return {String} The property value
- */
-function getRuleViewPropertyValue(view, selectorText, propertyName) {
-  return getRuleViewProperty(view, selectorText, propertyName)
-    .valueSpan.textContent;
-}
-
-/**
- * Get a reference to the selector DOM element corresponding to a given selector
- * in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for
- * @return {DOMNode} The selector DOM element
- */
-function getRuleViewSelector(view, selectorText) {
-  let rule = getRuleViewRule(view, selectorText);
-  return rule.querySelector(".ruleview-selector, .ruleview-selector-matched");
-}
-
-/**
- * Get a reference to the selectorhighlighter icon DOM element corresponding to
- * a given selector in the rule-view
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} selectorText
- *        The selector in the rule-view to look for
- * @return {DOMNode} The selectorhighlighter icon DOM element
- */
-function getRuleViewSelectorHighlighterIcon(view, selectorText) {
-  let rule = getRuleViewRule(view, selectorText);
-  return rule.querySelector(".ruleview-selectorhighlighter");
-}
-
-/**
  * Simulate a color change in a given color picker tooltip, and optionally wait
  * for a given element in the page to have its style changed as a result
  *
  * @param {RuleView} ruleView
  *        The related rule view instance
  * @param {SwatchColorPickerTooltip} colorPicker
  * @param {Array} newRgba
  *        The new color to be set [r, g, b, a]
@@ -395,110 +175,16 @@ var simulateColorPickerChange = Task.asy
     info("Waiting for the style to be applied on the page");
     yield waitForSuccess(() => {
       let {element, name, value} = expectedChange;
       return content.getComputedStyle(element)[name] === value;
     }, "Color picker change applied on the page");
   }
 });
 
-/**
- * Get a rule-link from the rule-view given its index
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {Number} index
- *        The index of the link to get
- * @return {DOMNode} The link if any at this index
- */
-function getRuleViewLinkByIndex(view, index) {
-  let links = view.styleDocument.querySelectorAll(".ruleview-rule-source");
-  return links[index];
-}
-
-/**
- * Get rule-link text from the rule-view given its index
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {Number} index
- *        The index of the link to get
- * @return {String} The string at this index
- */
-function getRuleViewLinkTextByIndex(view, index) {
-  let link = getRuleViewLinkByIndex(view, index);
-  return link.querySelector(".ruleview-rule-source-label").textContent;
-}
-
-/**
- * Click on a rule-view's close brace to focus a new property name editor
- *
- * @param {RuleEditor} ruleEditor
- *        An instance of RuleEditor that will receive the new property
- * @return a promise that resolves to the newly created editor when ready and
- * focused
- */
-var focusNewRuleViewProperty = Task.async(function* (ruleEditor) {
-  info("Clicking on a close ruleEditor brace to start editing a new property");
-  ruleEditor.closeBrace.scrollIntoView();
-  let editor = yield focusEditableField(ruleEditor.ruleView,
-    ruleEditor.closeBrace);
-
-  is(inplaceEditor(ruleEditor.newPropSpan), editor,
-    "Focused editor is the new property editor.");
-
-  return editor;
-});
-
-/**
- * Create a new property name in the rule-view, focusing a new property editor
- * by clicking on the close brace, and then entering the given text.
- * Keep in mind that the rule-view knows how to handle strings with multiple
- * properties, so the input text may be like: "p1:v1;p2:v2;p3:v3".
- *
- * @param {RuleEditor} ruleEditor
- *        The instance of RuleEditor that will receive the new property(ies)
- * @param {String} inputValue
- *        The text to be entered in the new property name field
- * @return a promise that resolves when the new property name has been entered
- * and once the value field is focused
- */
-var createNewRuleViewProperty = Task.async(function* (ruleEditor, inputValue) {
-  info("Creating a new property editor");
-  let editor = yield focusNewRuleViewProperty(ruleEditor);
-
-  info("Entering the value " + inputValue);
-  editor.input.value = inputValue;
-
-  info("Submitting the new value and waiting for value field focus");
-  let onFocus = once(ruleEditor.element, "focus", true);
-  EventUtils.synthesizeKey("VK_RETURN", {},
-    ruleEditor.element.ownerDocument.defaultView);
-  yield onFocus;
-});
-
-/**
- * Set the search value for the rule-view filter styles search box.
- *
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @param {String} searchValue
- *        The filter search value
- * @return a promise that resolves when the rule-view is filtered for the
- * search term
- */
-var setSearchFilter = Task.async(function* (view, searchValue) {
-  info("Setting filter text to \"" + searchValue + "\"");
-  let win = view.styleWindow;
-  let searchField = view.searchField;
-  searchField.focus();
-  synthesizeKeys(searchValue, win);
-  yield view.inspector.once("ruleview-filtered");
-});
-
 /* *********************************************
  * COMPUTED-VIEW
  * *********************************************
  * Computed-view related utility functions.
  * Allows to get properties, links, expand properties, ...
  */
 
 /**
@@ -534,28 +220,8 @@ function getComputedViewProperty(view, n
  * @param {String} name
  *        The name of the property to retrieve
  * @return {String} The property value
  */
 function getComputedViewPropertyValue(view, name, propertyName) {
   return getComputedViewProperty(view, name, propertyName)
     .valueSpan.textContent;
 }
-
-/**
- * Open the style editor context menu and return all of it's items in a flat array
- * @param {CssRuleView} view
- *        The instance of the rule-view panel
- * @return An array of MenuItems
- */
-function openStyleContextMenuAndGetAllItems(view, target) {
-  let menu = view._contextmenu._openMenu({target: target});
-
-  // Flatten all menu items into a single array to make searching through it easier
-  let allItems = [].concat.apply([], menu.items.map(function addItem(item) {
-    if (item.submenu) {
-      return addItem(item.submenu.items);
-    }
-    return item;
-  }));
-
-  return allItems;
-}
--- a/devtools/client/inspector/test/shared-head.js
+++ b/devtools/client/inspector/test/shared-head.js
@@ -1,16 +1,19 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 /* eslint no-unused-vars: [2, {"vars": "local"}] */
 /* globals registerTestActor, getTestActor, Task, openToolboxForTab, gBrowser */
+/* import-globals-from ../../framework/test/shared-head.js */
+
+var {getInplaceEditorForSpan: inplaceEditor} = require("devtools/client/shared/inplace-editor");
 
 // This file contains functions related to the inspector that are also of interest to
 // other test directores as well.
 
 /**
  * Open the toolbox, with the inspector tool visible.
  * @param {String} hostType Optional hostType, as defined in Toolbox.HostType
  * @return a promise that resolves when the inspector is ready
@@ -179,8 +182,361 @@ function manualThrottle() {
 
   throttle.flush = function () {
     calls.forEach(({func, scope, args}) => func.apply(scope, args));
     calls = [];
   };
 
   return throttle;
 }
+
+/**
+ * Wait for a content -> chrome message on the message manager (the window
+ * messagemanager is used).
+ *
+ * @param {String} name
+ *        The message name
+ * @return {Promise} A promise that resolves to the response data when the
+ * message has been received
+ */
+function waitForContentMessage(name) {
+  info("Expecting message " + name + " from content");
+
+  let mm = gBrowser.selectedBrowser.messageManager;
+
+  let def = defer();
+  mm.addMessageListener(name, function onMessage(msg) {
+    mm.removeMessageListener(name, onMessage);
+    def.resolve(msg.data);
+  });
+  return def.promise;
+}
+
+/**
+ * Send an async message to the frame script (chrome -> content) and wait for a
+ * response message with the same name (content -> chrome).
+ *
+ * @param {String} name
+ *        The message name. Should be one of the messages defined
+ *        in doc_frame_script.js
+ * @param {Object} data
+ *        Optional data to send along
+ * @param {Object} objects
+ *        Optional CPOW objects to send along
+ * @param {Boolean} expectResponse
+ *        If set to false, don't wait for a response with the same name
+ *        from the content script. Defaults to true.
+ * @return {Promise} Resolves to the response data if a response is expected,
+ * immediately resolves otherwise
+ */
+function executeInContent(name, data = {}, objects = {},
+                          expectResponse = true) {
+  info("Sending message " + name + " to content");
+  let mm = gBrowser.selectedBrowser.messageManager;
+
+  mm.sendAsyncMessage(name, data, objects);
+  if (expectResponse) {
+    return waitForContentMessage(name);
+  }
+
+  return promise.resolve();
+}
+
+/**
+ * Send an async message to the frame script and get back the requested
+ * computed style property.
+ *
+ * @param {String} selector
+ *        The selector used to obtain the element.
+ * @param {String} pseudo
+ *        pseudo id to query, or null.
+ * @param {String} name
+ *        name of the property.
+ */
+function* getComputedStyleProperty(selector, pseudo, propName) {
+  return yield executeInContent("Test:GetComputedStylePropertyValue",
+    {selector,
+     pseudo,
+     name: propName});
+}
+
+/**
+ * Send an async message to the frame script and wait until the requested
+ * computed style property has the expected value.
+ *
+ * @param {String} selector
+ *        The selector used to obtain the element.
+ * @param {String} pseudo
+ *        pseudo id to query, or null.
+ * @param {String} prop
+ *        name of the property.
+ * @param {String} expected
+ *        expected value of property
+ * @param {String} name
+ *        the name used in test message
+ */
+function* waitForComputedStyleProperty(selector, pseudo, name, expected) {
+  return yield executeInContent("Test:WaitForComputedStylePropertyValue",
+    {selector,
+     pseudo,
+     expected,
+     name});
+}
+
+/**
+ * Given an inplace editable element, click to switch it to edit mode, wait for
+ * focus
+ *
+ * @return a promise that resolves to the inplace-editor element when ready
+ */
+var focusEditableField = Task.async(function* (ruleView, editable, xOffset = 1,
+    yOffset = 1, options = {}) {
+  let onFocus = once(editable.parentNode, "focus", true);
+  info("Clicking on editable field to turn to edit mode");
+  EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
+    editable.ownerDocument.defaultView);
+  yield onFocus;
+
+  info("Editable field gained focus, returning the input field now");
+  let onEdit = inplaceEditor(editable.ownerDocument.activeElement);
+
+  return onEdit;
+});
+
+/**
+ * Get the DOMNode for a css rule in the rule-view that corresponds to the given
+ * selector.
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {String} selectorText
+ *        The selector in the rule-view for which the rule
+ *        object is wanted
+ * @param {Number} index
+ *        If there are more than 1 rule with the same selector, you may pass a
+ *        index to determine which of the rules you want.
+ * @return {DOMNode}
+ */
+function getRuleViewRule(view, selectorText, index = 0) {
+  let rule;
+  let pos = 0;
+  for (let r of view.styleDocument.querySelectorAll(".ruleview-rule")) {
+    let selector = r.querySelector(".ruleview-selectorcontainer, " +
+                                   ".ruleview-selector-matched");
+    if (selector && selector.textContent === selectorText) {
+      if (index == pos) {
+        rule = r;
+        break;
+      }
+      pos++;
+    }
+  }
+
+  return rule;
+}
+
+/**
+ * Get references to the name and value span nodes corresponding to a given
+ * selector and property name in the rule-view
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {String} selectorText
+ *        The selector in the rule-view to look for the property in
+ * @param {String} propertyName
+ *        The name of the property
+ * @return {Object} An object like {nameSpan: DOMNode, valueSpan: DOMNode}
+ */
+function getRuleViewProperty(view, selectorText, propertyName) {
+  let prop;
+
+  let rule = getRuleViewRule(view, selectorText);
+  if (rule) {
+    // Look for the propertyName in that rule element
+    for (let p of rule.querySelectorAll(".ruleview-property")) {
+      let nameSpan = p.querySelector(".ruleview-propertyname");
+      let valueSpan = p.querySelector(".ruleview-propertyvalue");
+
+      if (nameSpan.textContent === propertyName) {
+        prop = {nameSpan: nameSpan, valueSpan: valueSpan};
+        break;
+      }
+    }
+  }
+  return prop;
+}
+
+/**
+ * Get the text value of the property corresponding to a given selector and name
+ * in the rule-view
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {String} selectorText
+ *        The selector in the rule-view to look for the property in
+ * @param {String} propertyName
+ *        The name of the property
+ * @return {String} The property value
+ */
+function getRuleViewPropertyValue(view, selectorText, propertyName) {
+  return getRuleViewProperty(view, selectorText, propertyName)
+    .valueSpan.textContent;
+}
+
+/**
+ * Get a reference to the selector DOM element corresponding to a given selector
+ * in the rule-view
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {String} selectorText
+ *        The selector in the rule-view to look for
+ * @return {DOMNode} The selector DOM element
+ */
+function getRuleViewSelector(view, selectorText) {
+  let rule = getRuleViewRule(view, selectorText);
+  return rule.querySelector(".ruleview-selector, .ruleview-selector-matched");
+}
+
+/**
+ * Get a reference to the selectorhighlighter icon DOM element corresponding to
+ * a given selector in the rule-view
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {String} selectorText
+ *        The selector in the rule-view to look for
+ * @param {Number} index
+ *        If there are more than 1 rule with the same selector, use this index
+ *        to determine which one should be retrieved. Defaults to 0
+ * @return {DOMNode} The selectorhighlighter icon DOM element
+ */
+var getRuleViewSelectorHighlighterIcon = Task.async(function* (view,
+    selectorText, index = 0) {
+  let rule = getRuleViewRule(view, selectorText, index);
+
+  let editor = rule._ruleEditor;
+  if (!editor.uniqueSelector) {
+    yield once(editor, "selector-icon-created");
+  }
+
+  return rule.querySelector(".ruleview-selectorhighlighter");
+});
+
+/**
+ * Get a rule-link from the rule-view given its index
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {Number} index
+ *        The index of the link to get
+ * @return {DOMNode} The link if any at this index
+ */
+function getRuleViewLinkByIndex(view, index) {
+  let links = view.styleDocument.querySelectorAll(".ruleview-rule-source");
+  return links[index];
+}
+
+/**
+ * Get rule-link text from the rule-view given its index
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {Number} index
+ *        The index of the link to get
+ * @return {String} The string at this index
+ */
+function getRuleViewLinkTextByIndex(view, index) {
+  let link = getRuleViewLinkByIndex(view, index);
+  return link.querySelector(".ruleview-rule-source-label").textContent;
+}
+
+/**
+ * Click on a rule-view's close brace to focus a new property name editor
+ *
+ * @param {RuleEditor} ruleEditor
+ *        An instance of RuleEditor that will receive the new property
+ * @return a promise that resolves to the newly created editor when ready and
+ * focused
+ */
+var focusNewRuleViewProperty = Task.async(function* (ruleEditor) {
+  info("Clicking on a close ruleEditor brace to start editing a new property");
+
+  // Use bottom alignment to avoid scrolling out of the parent element area.
+  ruleEditor.closeBrace.scrollIntoView(false);
+  let editor = yield focusEditableField(ruleEditor.ruleView,
+    ruleEditor.closeBrace);
+
+  is(inplaceEditor(ruleEditor.newPropSpan), editor,
+    "Focused editor is the new property editor.");
+
+  return editor;
+});
+
+/**
+ * Create a new property name in the rule-view, focusing a new property editor
+ * by clicking on the close brace, and then entering the given text.
+ * Keep in mind that the rule-view knows how to handle strings with multiple
+ * properties, so the input text may be like: "p1:v1;p2:v2;p3:v3".
+ *
+ * @param {RuleEditor} ruleEditor
+ *        The instance of RuleEditor that will receive the new property(ies)
+ * @param {String} inputValue
+ *        The text to be entered in the new property name field
+ * @return a promise that resolves when the new property name has been entered
+ * and once the value field is focused
+ */
+var createNewRuleViewProperty = Task.async(function* (ruleEditor, inputValue) {
+  info("Creating a new property editor");
+  let editor = yield focusNewRuleViewProperty(ruleEditor);
+
+  info("Entering the value " + inputValue);
+  editor.input.value = inputValue;
+
+  info("Submitting the new value and waiting for value field focus");
+  let onFocus = once(ruleEditor.element, "focus", true);
+  EventUtils.synthesizeKey("VK_RETURN", {},
+    ruleEditor.element.ownerDocument.defaultView);
+  yield onFocus;
+});
+
+/**
+ * Set the search value for the rule-view filter styles search box.
+ *
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @param {String} searchValue
+ *        The filter search value
+ * @return a promise that resolves when the rule-view is filtered for the
+ * search term
+ */
+var setSearchFilter = Task.async(function* (view, searchValue) {
+  info("Setting filter text to \"" + searchValue + "\"");
+
+  let searchField = view.searchField;
+  searchField.focus();
+
+  for (let key of searchValue.split("")) {
+    EventUtils.synthesizeKey(key, {}, view.styleWindow);
+  }
+
+  yield view.inspector.once("ruleview-filtered");
+});
+
+/**
+ * Open the style editor context menu and return all of it's items in a flat array
+ * @param {CssRuleView} view
+ *        The instance of the rule-view panel
+ * @return An array of MenuItems
+ */
+function openStyleContextMenuAndGetAllItems(view, target) {
+  let menu = view._contextmenu._openMenu({target: target});
+
+  // Flatten all menu items into a single array to make searching through it easier
+  let allItems = [].concat.apply([], menu.items.map(function addItem(item) {
+    if (item.submenu) {
+      return addItem(item.submenu.items);
+    }
+    return item;
+  }));
+
+  return allItems;
+}
--- a/devtools/client/inspector/toolsidebar.js
+++ b/devtools/client/inspector/toolsidebar.js
@@ -219,49 +219,58 @@ ToolSidebar.prototype = {
    */
   handleSelectionChange: function (id) {
     if (this._destroyed) {
       return;
     }
 
     let previousTool = this._currentTool;
     if (previousTool) {
-      if (this._telemetry) {
-        this._telemetry.toolClosed(previousTool);
-      }
       this.emit(previousTool + "-unselected");
     }
 
     this._currentTool = id;
 
-    if (this._telemetry) {
-      this._telemetry.toolOpened(this._currentTool);
+    this.updateTelemetryOnChange(id, previousTool);
+    this.emit(this._currentTool + "-selected");
+    this.emit("select", this._currentTool);
+  },
+
+  /**
+   * Log toolClosed and toolOpened events on telemetry.
+   *
+   * @param  {String} currentToolId
+   *         id of the tool being selected.
+   * @param  {String} previousToolId
+   *         id of the previously selected tool.
+   */
+  updateTelemetryOnChange: function (currentToolId, previousToolId) {
+    if (currentToolId === previousToolId || !this._telemetry) {
+      // Skip telemetry if the tool id did not change or telemetry is unavailable.
+      return;
     }
 
-    this.emit(this._currentTool + "-selected");
-    this.emit("select", this._currentTool);
+    if (previousToolId) {
+      this._telemetry.toolClosed(previousToolId);
+    }
+    this._telemetry.toolOpened(currentToolId);
   },
 
   /**
    * Show the sidebar.
    *
    * @param  {String} id
    *         The sidebar tab id to select.
    */
   show: function (id) {
     this._tabbox.removeAttribute("hidden");
 
-    // If an id is given, select the corresponding sidebar tab and record the
-    // tool opened.
+    // If an id is given, select the corresponding sidebar tab.
     if (id) {
-      this._currentTool = id;
-
-      if (this._telemetry) {
-        this._telemetry.toolOpened(this._currentTool);
-      }
+      this.select(id);
     }
 
     this.emit("show");
   },
 
   /**
    * Show the sidebar.
    */
deleted file mode 100644
--- a/devtools/client/netmonitor/components/clear-button.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { DOM } = require("devtools/client/shared/vendor/react");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const { L10N } = require("../l10n");
-const Actions = require("../actions/index");
-
-const { button } = DOM;
-
-/*
- * Clear button component
- * A type of tool button is responsible for cleaning network requests.
- */
-function ClearButton({ onClick }) {
-  return button({
-    id: "requests-menu-clear-button",
-    className: "devtools-button devtools-clear-icon",
-    title: L10N.getStr("netmonitor.toolbar.clear"),
-    onClick,
-  });
-}
-
-module.exports = connect(
-  undefined,
-  dispatch => ({
-    onClick: () => dispatch(Actions.clearRequests())
-  })
-)(ClearButton);
deleted file mode 100644
--- a/devtools/client/netmonitor/components/filter-buttons.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const { L10N } = require("../l10n");
-const Actions = require("../actions/index");
-
-const { button, div } = DOM;
-
-function FilterButtons({
-  requestFilterTypes,
-  toggleRequestFilterType,
-}) {
-  const buttons = requestFilterTypes.entrySeq().map(([type, checked]) => {
-    let classList = ["menu-filter-button"];
-    checked && classList.push("checked");
-
-    return button({
-      id: `requests-menu-filter-${type}-button`,
-      key: type,
-      className: classList.join(" "),
-      "data-key": type,
-      onClick: toggleRequestFilterType,
-      onKeyDown: toggleRequestFilterType,
-    }, L10N.getStr(`netmonitor.toolbar.filter.${type}`));
-  }).toArray();
-
-  return div({ id: "requests-menu-filter-buttons" }, buttons);
-}
-
-FilterButtons.propTypes = {
-  requestFilterTypes: PropTypes.object.isRequired,
-  toggleRequestFilterType: PropTypes.func.isRequired,
-};
-
-module.exports = connect(
-  (state) => ({ requestFilterTypes: state.filters.requestFilterTypes }),
-  (dispatch) => ({
-    toggleRequestFilterType: (evt) => {
-      if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
-        return;
-      }
-      dispatch(Actions.toggleRequestFilterType(evt.target.dataset.key));
-    },
-  })
-)(FilterButtons);
--- a/devtools/client/netmonitor/components/moz.build
+++ b/devtools/client/netmonitor/components/moz.build
@@ -1,18 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DevToolsModules(
-    'clear-button.js',
-    'filter-buttons.js',
     'request-list-content.js',
     'request-list-empty.js',
     'request-list-header.js',
     'request-list-item.js',
     'request-list-tooltip.js',
     'request-list.js',
-    'search-box.js',
-    'summary-button.js',
-    'toggle-button.js',
+    'statistics-panel.js',
     'toolbar.js',
 )
deleted file mode 100644
--- a/devtools/client/netmonitor/components/search-box.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const SearchBox = require("devtools/client/shared/components/search-box");
-const { L10N } = require("../l10n");
-const Actions = require("../actions/index");
-const { FILTER_SEARCH_DELAY } = require("../constants");
-
-module.exports = connect(
-  (state) => ({
-    delay: FILTER_SEARCH_DELAY,
-    keyShortcut: L10N.getStr("netmonitor.toolbar.filterFreetext.key"),
-    placeholder: L10N.getStr("netmonitor.toolbar.filterFreetext.label"),
-    type: "filter",
-  }),
-  (dispatch) => ({
-    onChange: (text) => dispatch(Actions.setRequestFilterText(text)),
-  })
-)(SearchBox);
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/components/statistics-panel.js
@@ -0,0 +1,229 @@
+/* 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/. */
+
+/* globals document */
+
+"use strict";
+
+const {
+  createClass,
+  DOM,
+  PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { Chart } = require("devtools/client/shared/widgets/Chart");
+const { PluralForm } = require("devtools/shared/plural-form");
+const Actions = require("../actions/index");
+const { Filters } = require("../filter-predicates");
+const { L10N } = require("../l10n");
+const {
+  getSizeWithDecimals,
+  getTimeWithDecimals
+} = require("../utils/format-utils");
+
+const { button, div } = DOM;
+
+const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
+const BACK_BUTTON = L10N.getStr("netmonitor.backButton");
+const CHARTS_CACHE_ENABLED = L10N.getStr("charts.cacheEnabled");
+const CHARTS_CACHE_DISABLED = L10N.getStr("charts.cacheDisabled");
+
+/*
+ * Statistics panel component
+ * Performance analysis tool which shows you how long the browser takes to
+ * download the different parts of your site.
+ */
+const StatisticsPanel = createClass({
+  displayName: "StatisticsPanel",
+
+  propTypes: {
+    closeStatistics: PropTypes.func.isRequired,
+    enableRequestFilterTypeOnly: PropTypes.func.isRequired,
+    requests: PropTypes.object,
+  },
+
+  componentDidUpdate(prevProps) {
+    const { requests } = this.props;
+    let ready = requests && requests.every((req) =>
+      req.contentSize !== undefined && req.mimeType && req.responseHeaders &&
+      req.status !== undefined && req.totalTime !== undefined
+    );
+
+    this.createChart({
+      id: "primedCacheChart",
+      title: CHARTS_CACHE_ENABLED,
+      data: ready ? this.sanitizeChartDataSource(requests, false) : null,
+    });
+
+    this.createChart({
+      id: "emptyCacheChart",
+      title: CHARTS_CACHE_DISABLED,
+      data: ready ? this.sanitizeChartDataSource(requests, true) : null,
+    });
+  },
+
+  createChart({ id, title, data }) {
+    // Create a new chart.
+    let chart = Chart.PieTable(document, {
+      diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
+      title,
+      data,
+      strings: {
+        size: (value) =>
+          L10N.getFormatStr("charts.sizeKB", getSizeWithDecimals(value / 1024)),
+        time: (value) =>
+          L10N.getFormatStr("charts.totalS", getTimeWithDecimals(value / 1000)),
+      },
+      totals: {
+        cached: (total) => L10N.getFormatStr("charts.totalCached", total),
+        count: (total) => L10N.getFormatStr("charts.totalCount", total),
+        size: (total) =>
+          L10N.getFormatStr("charts.totalSize", getSizeWithDecimals(total / 1024)),
+        time: (total) => {
+          let seconds = total / 1000;
+          let string = getTimeWithDecimals(seconds);
+          return PluralForm.get(seconds,
+            L10N.getStr("charts.totalSeconds")).replace("#1", string);
+        },
+      },
+      sorted: true,
+    });
+
+    chart.on("click", (_, { label }) => {
+      // Reset FilterButtons and enable one filter exclusively
+      this.props.closeStatistics();
+      this.props.enableRequestFilterTypeOnly(label);
+    });
+
+    let container = this.refs[id];
+
+    // Nuke all existing charts of the specified type.
+    while (container.hasChildNodes()) {
+      container.firstChild.remove();
+    }
+
+    container.appendChild(chart.node);
+  },
+
+  sanitizeChartDataSource(requests, emptyCache) {
+    let data = [
+      "html", "css", "js", "xhr", "fonts", "images", "media", "flash", "ws", "other"
+    ].map((type) => ({ cached: 0, count: 0, label: type, size: 0, time: 0 }));
+
+    for (let request of requests) {
+      let type;
+
+      if (Filters.html(request)) {
+        // "html"
+        type = 0;
+      } else if (Filters.css(request)) {
+        // "css"
+        type = 1;
+      } else if (Filters.js(request)) {
+        // "js"
+        type = 2;
+      } else if (Filters.fonts(request)) {
+        // "fonts"
+        type = 4;
+      } else if (Filters.images(request)) {
+        // "images"
+        type = 5;
+      } else if (Filters.media(request)) {
+        // "media"
+        type = 6;
+      } else if (Filters.flash(request)) {
+        // "flash"
+        type = 7;
+      } else if (Filters.ws(request)) {
+        // "ws"
+        type = 8;
+      } else if (Filters.xhr(request)) {
+        // Verify XHR last, to categorize other mime types in their own blobs.
+        // "xhr"
+        type = 3;
+      } else {
+        // "other"
+        type = 9;
+      }
+
+      if (emptyCache || !this.responseIsFresh(request)) {
+        data[type].time += request.totalTime || 0;
+        data[type].size += request.contentSize || 0;
+      } else {
+        data[type].cached++;
+      }
+      data[type].count++;
+    }
+
+    return data.filter(e => e.count > 0);
+  },
+
+  /**
+   * Checks if the "Expiration Calculations" defined in section 13.2.4 of the
+   * "HTTP/1.1: Caching in HTTP" spec holds true for a collection of headers.
+   *
+   * @param object
+   *        An object containing the { responseHeaders, status } properties.
+   * @return boolean
+   *         True if the response is fresh and loaded from cache.
+   */
+  responseIsFresh({ responseHeaders, status }) {
+    // Check for a "304 Not Modified" status and response headers availability.
+    if (status != 304 || !responseHeaders) {
+      return false;
+    }
+
+    let list = responseHeaders.headers;
+    let cacheControl = list.find(e => e.name.toLowerCase() === "cache-control");
+    let expires = list.find(e => e.name.toLowerCase() === "expires");
+
+    // Check the "Cache-Control" header for a maximum age value.
+    if (cacheControl) {
+      let maxAgeMatch =
+        cacheControl.value.match(/s-maxage\s*=\s*(\d+)/) ||
+        cacheControl.value.match(/max-age\s*=\s*(\d+)/);
+
+      if (maxAgeMatch && maxAgeMatch.pop() > 0) {
+        return true;
+      }
+    }
+
+    // Check the "Expires" header for a valid date.
+    if (expires && Date.parse(expires.value)) {
+      return true;
+    }
+
+    return false;
+  },
+
+  render() {
+    const { closeStatistics } = this.props;
+    return (
+      div({ className: "statistics-panel" },
+        button({
+          className: "back-button devtools-toolbarbutton",
+          "data-text-only": "true",
+          title: BACK_BUTTON,
+          onClick: closeStatistics,
+        }, BACK_BUTTON),
+        div({ className: "charts-container devtools-responsive-container" },
+          div({ ref: "primedCacheChart", className: "charts primed-cache-chart" }),
+          div({ className: "splitter devtools-side-splitter" }),
+          div({ ref: "emptyCacheChart", className: "charts empty-cache-chart" }),
+        ),
+      )
+    );
+  }
+});
+
+module.exports = connect(
+  (state) => ({
+    requests: state.requests.requests.valueSeq(),
+  }),
+  (dispatch) => ({
+    closeStatistics: () => dispatch(Actions.openStatistics(false)),
+    enableRequestFilterTypeOnly: (label) =>
+      dispatch(Actions.enableRequestFilterTypeOnly(label)),
+  })
+)(StatisticsPanel);
deleted file mode 100644
--- a/devtools/client/netmonitor/components/summary-button.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const { PluralForm } = require("devtools/shared/plural-form");
-const { L10N } = require("../l10n");
-const { getDisplayedRequestsSummary } = require("../selectors/index");
-const Actions = require("../actions/index");
-const {
-  getSizeWithDecimals,
-  getTimeWithDecimals,
-} = require("../utils/format-utils");
-
-const { button, span } = DOM;
-
-function SummaryButton({
-  summary,
-  triggerSummary,
-}) {
-  let { count, bytes, millis } = summary;
-  const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
-    PluralForm.get(count, L10N.getStr("networkMenu.summary"))
-    .replace("#1", count)
-    .replace("#2", getSizeWithDecimals(bytes / 1024))
-    .replace("#3", getTimeWithDecimals(millis / 1000));
-
-  return button({
-    id: "requests-menu-network-summary-button",
-    className: "devtools-button",
-    title: count ? text : L10N.getStr("netmonitor.toolbar.perf"),
-    onClick: triggerSummary,
-  },
-  span({ className: "summary-info-icon" }),
-  span({ className: "summary-info-text" }, text));
-}
-
-SummaryButton.propTypes = {
-  summary: PropTypes.object.isRequired,
-};
-
-module.exports = connect(
-  (state) => ({
-    summary: getDisplayedRequestsSummary(state),
-  }),
-  (dispatch) => ({
-    triggerSummary: () => {
-      dispatch(Actions.openStatistics(true));
-    },
-  })
-)(SummaryButton);
deleted file mode 100644
--- a/devtools/client/netmonitor/components/toggle-button.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
-const { L10N } = require("../l10n");
-const Actions = require("../actions/index");
-const { isSidebarToggleButtonDisabled } = require("../selectors/index");
-
-const { button } = DOM;
-
-function ToggleButton({
-  disabled,
-  open,
-  onToggle,
-}) {
-  let className = ["devtools-button"];
-  if (!open) {
-    className.push("pane-collapsed");
-  }
-
-  const title = open ? L10N.getStr("collapseDetailsPane") :
-                       L10N.getStr("expandDetailsPane");
-
-  return button({
-    id: "details-pane-toggle",
-    className: className.join(" "),
-    title,
-    disabled,
-    tabIndex: "0",
-    onMouseDown: onToggle,
-  });
-}
-
-ToggleButton.propTypes = {
-  disabled: PropTypes.bool.isRequired,
-  onToggle: PropTypes.func.isRequired,
-};
-
-module.exports = connect(
-  (state) => ({
-    disabled: isSidebarToggleButtonDisabled(state),
-    open: state.ui.sidebarOpen,
-  }),
-  (dispatch) => ({
-    onToggle: () => dispatch(Actions.toggleSidebar())
-  })
-)(ToggleButton);
--- a/devtools/client/netmonitor/components/toolbar.js
+++ b/devtools/client/netmonitor/components/toolbar.js
@@ -2,36 +2,155 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const {
   createFactory,
   DOM,
+  PropTypes,
 } = require("devtools/client/shared/vendor/react");
-const ClearButton = createFactory(require("./clear-button"));
-const FilterButtons = createFactory(require("./filter-buttons"));
-const ToolbarSearchBox = createFactory(require("./search-box"));
-const SummaryButton = createFactory(require("./summary-button"));
-const ToggleButton = createFactory(require("./toggle-button"));
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { PluralForm } = require("devtools/shared/plural-form");
+const Actions = require("../actions/index");
+const { L10N } = require("../l10n");
+const {
+  getDisplayedRequestsSummary,
+  isSidebarToggleButtonDisabled,
+} = require("../selectors/index");
+const {
+  getSizeWithDecimals,
+  getTimeWithDecimals,
+} = require("../utils/format-utils");
+const { FILTER_SEARCH_DELAY } = require("../constants");
 
-const { span } = DOM;
+// Components
+const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
+
+const { button, div, span } = DOM;
+
+const COLLPASE_DETAILS_PANE = L10N.getStr("collapseDetailsPane");
+const EXPAND_DETAILS_PANE = L10N.getStr("expandDetailsPane");
+const SEARCH_KEY_SHORTCUT = L10N.getStr("netmonitor.toolbar.filterFreetext.key");
+const SEARCH_PLACE_HOLDER = L10N.getStr("netmonitor.toolbar.filterFreetext.label");
+const TOOLBAR_CLEAR = L10N.getStr("netmonitor.toolbar.clear");
 
 /*
  * Network monitor toolbar component
  * Toolbar contains a set of useful tools to control network requests
  */
-function Toolbar() {
-  return span({ className: "devtools-toolbar devtools-toolbar-container" },
-    span({ className: "devtools-toolbar-group" },
-      ClearButton(),
-      FilterButtons()
-    ),
-    span({ className: "devtools-toolbar-group" },
-      SummaryButton(),
-      ToolbarSearchBox(),
-      ToggleButton()
+function Toolbar({
+  clearRequests,
+  openStatistics,
+  requestFilterTypes,
+  setRequestFilterText,
+  sidebarToggleDisabled,
+  sidebarOpen,
+  summary,
+  toggleRequestFilterType,
+  toggleSidebar,
+}) {
+  let toggleButtonClassName = ["devtools-button"];
+  if (!sidebarOpen) {
+    toggleButtonClassName.push("pane-collapsed");
+  }
+
+  let { count, bytes, millis } = summary;
+  const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
+    PluralForm.get(count, L10N.getStr("networkMenu.summary"))
+    .replace("#1", count)
+    .replace("#2", getSizeWithDecimals(bytes / 1024))
+    .replace("#3", getTimeWithDecimals(millis / 1000));
+
+  const buttons = requestFilterTypes.entrySeq().map(([type, checked]) => {
+    let classList = ["menu-filter-button"];
+    checked && classList.push("checked");
+
+    return (
+      button({
+        id: `requests-menu-filter-${type}-button`,
+        key: type,
+        className: classList.join(" "),
+        "data-key": type,
+        onClick: toggleRequestFilterType,
+        onKeyDown: toggleRequestFilterType,
+      },
+        L10N.getStr(`netmonitor.toolbar.filter.${type}`)
+      )
+    );
+  }).toArray();
+
+  return (
+    span({ className: "devtools-toolbar devtools-toolbar-container" },
+      span({ className: "devtools-toolbar-group" },
+        button({
+          id: "requests-menu-clear-button",
+          className: "devtools-button devtools-clear-icon",
+          title: TOOLBAR_CLEAR,
+          onClick: clearRequests,
+        }),
+        div({ id: "requests-menu-filter-buttons" }, buttons),
+      ),
+      span({ className: "devtools-toolbar-group" },
+        button({
+          id: "requests-menu-network-summary-button",
+          className: "devtools-button",
+          title: count ? text : L10N.getStr("netmonitor.toolbar.perf"),
+          onClick: openStatistics,
+        },
+          span({ className: "summary-info-icon" }),
+          span({ className: "summary-info-text" }, text),
+        ),
+        SearchBox({
+          delay: FILTER_SEARCH_DELAY,
+          keyShortcut: SEARCH_KEY_SHORTCUT,
+          placeholder: SEARCH_PLACE_HOLDER,
+          type: "filter",
+          onChange: setRequestFilterText,
+        }),
+        button({
+          id: "details-pane-toggle",
+          className: toggleButtonClassName.join(" "),
+          title: sidebarOpen ? COLLPASE_DETAILS_PANE : EXPAND_DETAILS_PANE,
+          disabled: sidebarToggleDisabled,
+          tabIndex: "0",
+          onMouseDown: toggleSidebar,
+        }),
+      )
     )
   );
 }
 
-module.exports = Toolbar;
+Toolbar.displayName = "Toolbar";
+
+Toolbar.propTypes = {
+  clearRequests: PropTypes.func.isRequired,
+  openStatistics: PropTypes.func.isRequired,
+  requestFilterTypes: PropTypes.object.isRequired,
+  setRequestFilterText: PropTypes.func.isRequired,
+  sidebarToggleDisabled: PropTypes.bool.isRequired,
+  sidebarOpen: PropTypes.bool.isRequired,
+  summary: PropTypes.object.isRequired,
+  toggleRequestFilterType: PropTypes.func.isRequired,
+  toggleSidebar: PropTypes.func.isRequired,
+};
+
+module.exports = connect(
+  (state) => ({
+    sidebarToggleDisabled: isSidebarToggleButtonDisabled(state),
+    sidebarOpen: state.ui.sidebarOpen,
+    requestFilterTypes: state.filters.requestFilterTypes,
+    summary: getDisplayedRequestsSummary(state),
+  }),
+  (dispatch) => ({
+    clearRequests: () => dispatch(Actions.clearRequests()),
+    openStatistics: () => dispatch(Actions.openStatistics(true)),
+    setRequestFilterText: (text) => dispatch(Actions.setRequestFilterText(text)),
+    toggleRequestFilterType: (evt) => {
+      if (evt.type === "keydown" && (evt.key !== "" || evt.key !== "Enter")) {
+        return;
+      }
+      dispatch(Actions.toggleRequestFilterType(evt.target.dataset.key));
+    },
+    toggleSidebar: () => dispatch(Actions.toggleSidebar()),
+  })
+)(Toolbar);
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -23,18 +23,16 @@ DevToolsModules(
     'netmonitor-view.js',
     'panel.js',
     'prefs.js',
     'request-list-context-menu.js',
     'request-utils.js',
     'requests-menu-view.js',
     'sidebar-view.js',
     'sort-predicates.js',
-    'statistics-view.js',
     'store.js',
-    'toolbar-view.js',
     'waterfall-background.js',
 )
 
 BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Developer Tools: Netmonitor')
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -1,84 +1,86 @@
 /* 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/. */
 
-/* eslint-disable mozilla/reject-some-requires */
 /* globals $, gStore, NetMonitorController, dumpn */
 
 "use strict";
 
-const { testing: isTesting } = require("devtools/shared/flags");
 const { Task } = require("devtools/shared/task");
 const { ViewHelpers } = require("devtools/client/shared/widgets/view-helpers");
 const { RequestsMenuView } = require("./requests-menu-view");
 const { CustomRequestView } = require("./custom-request-view");
-const { ToolbarView } = require("./toolbar-view");
 const { SidebarView } = require("./sidebar-view");
-const { StatisticsView } = require("./statistics-view");
 const { ACTIVITY_TYPE } = require("./constants");
 const { Prefs } = require("./prefs");
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const Actions = require("./actions/index");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+
+// Components
 const DetailsPanel = createFactory(require("./shared/components/details-panel"));
-
-// ms
-const WDA_DEFAULT_VERIFY_INTERVAL = 50;
-
-// Use longer timeout during testing as the tests need this process to succeed
-// and two seconds is quite short on slow debug builds. The timeout here should
-// be at least equal to the general mochitest timeout of 45 seconds so that this
-// never gets hit during testing.
-// ms
-const WDA_DEFAULT_GIVE_UP_TIMEOUT = isTesting ? 45000 : 2000;
+const StatisticsPanel = createFactory(require("./components/statistics-panel"));
+const Toolbar = createFactory(require("./components/toolbar"));
 
 /**
  * Object defining the network monitor view components.
  */
 var NetMonitorView = {
   /**
    * Initializes the network monitor view.
    */
   initialize: function () {
     this._initializePanes();
 
-    this.Toolbar.initialize(gStore);
-    this.RequestsMenu.initialize(gStore);
-    this.CustomRequest.initialize();
-    this.Statistics.initialize(gStore);
-
     this.detailsPanel = $("#react-details-panel-hook");
 
     ReactDOM.render(Provider(
       { store: gStore },
       DetailsPanel({ toolbox: NetMonitorController._toolbox }),
     ), this.detailsPanel);
 
+    this.statisticsPanel = $("#statistics-panel");
+
+    ReactDOM.render(Provider(
+      { store: gStore },
+      StatisticsPanel(),
+    ), this.statisticsPanel);
+
+    this.toolbar = $("#react-toolbar-hook");
+
+    ReactDOM.render(Provider(
+      { store: gStore },
+      Toolbar(),
+    ), this.toolbar);
+
+    this.RequestsMenu.initialize(gStore);
+    this.CustomRequest.initialize();
+
     // Store watcher here is for observing the statisticsOpen state change.
     // It should be removed once we migrate to react and apply react/redex binding.
     this.unsubscribeStore = gStore.subscribe(storeWatcher(
       false,
       () => gStore.getState().ui.statisticsOpen,
       this.toggleFrontendMode.bind(this)
     ));
   },
 
   /**
    * Destroys the network monitor view.
    */
   destroy: function () {
     this._isDestroyed = true;
-    this.Toolbar.destroy();
     this.RequestsMenu.destroy();
     this.CustomRequest.destroy();
-    this.Statistics.destroy();
     ReactDOM.unmountComponentAtNode(this.detailsPanel);
+    ReactDOM.unmountComponentAtNode(this.statisticsPanel);
+    ReactDOM.unmountComponentAtNode(this.toolbar);
     this.unsubscribeStore();
 
     this._destroyPanes();
   },
 
   /**
    * Initializes the UI for all the displayed panes.
    */
@@ -136,139 +138,63 @@ var NetMonitorView = {
       gStore.dispatch(Actions.openSidebar(false));
     }
 
     if (tabIndex !== undefined) {
       $("#event-details-pane").selectedIndex = tabIndex;
     }
   },
 
-  /**
-   * Gets the current mode for this tool.
-   * @return string (e.g, "network-inspector-view" or "network-statistics-view")
-   */
   get currentFrontendMode() {
     // The getter may be called from a timeout after the panel is destroyed.
     if (!this._body.selectedPanel) {
       return null;
     }
     return this._body.selectedPanel.id;
   },
 
-  /**
-   * Toggles between the frontend view modes ("Inspector" vs. "Statistics").
-   */
   toggleFrontendMode: function () {
     if (gStore.getState().ui.statisticsOpen) {
       this.showNetworkStatisticsView();
     } else {
       this.showNetworkInspectorView();
     }
   },
 
-  /**
-   * Switches to the "Inspector" frontend view mode.
-   */
   showNetworkInspectorView: function () {
-    this._body.selectedPanel = $("#network-inspector-view");
+    this._body.selectedPanel = $("#inspector-panel");
   },
 
-  /**
-   * Switches to the "Statistics" frontend view mode.
-   */
   showNetworkStatisticsView: function () {
-    this._body.selectedPanel = $("#network-statistics-view");
-
-    let controller = NetMonitorController;
-    let requestsView = this.RequestsMenu;
-    let statisticsView = this.Statistics;
-
-    Task.spawn(function* () {
-      statisticsView.displayPlaceholderCharts();
-      yield controller.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
-
-      try {
-        // • The response headers and status code are required for determining
-        // whether a response is "fresh" (cacheable).
-        // • The response content size and request total time are necessary for
-        // populating the statistics view.
-        // • The response mime type is used for categorization.
-        yield whenDataAvailable(requestsView.store, [
-          "responseHeaders", "status", "contentSize", "mimeType", "totalTime"
-        ]);
-      } catch (ex) {
-        // Timed out while waiting for data. Continue with what we have.
-        console.error(ex);
-      }
-
-      const requests = requestsView.store.getState().requests.requests.valueSeq();
-      statisticsView.createPrimedCacheChart(requests);
-      statisticsView.createEmptyCacheChart(requests);
-    });
+    this._body.selectedPanel = $("#statistics-panel");
+    NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
   },
 
   reloadPage: function () {
     NetMonitorController.triggerActivity(
       ACTIVITY_TYPE.RELOAD.WITH_CACHE_DEFAULT);
   },
 
   _body: null,
   _detailsPane: null,
 };
 
-/**
- * Makes sure certain properties are available on all objects in a data store.
- *
- * @param Store dataStore
- *        A Redux store for which to check the availability of properties.
- * @param array mandatoryFields
- *        A list of strings representing properties of objects in dataStore.
- * @return object
- *         A promise resolved when all objects in dataStore contain the
- *         properties defined in mandatoryFields.
- */
-function whenDataAvailable(dataStore, mandatoryFields) {
-  return new Promise((resolve, reject) => {
-    let interval = setInterval(() => {
-      const { requests } = dataStore.getState().requests;
-      const allFieldsPresent = !requests.isEmpty() && requests.every(
-        item => mandatoryFields.every(
-          field => item.get(field) !== undefined
-        )
-      );
-
-      if (allFieldsPresent) {
-        clearInterval(interval);
-        clearTimeout(timer);
-        resolve();
-      }
-    }, WDA_DEFAULT_VERIFY_INTERVAL);
-
-    let timer = setTimeout(() => {
-      clearInterval(interval);
-      reject(new Error("Timed out while waiting for data"));
-    }, WDA_DEFAULT_GIVE_UP_TIMEOUT);
-  });
-}
-
 // A smart store watcher to notify store changes as necessary
 function storeWatcher(initialValue, reduceValue, onChange) {
   let currentValue = initialValue;
 
   return () => {
     const newValue = reduceValue();
     if (newValue !== currentValue) {
       onChange();
       currentValue = newValue;
     }
   };
 }
 
 /**
  * Preliminary setup for the NetMonitorView object.
  */
-NetMonitorView.Toolbar = new ToolbarView();
 NetMonitorView.Sidebar = new SidebarView();
 NetMonitorView.RequestsMenu = new RequestsMenuView();
 NetMonitorView.CustomRequest = new CustomRequestView();
-NetMonitorView.Statistics = new StatisticsView();
 
 exports.NetMonitorView = NetMonitorView;
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -14,17 +14,17 @@
           src="chrome://devtools/content/shared/theme-switching.js"/>
   <script type="text/javascript" src="netmonitor.js"/>
 
   <deck id="body"
         class="theme-sidebar"
         flex="1"
         data-localization-bundle="devtools/client/locales/netmonitor.properties">
 
-    <vbox id="network-inspector-view" flex="1">
+    <vbox id="inspector-panel" flex="1">
       <html:div xmlns="http://www.w3.org/1999/xhtml"
                 id="react-toolbar-hook"/>
       <hbox id="network-table-and-sidebar"
             class="devtools-responsive-container"
             flex="1">
         <html:div xmlns="http://www.w3.org/1999/xhtml"
                   id="network-table"
                   class="devtools-main-content">
@@ -96,25 +96,14 @@
           </vbox>
           <html:div xmlns="http://www.w3.org/1999/xhtml"
                     id="react-details-panel-hook"/>
         </deck>
       </hbox>
 
     </vbox>
 
-    <html:div id="network-statistics-view">
-      <html:div id="network-statistics-toolbar"
-                class="devtools-toolbar">
-        <html:div xmlns="http://www.w3.org/1999/xhtml"
-                  id="react-statistics-back-hook"/>
-      </html:div>
-      <html:div id="network-statistics-charts"
-                class="devtools-responsive-container">
-        <html:div id="primed-cache-chart"/>
-        <html:div id="network-statistics-view-splitter"
-                  class="split-box devtools-side-splitter"/>
-        <html:div id="empty-cache-chart"/>
-      </html:div>
+    <html:div xmlns="http://www.w3.org/1999/xhtml"
+              id="statistics-panel">
     </html:div>
   </deck>
 
 </window>
--- a/devtools/client/netmonitor/shared/components/properties-view.js
+++ b/devtools/client/netmonitor/shared/components/properties-view.js
@@ -107,17 +107,17 @@ const PropertiesView = createClass({
   renderValueWithRep(props) {
     const { member } = props;
 
     // Hide strings with following conditions
     // 1. this row is a togglable section
     // 2. the `value` object has a `value` property, only happend in Cookies panel
     // Put 2 here to not dup this method
     if (member.level === 0 ||
-      (typeof member.value === "object" && member.value.value)) {
+      (typeof member.value === "object" && member.value && member.value.value)) {
       return null;
     }
 
     return Rep(Object.assign(props, {
       // FIXME: A workaround for the issue in StringRep
       // Force StringRep to crop the text everytime
       member: Object.assign({}, member, { open: false }),
       mode: MODE.TINY,
deleted file mode 100644
--- a/devtools/client/netmonitor/statistics-view.js
+++ /dev/null
@@ -1,288 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* eslint-disable mozilla/reject-some-requires */
-/* globals $, window, document */
-
-"use strict";
-
-const { PluralForm } = require("devtools/shared/plural-form");
-const { Filters } = require("./filter-predicates");
-const { L10N } = require("./l10n");
-const { EVENTS } = require("./events");
-const { DOM } = require("devtools/client/shared/vendor/react");
-const { button } = DOM;
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
-const Actions = require("./actions/index");
-const { Chart } = require("devtools/client/shared/widgets/Chart");
-const {
-  getSizeWithDecimals,
-  getTimeWithDecimals
-} = require("./utils/format-utils");
-
-// px
-const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200;
-
-/**
- * Functions handling the performance statistics view.
- */
-function StatisticsView() {
-}
-
-StatisticsView.prototype = {
-  /**
-   * Initialization function, called when the statistics view is started.
-   */
-  initialize: function (store) {
-    this.store = store;
-    this.Chart = Chart;
-    this._backButton = $("#react-statistics-back-hook");
-
-    let backStr = L10N.getStr("netmonitor.backButton");
-    ReactDOM.render(button({
-      id: "network-statistics-back-button",
-      className: "devtools-toolbarbutton",
-      "data-text-only": "true",
-      title: backStr,
-      onClick: () => {
-        this.store.dispatch(Actions.openStatistics(false));
-      },
-    }, backStr), this._backButton);
-  },
-
-  /**
-    * Destruction function, called when the statistics view is closed.
-    */
-  destroy: function () {
-    ReactDOM.unmountComponentAtNode(this._backButton);
-  },
-
-  /**
-   * Initializes and displays empty charts in this container.
-   */
-  displayPlaceholderCharts: function () {
-    this._createChart({
-      id: "#primed-cache-chart",
-      title: "charts.cacheEnabled"
-    });
-    this._createChart({
-      id: "#empty-cache-chart",
-      title: "charts.cacheDisabled"
-    });
-    window.emit(EVENTS.PLACEHOLDER_CHARTS_DISPLAYED);
-  },
-
-  /**
-   * Populates and displays the primed cache chart in this container.
-   *
-   * @param array items
-   *        @see this._sanitizeChartDataSource
-   */
-  createPrimedCacheChart: function (items) {
-    this._createChart({
-      id: "#primed-cache-chart",
-      title: "charts.cacheEnabled",
-      data: this._sanitizeChartDataSource(items),
-      strings: this._commonChartStrings,
-      totals: this._commonChartTotals,
-      sorted: true
-    });
-    window.emit(EVENTS.PRIMED_CACHE_CHART_DISPLAYED);
-  },
-
-  /**
-   * Populates and displays the empty cache chart in this container.
-   *
-   * @param array items
-   *        @see this._sanitizeChartDataSource
-   */
-  createEmptyCacheChart: function (items) {
-    this._createChart({
-      id: "#empty-cache-chart",
-      title: "charts.cacheDisabled",
-      data: this._sanitizeChartDataSource(items, true),
-      strings: this._commonChartStrings,
-      totals: this._commonChartTotals,
-      sorted: true
-    });
-    window.emit(EVENTS.EMPTY_CACHE_CHART_DISPLAYED);
-  },
-
-  /**
-   * Common stringifier predicates used for items and totals in both the
-   * "primed" and "empty" cache charts.
-   */
-  _commonChartStrings: {
-    size: value => {
-      let string = getSizeWithDecimals(value / 1024);
-      return L10N.getFormatStr("charts.sizeKB", string);
-    },
-    time: value => {
-      let string = getTimeWithDecimals(value / 1000);
-      return L10N.getFormatStr("charts.totalS", string);
-    }
-  },
-  _commonChartTotals: {
-    size: total => {
-      let string = getSizeWithDecimals(total / 1024);
-      return L10N.getFormatStr("charts.totalSize", string);
-    },
-    time: total => {
-      let seconds = total / 1000;
-      let string = getTimeWithDecimals(seconds);
-      return PluralForm.get(seconds,
-        L10N.getStr("charts.totalSeconds")).replace("#1", string);
-    },
-    cached: total => {
-      return L10N.getFormatStr("charts.totalCached", total);
-    },
-    count: total => {
-      return L10N.getFormatStr("charts.totalCount", total);
-    }
-  },
-
-  /**
-   * Adds a specific chart to this container.
-   *
-   * @param object
-   *        An object containing all or some the following properties:
-   *          - id: either "#primed-cache-chart" or "#empty-cache-chart"
-   *          - title/data/strings/totals/sorted: @see Chart.js for details
-   */
-  _createChart: function ({ id, title, data, strings, totals, sorted }) {
-    let container = $(id);
-
-    // Nuke all existing charts of the specified type.
-    while (container.hasChildNodes()) {
-      container.firstChild.remove();
-    }
-
-    // Create a new chart.
-    let chart = this.Chart.PieTable(document, {
-      diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
-      title: L10N.getStr(title),
-      data: data,
-      strings: strings,
-      totals: totals,
-      sorted: sorted
-    });
-
-    chart.on("click", (_, item) => {
-      // Reset FilterButtons and enable one filter exclusively
-      this.store.dispatch(Actions.enableRequestFilterTypeOnly(item.label));
-      this.store.dispatch(Actions.openStatistics(false));
-    });
-
-    container.appendChild(chart.node);
-  },
-
-  /**
-   * Sanitizes the data source used for creating charts, to follow the
-   * data format spec defined in Chart.js.
-   *
-   * @param array items
-   *        A collection of request items used as the data source for the chart.
-   * @param boolean emptyCache
-   *        True if the cache is considered enabled, false for disabled.
-   */
-  _sanitizeChartDataSource: function (items, emptyCache) {
-    let data = [
-      "html", "css", "js", "xhr", "fonts", "images", "media", "flash", "ws", "other"
-    ].map(e => ({
-      cached: 0,
-      count: 0,
-      label: e,
-      size: 0,
-      time: 0
-    }));
-
-    for (let requestItem of items) {
-      let details = requestItem;
-      let type;
-
-      if (Filters.html(details)) {
-        // "html"
-        type = 0;
-      } else if (Filters.css(details)) {
-        // "css"
-        type = 1;
-      } else if (Filters.js(details)) {
-        // "js"
-        type = 2;
-      } else if (Filters.fonts(details)) {
-        // "fonts"
-        type = 4;
-      } else if (Filters.images(details)) {
-        // "images"
-        type = 5;
-      } else if (Filters.media(details)) {
-        // "media"
-        type = 6;
-      } else if (Filters.flash(details)) {
-        // "flash"
-        type = 7;
-      } else if (Filters.ws(details)) {
-        // "ws"
-        type = 8;
-      } else if (Filters.xhr(details)) {
-        // Verify XHR last, to categorize other mime types in their own blobs.
-        // "xhr"
-        type = 3;
-      } else {
-        // "other"
-        type = 9;
-      }
-
-      if (emptyCache || !responseIsFresh(details)) {
-        data[type].time += details.totalTime || 0;
-        data[type].size += details.contentSize || 0;
-      } else {
-        data[type].cached++;
-      }
-      data[type].count++;
-    }
-
-    return data.filter(e => e.count > 0);
-  },
-};
-
-/**
- * Checks if the "Expiration Calculations" defined in section 13.2.4 of the
- * "HTTP/1.1: Caching in HTTP" spec holds true for a collection of headers.
- *
- * @param object
- *        An object containing the { responseHeaders, status } properties.
- * @return boolean
- *         True if the response is fresh and loaded from cache.
- */
-function responseIsFresh({ responseHeaders, status }) {
-  // Check for a "304 Not Modified" status and response headers availability.
-  if (status != 304 || !responseHeaders) {
-    return false;
-  }
-
-  let list = responseHeaders.headers;
-  let cacheControl = list.find(e => e.name.toLowerCase() == "cache-control");
-  let expires = list.find(e => e.name.toLowerCase() == "expires");
-
-  // Check the "Cache-Control" header for a maximum age value.
-  if (cacheControl) {
-    let maxAgeMatch =
-      cacheControl.value.match(/s-maxage\s*=\s*(\d+)/) ||
-      cacheControl.value.match(/max-age\s*=\s*(\d+)/);
-
-    if (maxAgeMatch && maxAgeMatch.pop() > 0) {
-      return true;
-    }
-  }
-
-  // Check the "Expires" header for a valid date.
-  if (expires && Date.parse(expires.value)) {
-    return true;
-  }
-
-  return false;
-}
-
-exports.StatisticsView = StatisticsView;
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -12,16 +12,17 @@ support-files =
   html_cors-test-page.html
   html_custom-get-page.html
   html_cyrillic-test-page.html
   html_frame-test-page.html
   html_frame-subdocument.html
   html_filter-test-page.html
   html_infinite-get-page.html
   html_json-b64.html
+  html_json-basic.html
   html_json-custom-mime-test-page.html
   html_json-long-test-page.html
   html_json-malformed-test-page.html
   html_json-text-mime-test-page.html
   html_jsonp-test-page.html
   html_navigate-test-page.html
   html_params-test-page.html
   html_post-data-test-page.html
@@ -36,16 +37,17 @@ support-files =
   html_status-codes-test-page.html
   html_api-calls-test-page.html
   html_copy-as-curl.html
   html_curl-utils.html
   sjs_content-type-test-server.sjs
   sjs_cors-test-server.sjs
   sjs_https-redirect-test-server.sjs
   sjs_hsts-test-server.sjs
+  sjs_json-test-server.sjs
   sjs_simple-test-server.sjs
   sjs_sorting-test-server.sjs
   sjs_status-codes-test-server.sjs
   sjs_truncate-test-server.sjs
   test-image.png
   service-workers/status-codes.html
   service-workers/status-codes-service-worker.js
   !/devtools/client/framework/test/shared-head.js
@@ -102,16 +104,17 @@ skip-if = (os == 'linux' && debug && bit
 [browser_net_filter-02.js]
 [browser_net_filter-03.js]
 [browser_net_filter-04.js]
 [browser_net_footer-summary.js]
 [browser_net_html-preview.js]
 [browser_net_icon-preview.js]
 [browser_net_image-tooltip.js]
 [browser_net_json-b64.js]
+[browser_net_json-null.js]
 [browser_net_json-long.js]
 [browser_net_json-malformed.js]
 [browser_net_json_custom_mime.js]
 [browser_net_json_text_mime.js]
 [browser_net_jsonp.js]
 [browser_net_large-response.js]
 [browser_net_leak_on_tab_close.js]
 [browser_net_open_request_in_tab.js]
--- a/devtools/client/netmonitor/test/browser_net_charts-01.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-01.js
@@ -6,18 +6,18 @@
 /**
  * Makes sure Pie Charts have the right internal structure.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, NetMonitorView } = monitor.panelWin;
-  const { Chart } = NetMonitorView.Statistics;
+  let { document, windowRequire } = monitor.panelWin;
+  let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let pie = Chart.Pie(document, {
     width: 100,
     height: 100,
     data: [{
       size: 1,
       label: "foo"
     }, {
--- a/devtools/client/netmonitor/test/browser_net_charts-02.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-02.js
@@ -9,18 +9,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, NetMonitorView } = monitor.panelWin;
-  let { Chart } = NetMonitorView.Statistics;
+  let { document, windowRequire } = monitor.panelWin;
+  let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let pie = Chart.Pie(document, {
     data: null,
     width: 100,
     height: 100
   });
 
   let node = pie.node;
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -8,18 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, NetMonitorView } = monitor.panelWin;
-  let { Chart } = NetMonitorView.Statistics;
+  let { document, windowRequire } = monitor.panelWin;
+  let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
     title: "Table title",
     data: [{
       label1: 1,
       label2: 11.1
     }, {
       label1: 2,
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -9,18 +9,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, NetMonitorView } = monitor.panelWin;
-  let { Chart } = NetMonitorView.Statistics;
+  let { document, windowRequire } = monitor.panelWin;
+  let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
     title: "Table title",
     data: null,
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
     }
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -8,18 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, NetMonitorView } = monitor.panelWin;
-  let { Chart } = NetMonitorView.Statistics;
+  let { document, windowRequire } = monitor.panelWin;
+  let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let chart = Chart.PieTable(document, {
     title: "Table title",
     data: [{
       size: 1,
       label: 11.1
     }, {
       size: 2,
--- a/devtools/client/netmonitor/test/browser_net_charts-06.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-06.js
@@ -8,18 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, NetMonitorView } = monitor.panelWin;
-  let { Chart } = NetMonitorView.Statistics;
+  let { document, windowRequire } = monitor.panelWin;
+  let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let pie = Chart.Pie(document, {
     data: [],
     width: 100,
     height: 100
   });
 
   let node = pie.node;
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -8,18 +8,18 @@
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/l10n");
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
-  let { document, NetMonitorView } = monitor.panelWin;
-  let { Chart } = NetMonitorView.Statistics;
+  let { document, windowRequire } = monitor.panelWin;
+  let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
   let table = Chart.Table(document, {
     data: [],
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
     }
   });
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_json-null.js
@@ -0,0 +1,78 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { L10N } = require("devtools/client/netmonitor/l10n");
+
+/**
+ * Tests if JSON responses containing null values are properly displayed.
+ */
+
+add_task(function* () {
+  let { tab, monitor } = yield initNetMonitor(JSON_BASIC_URL + "?name=null");
+  info("Starting test... ");
+
+  let { document, NetMonitorView } = monitor.panelWin;
+  let { RequestsMenu } = NetMonitorView;
+
+  RequestsMenu.lazyUpdate = false;
+
+  let wait = waitForNetworkEvents(monitor, 1);
+  yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
+    content.wrappedJSObject.performRequests();
+  });
+  yield wait;
+
+  yield openResponsePanel(document);
+  checkResponsePanelDisplaysJSON(document);
+
+  let tabpanel = document.querySelector("#response-panel");
+  is(tabpanel.querySelectorAll(".tree-section").length, 1,
+    "There should be 1 tree sections displayed in this tabpanel.");
+  is(tabpanel.querySelectorAll(".treeRow:not(.tree-section)").length, 1,
+    "There should be 1 json properties displayed in this tabpanel.");
+  is(tabpanel.querySelectorAll(".empty-notice").length, 0,
+    "The empty notice should not be displayed in this tabpanel.");
+
+  let labels = tabpanel
+    .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
+  let values = tabpanel
+    .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
+
+  is(labels[0].textContent, "greeting", "The first json property name was incorrect.");
+  is(values[0].textContent, "null", "The first json property value was incorrect.");
+
+  yield teardown(monitor);
+});
+
+/**
+ * Helper to assert that the response panel found in the provided document is currently
+ * showing a preview of a JSON object.
+ */
+function checkResponsePanelDisplaysJSON(doc) {
+  let tabpanel = doc.querySelector("#response-panel");
+  is(tabpanel.querySelector(".response-error-header") === null, true,
+    "The response error header doesn't have the intended visibility.");
+  let jsonView = tabpanel.querySelector(".tree-section .treeLabel") || {};
+  is(jsonView.textContent === L10N.getStr("jsonScopeName"), true,
+    "The response json view has the intended visibility.");
+  is(tabpanel.querySelector(".editor-mount iframe") === null, true,
+    "The response editor doesn't have the intended visibility.");
+  is(tabpanel.querySelector(".response-image-box") === null, true,
+    "The response image box doesn't have the intended visibility.");
+}
+
+/**
+ * Open the netmonitor details panel and switch to the response tab.
+ * Returns a promise that will resolve when the response panel DOM element is available.
+ */
+function openResponsePanel(doc) {
+  let onReponsePanelReady = waitForDOM(doc, "#response-panel");
+  EventUtils.sendMouseEvent(
+    { type: "mousedown" },
+    doc.getElementById("details-pane-toggle")
+  );
+  doc.querySelector("#response-tab").click();
+  return onReponsePanelReady;
+}
--- a/devtools/client/netmonitor/test/browser_net_statistics-01.js
+++ b/devtools/client/netmonitor/test/browser_net_statistics-01.js
@@ -7,59 +7,50 @@
  * Tests if the statistics view is populated correctly.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(STATISTICS_URL);
   info("Starting test... ");
 
   let panel = monitor.panelWin;
-  let { $, $all, EVENTS, NetMonitorView, gStore, windowRequire } = panel;
+  let { document, gStore, windowRequire } = panel;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
 
-  is(NetMonitorView.currentFrontendMode, "network-inspector-view",
-   "The initial frontend mode is correct.");
+  let body = document.querySelector("#body");
 
-  is($("#primed-cache-chart").childNodes.length, 0,
-    "There should be no primed cache chart created yet.");
-  is($("#empty-cache-chart").childNodes.length, 0,
-    "There should be no empty cache chart created yet.");
-
-  let onChartDisplayed = Promise.all([
-    panel.once(EVENTS.PRIMED_CACHE_CHART_DISPLAYED),
-    panel.once(EVENTS.EMPTY_CACHE_CHART_DISPLAYED)
-  ]);
-  let onPlaceholderDisplayed = panel.once(EVENTS.PLACEHOLDER_CHARTS_DISPLAYED);
+  is(body.selectedPanel.id, "inspector-panel",
+    "The current main panel is correct.");
 
   info("Displaying statistics view");
   gStore.dispatch(Actions.openStatistics(true));
-  is(NetMonitorView.currentFrontendMode, "network-statistics-view",
-   "The current frontend mode is correct.");
+  is(body.selectedPanel.id, "statistics-panel",
+    "The current main panel is correct.");
 
   info("Waiting for placeholder to display");
-  yield onPlaceholderDisplayed;
-  is($("#primed-cache-chart").childNodes.length, 1,
+
+  is(document.querySelector(".primed-cache-chart").childNodes.length, 1,
     "There should be a placeholder primed cache chart created now.");
-  is($("#empty-cache-chart").childNodes.length, 1,
+  is(document.querySelector(".empty-cache-chart").childNodes.length, 1,
     "There should be a placeholder empty cache chart created now.");
 
-  is($all(".pie-chart-container[placeholder=true]").length, 2,
+  is(document.querySelectorAll(".pie-chart-container[placeholder=true]").length, 2,
     "Two placeholder pie chart appear to be rendered correctly.");
-  is($all(".table-chart-container[placeholder=true]").length, 2,
+  is(document.querySelectorAll(".table-chart-container[placeholder=true]").length, 2,
     "Two placeholder table chart appear to be rendered correctly.");
 
   info("Waiting for chart to display");
-  yield onChartDisplayed;
-  is($("#primed-cache-chart").childNodes.length, 1,
+
+  is(document.querySelector(".primed-cache-chart").childNodes.length, 1,
     "There should be a real primed cache chart created now.");
-  is($("#empty-cache-chart").childNodes.length, 1,
+  is(document.querySelector(".empty-cache-chart").childNodes.length, 1,
     "There should be a real empty cache chart created now.");
 
   yield waitUntil(
-    () => $all(".pie-chart-container:not([placeholder=true])").length == 2);
+    () => document.querySelectorAll(".pie-chart-container:not([placeholder=true])").length == 2);
   ok(true, "Two real pie charts appear to be rendered correctly.");
 
   yield waitUntil(
-    () => $all(".table-chart-container:not([placeholder=true])").length == 2);
+    () => document.querySelectorAll(".table-chart-container:not([placeholder=true])").length == 2);
   ok(true, "Two real table charts appear to be rendered correctly.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_statistics-02.js
+++ b/devtools/client/netmonitor/test/browser_net_statistics-02.js
@@ -8,39 +8,46 @@
  * the performance analysis view.
  */
 
 add_task(function* () {
   let { monitor } = yield initNetMonitor(FILTERING_URL);
   info("Starting test... ");
 
   let panel = monitor.panelWin;
-  let { $, $all, EVENTS, NetMonitorView, gStore, windowRequire } = panel;
+  let { document, gStore, windowRequire } = panel;
   let Actions = windowRequire("devtools/client/netmonitor/actions/index");
 
-  EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-html-button"));
-  EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-css-button"));
-  EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-js-button"));
-  EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-ws-button"));
-  EventUtils.sendMouseEvent({ type: "click" }, $("#requests-menu-filter-other-button"));
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#requests-menu-filter-html-button"));
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#requests-menu-filter-css-button"));
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#requests-menu-filter-js-button"));
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#requests-menu-filter-ws-button"));
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector("#requests-menu-filter-other-button"));
   testFilterButtonsCustom(monitor, [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1]);
   info("The correct filtering predicates are used before entering perf. analysis mode.");
 
-  let onEvents = promise.all([
-    panel.once(EVENTS.PRIMED_CACHE_CHART_DISPLAYED),
-    panel.once(EVENTS.EMPTY_CACHE_CHART_DISPLAYED)
-  ]);
   gStore.dispatch(Actions.openStatistics(true));
-  yield onEvents;
+
+  let body = document.querySelector("#body");
+
+  is(body.selectedPanel.id, "statistics-panel",
+    "The main panel is switched to the statistics panel.");
 
-  is(NetMonitorView.currentFrontendMode, "network-statistics-view",
-    "The frontend mode is switched to the statistics view.");
+  yield waitUntil(
+    () => document.querySelectorAll(".pie-chart-container:not([placeholder=true])").length == 2);
+  ok(true, "Two real pie charts appear to be rendered correctly.");
 
-  EventUtils.sendMouseEvent({ type: "click" }, $(".pie-chart-slice"));
+  EventUtils.sendMouseEvent({ type: "click" },
+    document.querySelector(".pie-chart-slice"));
 
-  is(NetMonitorView.currentFrontendMode, "network-inspector-view",
-    "The frontend mode is switched back to the inspector view.");
+  is(body.selectedPanel.id, "inspector-panel",
+    "The main panel is switched back to the inspector panel.");
 
   testFilterButtons(monitor, "html");
   info("The correct filtering predicate is used when exiting perf. analysis mode.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -35,16 +35,17 @@ const POST_RAW_URL = EXAMPLE_URL + "html
 const POST_RAW_WITH_HEADERS_URL = EXAMPLE_URL + "html_post-raw-with-headers-test-page.html";
 const PARAMS_URL = EXAMPLE_URL + "html_params-test-page.html";
 const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
 const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
 const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html";
 const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html";
 const JSON_TEXT_MIME_URL = EXAMPLE_URL + "html_json-text-mime-test-page.html";
 const JSON_B64_URL = EXAMPLE_URL + "html_json-b64.html";
+const JSON_BASIC_URL = EXAMPLE_URL + "html_json-basic.html";
 const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
 const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html";
 const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";
 const CUSTOM_GET_URL = EXAMPLE_URL + "html_custom-get-page.html";
 const SINGLE_GET_URL = EXAMPLE_URL + "html_single-get-page.html";
 const STATISTICS_URL = EXAMPLE_URL + "html_statistics-test-page.html";
 const CURL_URL = EXAMPLE_URL + "html_copy-as-curl.html";
 const CURL_UTILS_URL = EXAMPLE_URL + "html_curl-utils.html";
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_json-basic.html
@@ -0,0 +1,40 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+    <meta http-equiv="Pragma" content="no-cache" />
+    <meta http-equiv="Expires" content="0" />
+    <title>Network Monitor test page</title>
+  </head>
+
+  <body>
+    <p>JSON request test page</p>
+    <p>Pass the JSON name (as supported by sjs_json-test-server.sjs) as a query parameter</p>
+
+    <script type="text/javascript">
+      function get(aAddress, aCallback) {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", aAddress, true);
+
+        xhr.onreadystatechange = function() {
+          if (this.readyState == this.DONE) {
+            aCallback();
+          }
+        };
+        xhr.send(null);
+      }
+
+      function performRequests() {
+        // Forward the query parameter for this page to sjs_json-test-server
+        get("sjs_json-test-server.sjs" + window.location.search, function() {
+          // Done.
+        });
+      }
+    </script>
+  </body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/sjs_json-test-server.sjs
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response) {
+
+  response.setStatusLine(request.httpVersion, 200, "OK");
+  response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  response.setHeader("Pragma", "no-cache");
+  response.setHeader("Expires", "0");
+
+  response.setHeader("Content-Type", "application/json; charset=utf-8", false);
+
+  // This server checks the name parameter from the request to decide which JSON object to
+  // return.
+  let params = request.queryString.split("&");
+  let name = (params.filter((s) => s.includes("name="))[0] || "").split("=")[1];
+  switch (name) {
+    case "null":
+      response.write("{ \"greeting\": null }");
+      break;
+  }
+}
deleted file mode 100644
--- a/devtools/client/netmonitor/toolbar-view.js
+++ /dev/null
@@ -1,47 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* globals dumpn, $ */
-
-"use strict";
-
-const { createFactory } = require("devtools/client/shared/vendor/react");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
-const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
-const Toolbar = createFactory(require("./components/toolbar"));
-
-/**
- * Functions handling the toolbar view: expand/collapse button etc.
- */
-function ToolbarView() {
-  dumpn("ToolbarView was instantiated");
-}
-
-ToolbarView.prototype = {
-  /**
-   * Initialization function, called when the network monitor is started.
-   */
-  initialize: function (store) {
-    dumpn("Initializing the ToolbarView");
-
-    this._toolbarNode = $("#react-toolbar-hook");
-
-    ReactDOM.render(Provider(
-      { store },
-      Toolbar()
-    ), this._toolbarNode);
-  },
-
-  /**
-   * Destruction function, called when the network monitor is closed.
-   */
-  destroy: function () {
-    dumpn("Destroying the ToolbarView");
-
-    ReactDOM.unmountComponentAtNode(this._toolbarNode);
-  }
-
-};
-
-exports.ToolbarView = ToolbarView;
--- a/devtools/client/shared/test/browser_html_tooltip_hover.js
+++ b/devtools/client/shared/test/browser_html_tooltip_hover.js
@@ -23,16 +23,18 @@ const TEST_URI = `data:text/xml;charset=
     </vbox>
   </window>`;
 
 const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 loadHelperScript("helper_html_tooltip.js");
 
 add_task(function* () {
   let [,, doc] = yield createHost("bottom", TEST_URI);
+  // Wait for full page load before synthesizing events on the page.
+  yield waitUntil(() => doc.readyState === "complete");
 
   let width = 100, height = 50;
   let tooltipContent = doc.createElementNS(HTML_NS, "div");
   tooltipContent.textContent = "tooltip";
   let tooltip = new HTMLTooltip(doc, {useXulWrapper: false});
   tooltip.setContent(tooltipContent, {width, height});
 
   let container = doc.getElementById("container");
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -102,23 +102,16 @@
   --timing-send-color: rgba(70, 175, 227, 0.8); /* light blue */
   --timing-wait-color: rgba(94, 136, 176, 0.8); /* blue grey */
   --timing-receive-color: rgba(112, 191, 83, 0.8); /* green */
 
   --sort-ascending-image: url(chrome://devtools/skin/images/firebug/arrow-up.svg);
   --sort-descending-image: url(chrome://devtools/skin/images/firebug/arrow-down.svg);
 }
 
-#network-table {
-  display: -moz-box;
-  -moz-box-orient: vertical;
-  -moz-box-flex: 1;
-  overflow: hidden;
-}
-
 .request-list-container {
   display: -moz-box;
   -moz-box-orient: vertical;
   -moz-box-flex: 1;
 }
 
 .request-list-empty-notice {
   margin: 0;
@@ -840,57 +833,55 @@
 
 #requests-menu-network-summary-button:hover > .summary-info-icon,
 #requests-menu-network-summary-button:hover > .summary-info-text {
   opacity: 1;
 }
 
 /* Performance analysis view */
 
-#network-statistics-view {
-  display: -moz-box;
+.statistics-panel {
+  display: flex;
+  height: 100vh;
 }
 
-#network-statistics-toolbar {
-  border: none;
-  margin: 0;
-  padding: 0;
-}
-
-#network-statistics-back-button {
+.statistics-panel .devtools-toolbarbutton.back-button {
   min-width: 4em;
-  min-height: 100vh;
   margin: 0;
   padding: 0;
   border-radius: 0;
   border-top: none;
   border-bottom: none;
   border-inline-start: none;
 }
 
-#network-statistics-view-splitter {
+.statistics-panel .splitter {
   border-color: rgba(0,0,0,0.2);
   cursor: default;
   pointer-events: none;
+  height: 100vh;
 }
 
-#network-statistics-charts {
-  min-height: 1px;
+.statistics-panel .charts-container {
+  display: flex;
+  width: 100%;
 }
 
-#network-statistics-charts {
-  background-color: var(--theme-sidebar-background);
+.statistics-panel .charts,
+.statistics-panel .pie-table-chart-container {
+  width: 100%;
+  height: 100%;
 }
 
-#network-statistics-charts .pie-chart-container {
+.statistics-panel .pie-chart-container {
   margin-inline-start: 3vw;
   margin-inline-end: 1vw;
 }
 
-#network-statistics-charts .table-chart-container {
+.statistics-panel .table-chart-container {
   margin-inline-start: 1vw;
   margin-inline-end: 3vw;
 }
 
 .chart-colored-blob[name=html] {
   fill: var(--theme-highlight-bluegrey);
   background: var(--theme-highlight-bluegrey);
 }
@@ -1045,16 +1036,25 @@
   .requests-menu-size {
     max-width: none;
     width: 10vw;
   }
 
   .requests-menu-waterfall {
     display: none;
   }
+
+  .statistics-panel .charts-container {
+    flex-direction: column;
+  }
+
+  .statistics-panel .splitter {
+    width: 100vw;
+    height: 0;
+  }
 }
 
 /* Platform overrides (copied in from the old platform specific files) */
 :root[platform="win"] .requests-menu-header-button > .button-box {
   padding: 0;
 }
 
 :root[platform="win"] .requests-menu-timings-division {
@@ -1285,20 +1285,26 @@
   width: 100%;
   height: 100%;
 }
 
 /*
  * FIXME: normal html block element cannot fill outer XUL element
  * This workaround should be removed after netmonitor is migrated to react
  */
+#network-table {
+  display: -moz-box;
+  -moz-box-orient: vertical;
+  -moz-box-flex: 1;
+  overflow: hidden;
+}
 
+#statistics-panel,
 #react-details-panel-hook {
   display: flex;
   flex-direction: column;
 }
 
-#network-statistics-charts,
 #primed-cache-chart,
 #empty-cache-chart {
   display: -moz-box;
   -moz-box-flex: 1;
 }
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -361,18 +361,18 @@
 .ruleview-computed {
   margin-inline-start: 35px;
 }
 
 .ruleview-grid,
 .ruleview-swatch {
   cursor: pointer;
   border-radius: 50%;
-  width: 0.9em;
-  height: 0.9em;
+  width: 1em;
+  height: 1em;
   vertical-align: middle;
   /* align the swatch with its value */
   margin-top: -1px;
   margin-inline-end: 5px;
   display: inline-block;
   position: relative;
 }
 
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -86,16 +86,17 @@ support-files =
 [test_eval-01.js]
 [test_eval-02.js]
 [test_eval-03.js]
 [test_eval-04.js]
 [test_eval-05.js]
 [test_promises_actor_attach.js]
 [test_promises_actor_exist.js]
 [test_promises_actor_list_promises.js]
+skip-if = coverage # bug 1336670
 [test_promises_actor_onnewpromise.js]
 [test_promises_actor_onpromisesettled.js]
 [test_promises_client_getdependentpromises.js]
 [test_promises_object_creationtimestamp.js]
 [test_promises_object_timetosettle-01.js]
 [test_promises_object_timetosettle-02.js]
 [test_protocol_abort.js]
 [test_protocol_async.js]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3589,33 +3589,31 @@ nsDocShell::CanAccessItem(nsIDocShellTre
   OriginAttributes accessingOA =
     static_cast<nsDocShell*>(accessingDS.get())->GetOriginAttributes();
 
   // When the first party isolation is on, the top-level docShell may not have
   // the firstPartyDomain in its originAttributes, but its document will have
   // it. So we get the firstPartyDomain from the nodePrincipal of the document
   // before we compare the originAttributes.
   if (OriginAttributes::IsFirstPartyEnabled()) {
-    if (accessingDS == accessingRootDS &&
-        aAccessingItem->ItemType() == nsIDocShellTreeItem::typeContent &&
-        !accessingDS->GetIsMozBrowser()) {
+    if (aAccessingItem->ItemType() == nsIDocShellTreeItem::typeContent &&
+        (accessingDS == accessingRootDS || accessingDS->GetIsMozBrowser())) {
 
       nsCOMPtr<nsIDocument> accessingDoc = aAccessingItem->GetDocument();
 
       if (accessingDoc) {
         nsCOMPtr<nsIPrincipal> accessingPrincipal = accessingDoc->NodePrincipal();
 
         accessingOA.mFirstPartyDomain =
           accessingPrincipal->OriginAttributesRef().mFirstPartyDomain;
       }
     }
 
-    if (targetDS == targetRootDS &&
-        aTargetItem->ItemType() == nsIDocShellTreeItem::typeContent &&
-        !targetDS->GetIsMozBrowser()) {
+    if (aTargetItem->ItemType() == nsIDocShellTreeItem::typeContent &&
+        (targetDS == targetRootDS || targetDS->GetIsMozBrowser())) {
 
       nsCOMPtr<nsIDocument> targetDoc = aAccessingItem->GetDocument();
 
       if (targetDoc) {
         nsCOMPtr<nsIPrincipal> targetPrincipal = targetDoc->NodePrincipal();
 
         targetOA.mFirstPartyDomain =
           targetPrincipal->OriginAttributesRef().mFirstPartyDomain;
@@ -7937,17 +7935,19 @@ nsDocShell::EndPageLoad(nsIWebProgress* 
       // out which error to return. Or we can fix up the error here.
       if (!(mLoadType & LOAD_CMD_HISTORY)) {
         aStatus = NS_ERROR_OFFLINE;
       }
       DisplayLoadError(aStatus, url, nullptr, aChannel);
     }
   } else if (url && NS_SUCCEEDED(aStatus)) {
     // If we have a host
-    mozilla::net::PredictorLearnRedirect(url, aChannel, this);
+    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+    mozilla::net::PredictorLearnRedirect(url, aChannel,
+                                         loadInfo->GetOriginAttributes());
   }
 
   return NS_OK;
 }
 
 //*****************************************************************************
 // nsDocShell: Content Viewer Management
 //*****************************************************************************
@@ -9868,16 +9868,17 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   if (IsFrame() && !isTargetTopLevelDocShell) {
     nsCOMPtr<Element> requestingElement =
       mScriptGlobal->AsOuter()->GetFrameElementInternal();
     NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
     contentType = requestingElement->IsHTMLElement(nsGkAtoms::iframe) ?
       nsIContentPolicy::TYPE_INTERNAL_IFRAME : nsIContentPolicy::TYPE_INTERNAL_FRAME;
   } else {
     contentType = nsIContentPolicy::TYPE_DOCUMENT;
+    isTargetTopLevelDocShell = true;
   }
 
   // If there's no targetDocShell, that means we are about to create a new window,
   // perform a content policy check before creating the window.
   if (!targetDocShell) {
     nsCOMPtr<Element> requestingElement;
     nsISupports* requestingContext = nullptr;
 
@@ -10723,20 +10724,27 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
   nsAutoString srcdoc;
   if (aFlags & INTERNAL_LOAD_FLAGS_IS_SRCDOC) {
     srcdoc = aSrcdoc;
   } else {
     srcdoc = NullString();
   }
 
+  OriginAttributes attrs;
+  bool isTopLevelDoc = mItemType == typeContent &&
+                       (isTargetTopLevelDocShell ||
+                        GetIsMozBrowser());
+  attrs.Inherit(GetOriginAttributes());
+  attrs.SetFirstPartyDomain(isTopLevelDoc, aURI);
+
   net::PredictorLearn(aURI, nullptr,
-                      nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, this);
+                      nsINetworkPredictor::LEARN_LOAD_TOPLEVEL, attrs);
   net::PredictorPredict(aURI, nullptr,
-                        nsINetworkPredictor::PREDICT_LOAD, this, nullptr);
+                        nsINetworkPredictor::PREDICT_LOAD, attrs, nullptr);
 
   nsCOMPtr<nsIRequest> req;
   rv = DoURILoad(aURI, aOriginalURI, aLoadReplace, aReferrer,
                  !(aFlags & INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER),
                  aReferrerPolicy,
                  aTriggeringPrincipal, principalToInherit, aTypeHint,
                  aFileName, aPostData, aHeadersData,
                  aFirstParty, aDocShell, getter_AddRefs(req),
@@ -10958,19 +10966,19 @@ nsDocShell::DoURILoad(nsIURI* aURI,
 
   if (aPrincipalToInherit) {
     loadInfo->SetPrincipalToInherit(aPrincipalToInherit);
   }
 
   // We have to do this in case our OriginAttributes are different from the
   // OriginAttributes of the parent document. Or in case there isn't a
   // parent document.
-  bool isTopLevelDoc = aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT &&
-                       mItemType == typeContent &&
-                       !GetIsMozBrowser();
+  bool isTopLevelDoc = mItemType == typeContent &&
+                       (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
+                        GetIsMozBrowser());
 
   OriginAttributes attrs;
   attrs.Inherit(GetOriginAttributes());
   attrs.SetFirstPartyDomain(isTopLevelDoc, aURI);
   rv = loadInfo->SetOriginAttributes(attrs);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -14188,19 +14196,22 @@ nsDocShell::OnOverLink(nsIContent* aCont
   nsAutoCString spec;
   rv = aURI->GetSpec(spec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoString uStr;
   rv = textToSubURI->UnEscapeURIForUI(charset, spec, uStr);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  OriginAttributes attrs;
+  attrs.Inherit(aContent->NodePrincipal()->OriginAttributesRef());
+
   mozilla::net::PredictorPredict(aURI, mCurrentURI,
                                  nsINetworkPredictor::PREDICT_LINK,
-                                 this, nullptr);
+                                 attrs, nullptr);
 
   if (browserChrome2) {
     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
     rv = browserChrome2->SetStatusWithContext(nsIWebBrowserChrome::STATUS_LINK,
                                               uStr, element);
   } else {
     rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, uStr.get());
   }
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -1054,16 +1054,39 @@ AudioChannelService::RefreshAgentsAudioF
     AudioChannelWindow* winData = iter.GetNext();
     if (winData->mOwningAudioFocus) {
       winData->AudioFocusChanged(aAgent, aActive);
     }
   }
 }
 
 void
+AudioChannelService::NotifyMediaResumedFromBlock(nsPIDOMWindowOuter* aWindow)
+{
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aWindow->IsOuterWindow());
+
+  nsCOMPtr<nsPIDOMWindowOuter> topWindow = aWindow->GetScriptableTop();
+  if (!topWindow) {
+    return;
+  }
+
+  AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
+  if (!winData) {
+    return;
+  }
+
+  if (!winData->mShouldSendBlockStopEvent) {
+    return;
+  }
+
+  winData->NotifyMediaBlockStop(aWindow);
+}
+
+void
 AudioChannelService::AudioChannelWindow::RequestAudioFocus(AudioChannelAgent* aAgent)
 {
   MOZ_ASSERT(aAgent);
 
   // Don't need to check audio focus for window-less agent.
   if (!aAgent->Window()) {
     return;
   }
@@ -1259,16 +1282,36 @@ AudioChannelService::AudioChannelWindow:
   RemoveAgentAndReduceAgentsNum(aAgent);
   AudioCapturedChanged(aAgent, AudioCaptureState::eNotCapturing);
   AudioAudibleChanged(aAgent,
                       AudibleState::eNotAudible,
                       AudibleChangedReasons::ePauseStateChanged);
 }
 
 void
+AudioChannelService::AudioChannelWindow::NotifyMediaBlockStop(nsPIDOMWindowOuter* aWindow)
+{
+  mShouldSendBlockStopEvent = false;
+  // Can't use raw pointer for lamba variable capturing, use smart ptr.
+  nsCOMPtr<nsPIDOMWindowOuter> window = aWindow;
+  NS_DispatchToCurrentThread(NS_NewRunnableFunction([window] () -> void {
+      nsCOMPtr<nsIObserverService> observerService =
+        services::GetObserverService();
+      if (NS_WARN_IF(!observerService)) {
+        return;
+      }
+
+      observerService->NotifyObservers(ToSupports(window),
+                                       "audio-playback",
+                                       u"blockStop");
+    })
+  );
+}
+
+void
 AudioChannelService::AudioChannelWindow::AppendAgentAndIncreaseAgentsNum(AudioChannelAgent* aAgent)
 {
   MOZ_ASSERT(aAgent);
   MOZ_ASSERT(!mAgents.Contains(aAgent));
 
   int32_t channel = aAgent->AudioChannelType();
   mAgents.AppendElement(aAgent);
 
@@ -1322,17 +1365,17 @@ AudioChannelService::AudioChannelWindow:
   if (aAudible == AudibleState::eAudible) {
     AppendAudibleAgentIfNotContained(aAgent, aReason);
   } else {
     RemoveAudibleAgentIfContained(aAgent, aReason);
   }
 
   NotifyAudioCompetingChanged(aAgent, aAudible == AudibleState::eAudible);
   if (aAudible != AudibleState::eNotAudible) {
-    MaybeNotifyMediaBlocked(aAgent);
+    MaybeNotifyMediaBlockStart(aAgent);
   }
 }
 
 void
 AudioChannelService::AudioChannelWindow::AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
                                                                           AudibleChangedReasons aReason)
 {
   MOZ_ASSERT(aAgent);
@@ -1399,17 +1442,17 @@ AudioChannelService::AudioChannelWindow:
 {
   RefPtr<NotifyChannelActiveRunnable> runnable =
     new NotifyChannelActiveRunnable(aWindowID, aChannel, aActive);
   DebugOnly<nsresult> rv = NS_DispatchToCurrentThread(runnable);
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToCurrentThread failed");
 }
 
 void
-AudioChannelService::AudioChannelWindow::MaybeNotifyMediaBlocked(AudioChannelAgent* aAgent)
+AudioChannelService::AudioChannelWindow::MaybeNotifyMediaBlockStart(AudioChannelAgent* aAgent)
 {
   nsCOMPtr<nsPIDOMWindowOuter> window = aAgent->Window();
   if (!window) {
     return;
   }
 
   MOZ_ASSERT(window->IsOuterWindow());
   nsCOMPtr<nsPIDOMWindowInner> inner = window->GetCurrentInnerWindow();
@@ -1422,21 +1465,24 @@ AudioChannelService::AudioChannelWindow:
     return;
   }
 
   if (window->GetMediaSuspend() != nsISuspendedTypes::SUSPENDED_BLOCK ||
       !doc->Hidden()) {
     return;
   }
 
-  NS_DispatchToCurrentThread(NS_NewRunnableFunction([window] () -> void {
-      nsCOMPtr<nsIObserverService> observerService =
-        services::GetObserverService();
-      if (NS_WARN_IF(!observerService)) {
-        return;
-      }
+  if (!mShouldSendBlockStopEvent) {
+      mShouldSendBlockStopEvent = true;
+      NS_DispatchToCurrentThread(NS_NewRunnableFunction([window] () -> void {
+        nsCOMPtr<nsIObserverService> observerService =
+          services::GetObserverService();
+        if (NS_WARN_IF(!observerService)) {
+          return;
+        }
 
-      observerService->NotifyObservers(ToSupports(window),
-                                       "audio-playback",
-                                       u"block");
-    })
-  );
+        observerService->NotifyObservers(ToSupports(window),
+                                         "audio-playback",
+                                         u"blockStart");
+      })
+    );
+  }
 }
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -196,16 +196,18 @@ public:
 
   void Notify(uint64_t aWindowID);
 
   void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel,
                            bool aContentOrNormalChannel, bool aAnyChannel);
 
   void NotifyCreatedNewAgent(AudioChannelAgent* aAgent);
 
+  void NotifyMediaResumedFromBlock(nsPIDOMWindowOuter* aWindow);
+
 private:
   AudioChannelService();
   ~AudioChannelService();
 
   void RefreshAgents(nsPIDOMWindowOuter* aWindow,
                      std::function<void(AudioChannelAgent*)> aFunc);
 
   static void CreateServiceIfNeeded();
@@ -240,41 +242,47 @@ private:
 
   class AudioChannelWindow final
   {
   public:
     explicit AudioChannelWindow(uint64_t aWindowID)
       : mWindowID(aWindowID)
       , mIsAudioCaptured(false)
       , mOwningAudioFocus(!AudioChannelService::IsEnableAudioCompeting())
+      , mShouldSendBlockStopEvent(false)
     {
       // 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,
                              AudibleChangedReasons aReason);
 
     void AppendAgent(AudioChannelAgent* aAgent, AudibleState aAudible);
     void RemoveAgent(AudioChannelAgent* aAgent);
 
+    void NotifyMediaBlockStop(nsPIDOMWindowOuter* aWindow);
+
     uint64_t mWindowID;
     bool mIsAudioCaptured;
     AudioChannelConfig mChannels[NUMBER_OF_AUDIO_CHANNELS];
 
     // Raw pointer because the AudioChannelAgent must unregister itself.
     nsTObserverArray<AudioChannelAgent*> mAgents;
     nsTObserverArray<AudioChannelAgent*> mAudibleAgents;
 
     // Owning audio focus when the window starts playing audible sound, and
     // lose audio focus when other windows starts playing.
     bool mOwningAudioFocus;
 
+    // If we've dispatched "blockStart" event, we must dispatch another event
+    // "blockStop" when the window is resumed from suspend-block.
+    bool mShouldSendBlockStopEvent;
   private:
     void AudioCapturedChanged(AudioChannelAgent* aAgent,
                               AudioCaptureState aCapture);
 
     void AppendAudibleAgentIfNotContained(AudioChannelAgent* aAgent,
                                           AudibleChangedReasons aReason);
     void RemoveAudibleAgentIfContained(AudioChannelAgent* aAgent,
                                        AudibleChangedReasons aReason);
@@ -286,17 +294,17 @@ private:
     bool IsLastAudibleAgent() const;
 
     void NotifyAudioAudibleChanged(nsPIDOMWindowOuter* aWindow,
                                    AudibleState aAudible,
                                    AudibleChangedReasons aReason);
 
     void NotifyChannelActive(uint64_t aWindowID, AudioChannel aChannel,
                              bool aActive);
-    void MaybeNotifyMediaBlocked(AudioChannelAgent* aAgent);
+    void MaybeNotifyMediaBlockStart(AudioChannelAgent* aAgent);
 
     void RequestAudioFocus(AudioChannelAgent* aAgent);
     void NotifyAudioCompetingChanged(AudioChannelAgent* aAgent, bool aActive);
 
     uint32_t GetCompetingBehavior(AudioChannelAgent* aAgent,
                                   int32_t aIncomingChannelType,
                                   bool aIncomingChannelActive) const;
     bool IsAgentInvolvingInAudioCompeting(AudioChannelAgent* aAgent) const;
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -194,10 +194,21 @@ ChromeUtils::IsOriginAttributesEqualIgno
                                                     const dom::OriginAttributesDictionary& aB)
 {
   return aA.mAppId == aB.mAppId &&
          aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
          aA.mUserContextId == aB.mUserContextId &&
          aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
 }
 
+/* static */ bool
+ChromeUtils::IsOriginAttributesEqualIgnoringFPD(const dom::OriginAttributesDictionary& aA,
+                                                const dom::OriginAttributesDictionary& aB)
+{
+  return aA.mAddonId == aB.mAddonId &&
+         aA.mAppId == aB.mAppId &&
+         aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
+         aA.mUserContextId == aB.mUserContextId &&
+         aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -90,14 +90,18 @@ public:
 
   static bool
   IsOriginAttributesEqual(const dom::OriginAttributesDictionary& aA,
                           const dom::OriginAttributesDictionary& aB);
 
   static bool
   IsOriginAttributesEqualIgnoringAddonId(const dom::OriginAttributesDictionary& aA,
                                          const dom::OriginAttributesDictionary& aB);
+
+  static bool
+  IsOriginAttributesEqualIgnoringFPD(const dom::OriginAttributesDictionary& aA,
+                                     const dom::OriginAttributesDictionary& aB);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ChromeUtils__
--- a/dom/base/DOMParser.cpp
+++ b/dom/base/DOMParser.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/ScriptSettings.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 DOMParser::DOMParser()
   : mAttemptedInit(false)
+  , mOriginalPrincipalWasSystem(false)
 {
 }
 
 DOMParser::~DOMParser()
 {
 }
 
 // QueryInterface implementation for DOMParser
@@ -87,28 +88,22 @@ DOMParser::ParseFromString(const nsAStri
   nsresult rv;
 
   if (!nsCRT::strcmp(contentType, "text/html")) {
     nsCOMPtr<nsIDOMDocument> domDocument;
     rv = SetUpDocument(DocumentFlavorHTML, getter_AddRefs(domDocument));
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIDocument> document = do_QueryInterface(domDocument);
 
-    // Keep the XULXBL state, base URL and principal setting in sync with the
-    // XML case
+    // Keep the XULXBL state in sync with the XML case.
 
-    if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal)) {
+    if (mOriginalPrincipalWasSystem) {
       document->ForceEnableXULXBL();
     }
 
-    // Make sure to give this document the right base URI
-    document->SetBaseURI(mBaseURI);
-    // And the right principal
-    document->SetPrincipal(mPrincipal);
-
     rv = nsContentUtils::ParseDocumentHTML(str, document, false);
     NS_ENSURE_SUCCESS(rv, rv);
 
     domDocument.forget(aResult);
     return rv;
   }
 
   nsAutoCString utf8str;
@@ -240,54 +235,46 @@ DOMParser::ParseFromStream(nsIInputStrea
                      getter_AddRefs(domDocument));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Create a fake channel 
   nsCOMPtr<nsIChannel> parserChannel;
   NS_NewInputStreamChannel(getter_AddRefs(parserChannel),
                            mDocumentURI,
                            nullptr, // aStream
-                           mOriginalPrincipal,
+                           mPrincipal,
                            nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL,
                            nsIContentPolicy::TYPE_OTHER,
                            nsDependentCString(contentType));
   NS_ENSURE_STATE(parserChannel);
 
   if (charset) {
     parserChannel->SetContentCharset(nsDependentCString(charset));
   }
 
   // Tell the document to start loading
   nsCOMPtr<nsIStreamListener> listener;
 
   // Have to pass false for reset here, else the reset will remove
   // our event listener.  Should that listener addition move to later
-  // than this call?  Then we wouldn't need to mess around with
-  // SetPrincipal, etc, probably!
+  // than this call?
   nsCOMPtr<nsIDocument> document(do_QueryInterface(domDocument));
   if (!document) return NS_ERROR_FAILURE;
 
-  // Keep the XULXBL state, base URL and principal setting in sync with the
-  // HTML case
+  // Keep the XULXBL state in sync with the HTML case
 
-  if (nsContentUtils::IsSystemPrincipal(mOriginalPrincipal)) {
+  if (mOriginalPrincipalWasSystem) {
     document->ForceEnableXULXBL();
   }
 
   rv = document->StartDocumentLoad(kLoadAsData, parserChannel, 
                                    nullptr, nullptr, 
                                    getter_AddRefs(listener),
                                    false);
 
-  // Make sure to give this document the right base URI
-  document->SetBaseURI(mBaseURI);
-
-  // And the right principal
-  document->SetPrincipal(mPrincipal);
-
   if (NS_FAILED(rv) || !listener) {
     return NS_ERROR_FAILURE;
   }
 
   // Now start pumping data to the listener
   nsresult status;
 
   rv = listener->OnStartRequest(parserChannel, nullptr);
@@ -348,38 +335,33 @@ DOMParser::Init(nsIPrincipal* principal,
                                     "ChromeScriptedDOMParserWithoutPrincipal",
                                     nullptr,
                                     0,
                                     documentURI);
 
     OriginAttributes attrs;
     mPrincipal = BasePrincipal::CreateCodebasePrincipal(mDocumentURI, attrs);
     NS_ENSURE_TRUE(mPrincipal, NS_ERROR_FAILURE);
-    mOriginalPrincipal = mPrincipal;
   } else {
-    mOriginalPrincipal = mPrincipal;
     if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
       // Don't give DOMParsers the system principal.  Use a null
       // principal instead.
+      mOriginalPrincipalWasSystem = true;
       mPrincipal = nsNullPrincipal::Create();
 
       if (!mDocumentURI) {
         rv = mPrincipal->GetURI(getter_AddRefs(mDocumentURI));
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
   }
-  
+
   mBaseURI = baseURI;
-  // Note: if mBaseURI is null, fine.  Leave it like that; that will use the
-  // documentURI as the base.  Otherwise for null principals we'll get
-  // nsDocument::SetBaseURI giving errors.
 
   NS_POSTCONDITION(mPrincipal, "Must have principal");
-  NS_POSTCONDITION(mOriginalPrincipal, "Must have original principal");
   NS_POSTCONDITION(mDocumentURI, "Must have document URI");
   return NS_OK;
 }
 
 /*static */already_AddRefed<DOMParser>
 DOMParser::Constructor(const GlobalObject& aOwner,
                        nsIPrincipal* aPrincipal, nsIURI* aDocumentURI,
                        nsIURI* aBaseURI, ErrorResult& rv)
@@ -477,19 +459,15 @@ DOMParser::SetUpDocument(DocumentFlavor 
     nsCOMPtr<nsIPrincipal> prin = nsNullPrincipal::Create();
     rv = Init(prin, nullptr, nullptr, scriptHandlingObject);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ASSERTION(mPrincipal, "Must have principal by now");
   NS_ASSERTION(mDocumentURI, "Must have document URI by now");
 
-  // Here we have to cheat a little bit...  Setting the base URI won't
-  // work if the document has a null principal, so use
-  // mOriginalPrincipal when creating the document, then reset the
-  // principal.
   return NS_NewDOMDocument(aResult, EmptyString(), EmptyString(), nullptr,
                            mDocumentURI, mBaseURI,
-                           mOriginalPrincipal,
+                           mPrincipal,
                            true,
                            scriptHandlingObject,
                            aFlavor);
 }
--- a/dom/base/DOMParser.h
+++ b/dom/base/DOMParser.h
@@ -107,20 +107,20 @@ private:
     }
 
   private:
     bool* mAttemptedInit;
   };
 
   nsCOMPtr<nsISupports> mOwner;
   nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<nsIPrincipal> mOriginalPrincipal;
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIURI> mBaseURI;
   nsWeakPtr mScriptHandlingObject;
-  
+
   bool mAttemptedInit;
+  bool mOriginalPrincipalWasSystem;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -3,50 +3,36 @@
 /* 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/DocGroup.h"
 #include "mozilla/dom/TabGroup.h"
 #include "mozilla/Telemetry.h"
 #include "nsIDocShell.h"
-#include "nsIEffectiveTLDService.h"
-#include "nsIURI.h"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ nsresult
 DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey)
 {
-  aKey.Truncate();
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  // Use GetBaseDomain() to handle things like file URIs, IP address URIs,
+  // etc. correctly.
+  nsresult rv = aPrincipal->GetBaseDomain(aKey);
   if (NS_FAILED(rv)) {
-    return NS_OK;   // aKey is the empty string
-  }
-
-  // GetBaseDomain works fine if |uri| is null, but it outputs a warning
-  // which ends up cluttering the logs.
-  if (!uri) {
-    return NS_OK;   // aKey is the empty string
-  }
-
-  nsCOMPtr<nsIEffectiveTLDService> tldService =
-    do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
-  if (!tldService) {
-    return NS_ERROR_FAILURE;
-  }
-
-  rv = tldService->GetBaseDomain(uri, 0, aKey);
-  if (NS_FAILED(rv)) {
+    // We don't really know what to do here.  But we should be conservative,
+    // otherwise it would be possible to reorder two events incorrectly in the
+    // future if we interrupt at the DocGroup level, so to be safe, use an
+    // empty string to classify all such documents as belonging to the same
+    // DocGroup.
     aKey.Truncate();
   }
 
-  return NS_OK;   // aKey may be the empty string
+  return rv;
 }
 
 void
 DocGroup::RemoveDocument(nsIDocument* aDocument)
 {
   MOZ_ASSERT(mDocuments.Contains(aDocument));
   mDocuments.RemoveElement(aDocument);
 }
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9853,8 +9853,38 @@ nsContentUtils::GetContentPolicyTypeForU
       aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
     } else {
       // Fallback if the deserialization is failed.
       loadingPrincipal = aLoadingNode->NodePrincipal();
     }
   }
   loadingPrincipal.forget(aLoadingPrincipal);
 }
+
+/* static */ nsresult
+nsContentUtils::CreateJSValueFromSequenceOfObject(JSContext* aCx,
+                                                  const Sequence<JSObject*>& aTransfer,
+                                                  JS::MutableHandle<JS::Value> aValue)
+{
+  if (aTransfer.IsEmpty()) {
+    return NS_OK;
+  }
+
+  JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, aTransfer.Length()));
+  if (!array) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
+    JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
+    if (!object) {
+      continue;
+    }
+
+    if (NS_WARN_IF(!JS_DefineElement(aCx, array, i, object,
+                                     JSPROP_ENUMERATE))) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+  }
+
+  aValue.setObject(*array);
+  return NS_OK;
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2741,16 +2741,21 @@ public:
    * for displaying in the UI.  The sources of such images can be <xul:image>,
    * <xul:menuitem> on OSX where we load the image through nsMenuItemIconX, etc.
    */
   static void
   GetContentPolicyTypeForUIImageLoading(nsIContent* aLoadingNode,
                                         nsIPrincipal** aLoadingPrincipal,
                                         nsContentPolicyType& aContentPolicyType);
 
+  static nsresult
+  CreateJSValueFromSequenceOfObject(JSContext* aCx,
+                                    const mozilla::dom::Sequence<JSObject*>& aTransfer,
+                                    JS::MutableHandle<JS::Value> aValue);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -2122,25 +2122,17 @@ nsFocusManager::SendFocusOrBlurEvent(Eve
   // set aRelatedTarget to null if it's not in the same document as eventTarget
   if (eventTargetDoc != relatedTargetDoc) {
     aRelatedTarget = nullptr;
   }
 
   bool dontDispatchEvent =
     eventTargetDoc && nsContentUtils::IsUserFocusIgnored(eventTargetDoc);
 
-  // for focus events, if this event was from a mouse or key and event
-  // handling on the document is suppressed, queue the event and fire it
-  // later. For blur events, a non-zero value would be set for aFocusMethod.
-  if (aFocusMethod && !dontDispatchEvent &&
-      aDocument && aDocument->EventHandlingSuppressed()) {
-    // aFlags is always 0 when aWindowRaised is true so this won't be called
-    // on a window raise.
-    NS_ASSERTION(!aWindowRaised, "aWindowRaised should not be set");
-
+  if (!dontDispatchEvent && aDocument && aDocument->EventHandlingSuppressed()) {
     for (uint32_t i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
       // if this event was already queued, remove it and append it to the end
       if (mDelayedBlurFocusEvents[i - 1].mEventMessage == aEventMessage &&
           mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
           mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
           mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget &&
           mDelayedBlurFocusEvents[i - 1].mRelatedTarget == aRelatedTarget) {
         mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -4221,22 +4221,35 @@ void
 nsPIDOMWindowOuter::SetMediaSuspend(SuspendTypes aSuspend)
 {
   if (IsInnerWindow()) {
     mOuterWindow->SetMediaSuspend(aSuspend);
     return;
   }
 
   if (!IsDisposableSuspend(aSuspend)) {
+    MaybeNotifyMediaResumedFromBlock(aSuspend);
     mMediaSuspend = aSuspend;
   }
 
   RefreshMediaElementsSuspend(aSuspend);
 }
 
+void
+nsPIDOMWindowOuter::MaybeNotifyMediaResumedFromBlock(SuspendTypes aSuspend)
+{
+  if (mMediaSuspend == nsISuspendedTypes::SUSPENDED_BLOCK &&
+      aSuspend == nsISuspendedTypes::NONE_SUSPENDED) {
+    RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+    if (service) {
+      service->NotifyMediaResumedFromBlock(GetOuterWindow());
+    }
+  }
+}
+
 bool
 nsPIDOMWindowOuter::GetAudioMuted() const
 {
   if (IsInnerWindow()) {
     return mOuterWindow->GetAudioMuted();
   }
 
   return mAudioMuted;
@@ -8841,38 +8854,30 @@ nsGlobalWindow::PostMessageMoz(JSContext
                             (aCx, aMessage, aTargetOrigin, aTransfer,
                              aSubjectPrincipal, aError),
                             aError, );
 }
 
 void
 nsGlobalWindow::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                                const nsAString& aTargetOrigin,
-                               const Optional<Sequence<JS::Value>>& aTransfer,
+                               const Sequence<JSObject*>& aTransfer,
                                nsIPrincipal& aSubjectPrincipal,
-                               ErrorResult& aError)
+                               ErrorResult& aRv)
 {
   JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
-  if (aTransfer.WasPassed()) {
-    const Sequence<JS::Value >& values = aTransfer.Value();
-
-    // The input sequence only comes from the generated bindings code, which
-    // ensures it is rooted.
-    JS::HandleValueArray elements =
-      JS::HandleValueArray::fromMarkedLocation(values.Length(), values.Elements());
-
-    transferArray = JS::ObjectOrNullValue(JS_NewArrayObject(aCx, elements));
-    if (transferArray.isNull()) {
-      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return;
-    }
+
+  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer,
+                                                          &transferArray);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
   }
 
   PostMessageMoz(aCx, aMessage, aTargetOrigin, transferArray,
-                 aSubjectPrincipal, aError);
+                 aSubjectPrincipal, aRv);
 }
 
 class nsCloseEvent : public Runnable {
 
   RefPtr<nsGlobalWindow> mWindow;
   bool mIndirect;
 
   nsCloseEvent(nsGlobalWindow *aWindow, bool aIndirect)
@@ -9200,17 +9205,17 @@ nsGlobalWindow::EnterModalState()
     nsIPresShell::SetCapturingContent(nullptr, 0);
   }
 
   if (topWin->mModalStateDepth == 0) {
     NS_ASSERTION(!topWin->mSuspendedDoc, "Shouldn't have mSuspendedDoc here!");
 
     topWin->mSuspendedDoc = topDoc;
     if (topDoc) {
-      topDoc->SuppressEventHandling(nsIDocument::eAnimationsOnly);
+      topDoc->SuppressEventHandling(nsIDocument::eEvents);
     }
 
     nsGlobalWindow* inner = topWin->GetCurrentInnerWindowInternal();
     if (inner) {
       topWin->GetCurrentInnerWindowInternal()->Suspend();
     }
   }
   topWin->mModalStateDepth++;
@@ -9237,17 +9242,17 @@ nsGlobalWindow::LeaveModalState()
 
   if (topWin->mModalStateDepth == 0) {
     if (inner) {
       inner->Resume();
     }
 
     if (topWin->mSuspendedDoc) {
       nsCOMPtr<nsIDocument> currentDoc = topWin->GetExtantDoc();
-      topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eAnimationsOnly,
+      topWin->mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(nsIDocument::eEvents,
                                                                   currentDoc == topWin->mSuspendedDoc);
       topWin->mSuspendedDoc = nullptr;
     }
   }
 
   // Remember the time of the last dialog quit.
   if (inner) {
     inner->mLastDialogQuitTime = TimeStamp::Now();
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -977,17 +977,17 @@ public:
   void ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
                        JS::Handle<JS::Value> aArgument,
                        const nsAString& aOptions,
                        JS::MutableHandle<JS::Value> aRetval,
                        nsIPrincipal& aSubjectPrincipal,
                        mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
-                      const mozilla::dom::Optional<mozilla::dom::Sequence<JS::Value > >& aTransfer,
+                      const mozilla::dom::Sequence<JSObject*>& aTransfer,
                       nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& aArguments,
                      mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, const nsAString& aHandler,
                      int32_t aTimeout,
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -893,16 +893,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWin
 // NB: It's very very important that these two classes have identical vtables
 // and memory layout!
 class nsPIDOMWindowOuter : public nsPIDOMWindow<mozIDOMWindowProxy>
 {
 protected:
   void RefreshMediaElementsVolume();
   void RefreshMediaElementsSuspend(SuspendTypes aSuspend);
   bool IsDisposableSuspend(SuspendTypes aSuspend) const;
+  void MaybeNotifyMediaResumedFromBlock(SuspendTypes aSuspend);
 
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_PIDOMWINDOWOUTER_IID)
 
   static nsPIDOMWindowOuter* From(mozIDOMWindowProxy* aFrom) {
     return static_cast<nsPIDOMWindowOuter*>(aFrom);
   }
 
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -1279,19 +1279,21 @@ nsScriptLoader::StartLoad(nsScriptLoadRe
                                        aRequest->mReferrerPolicy);
 
     nsCOMPtr<nsIHttpChannelInternal> internalChannel(do_QueryInterface(httpChannel));
     if (internalChannel) {
       internalChannel->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString());
     }
   }
 
-  nsCOMPtr<nsILoadContext> loadContext(do_QueryInterface(docshell));
+  OriginAttributes attrs;
+  attrs.Inherit(mDocument->NodePrincipal()->OriginAttributesRef());
+
   mozilla::net::PredictorLearn(aRequest->mURI, mDocument->GetDocumentURI(),
-      nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, loadContext);
+      nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE, attrs);
 
   // Set the initiator type
   nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
   if (timedChannel) {
     timedChannel->SetInitiatorType(NS_LITERAL_STRING("script"));
   }
 
   nsAutoPtr<mozilla::dom::SRICheckDataVerifier> sriDataVerifier;
--- a/dom/filesystem/compat/CallbackRunnables.cpp
+++ b/dom/filesystem/compat/CallbackRunnables.cpp
@@ -46,21 +46,16 @@ ErrorCallbackRunnable::ErrorCallbackRunn
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(NS_FAILED(aError));
 }
 
 NS_IMETHODIMP
 ErrorCallbackRunnable::Run()
 {
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
-  if (NS_WARN_IF(!window)) {
-    return NS_ERROR_FAILURE;
-  }
-
   RefPtr<DOMException> exception = DOMException::Create(mError);
   mCallback->HandleEvent(*exception);
   return NS_OK;
 }
 
 EmptyEntriesCallbackRunnable::EmptyEntriesCallbackRunnable(FileSystemEntriesCallback* aCallback)
   : mCallback(aCallback)
 {
--- a/dom/indexedDB/test/unit/xpcshell-parent-process.ini
+++ b/dom/indexedDB/test/unit/xpcshell-parent-process.ini
@@ -60,10 +60,13 @@ skip-if = true
 [test_schema23upgrade.js]
 [test_snappyUpgrade.js]
 [test_storagePersistentUpgrade.js]
 [test_temporary_storage.js]
 # bug 951017: intermittent failure on Android x86 emulator
 skip-if = os == "android" && processor == "x86"
 [test_view_put_get_values.js]
 [test_wasm_getAll.js]
+skip-if = coverage # bug 1336727
 [test_wasm_put_get_values.js]
+skip-if = coverage # bug 1336727
 [test_wasm_recompile.js]
+skip-if = coverage # bug 1336727
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -449,17 +449,16 @@ function SetupEME(test, token, params)
     }
   });
   return v;
 }
 
 function SetupEMEPref(callback) {
   var prefs = [
     [ "media.mediasource.enabled", true ],
-    [ "media.eme.apiVisible", true ],
     [ "media.mediasource.webm.enabled", true ],
   ];
 
   if (SpecialPowers.Services.appinfo.name == "B2G" ||
       !manifestVideo().canPlayType("video/mp4")) {
     // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
     prefs.push([ "media.use-blank-decoder", true ]);
   }
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -13,16 +13,17 @@ support-files =
   corsServer.sjs
   invalid.txt
   layouttest-glue.js
   noaudio.webm
   small-shot-expected.wav
   small-shot-mono-expected.wav
   small-shot.ogg
   small-shot.mp3
+  sweep-300-330-1sec.opus
   ting-44.1k-1ch.ogg
   ting-44.1k-2ch.ogg
   ting-48k-1ch.ogg
   ting-48k-2ch.ogg
   ting-44.1k-1ch.wav
   ting-44.1k-2ch.wav
   ting-48k-1ch.wav
   ting-48k-2ch.wav
@@ -109,16 +110,17 @@ skip-if = (android_version == '18' && de
 [test_convolverNodeDelay.html]
 [test_convolverNodeFiniteInfluence.html]
 [test_convolverNodePassThrough.html]
 [test_convolverNodeWithGain.html]
 [test_currentTime.html]
 [test_decodeAudioDataOnDetachedBuffer.html]
 [test_decodeAudioDataPromise.html]
 [test_decodeMultichannel.html]
+[test_decodeOpusTail.html]
 [test_delayNode.html]
 [test_delayNodeAtMax.html]
 [test_delayNodeChannelChanges.html]
 skip-if = toolkit == 'android' # bug 1056706
 [test_delayNodeCycles.html]
 [test_delayNodePassThrough.html]
 [test_delayNodeSmallMaxDelay.html]
 [test_delayNodeTailIncrease.html]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..619d1e08442c2f06a80bc7a4c68369f1012bc8b9
GIT binary patch
literal 8889
zc$`&wb8sb0^ks~Rt%+^h6FZsMwr$&dvF(X%8xv#VykKI!c<1}=R_*Ss>OOV5Pt`qj
z?myjCSIN>+6$}FG|3t(I!1O<q7I$|8$53)|ca=6bHUkIe0T1f_AA-_<bg=)I4*n0b
z#vF6NfBr{TGq!X^`gdYyZQ}SZC1GV^Wn%k}!uoGXD2geGOZ*i4@5bE0l*Gct(VpbL
zgwf5>(ax2G<sX@ok)4;((u|wcoRgd5e^>iIrT?eee@fkIkZ#z&*iw>`09ly@nK@ZG
znMJw3D)I|Ut6RIuYbqP7YO0&c>RKvW>MANLYs$(i>WVAtO3Oe~&Y3nNwz8U|%Mkns
zX(aX}`vSZRF%WzEHPdVEPLk8#PdZVG%A{)u^v;tM2W#D{g$*@mOy#FwnvqzmS)S1L
zuYn83I3?+#QZynxuIs+>;cK{S-DvGQ3wz6J4|f;8iMs^>FTt2)aO$E-%crPY3G=Q`
zS8ex3WoEs^a|wpFTqgv-h_jtpC9lqVsn*dfEcaQzaJh)&uD^{rsngGw;bF%WEm#ZS
z_DH@frg(HkG8&woLB9oPu*3snTL%h5+4;qJA3Bu^|LrX&7k7XIW;L6&(ZDutY8O5L
zx1^910jd(#iOP4`2)vspV&t2`NfsWT=ONYWm1Ua79RjQ*jjyJQJepHG6@PjnutY#%
z%quGI$yC)4o+Y<|gMxs$vz4(JydFEdQUZJVQyyp-qHkotPbxeJ$*MP#G1nzjBDE~~
z#|akOIpzYZD+#|%9(Q|PO~8=Z{i%^<BLXHdu9XpRLhHLqFaj^XulT-|%z3@eV26}s
z+78aQ_RyZ8b4~pLsu=8ZIJE8zlQ7M6euX}p3BJc-{hz5`W5MVR5+Xhlbbs3B^XGe}
zss)*RJNN6*9<Kz{RzKM1M!H;aK*M+qwl-pq>I%#Vw$Dv1WrmCL)w!=S$>ocAtV(@@
zt?eEzr-ddV*)I!pe>SnWPw^njh-K0h#*wpB7Xm<Tzx5wodKT>)cFG0I&Wb%&Y8)Ix
zo2u>~-rC&o#m^};^VmEC!jwxV-L#`Fu}I1+)#3FJ>idHi^G@_-q|Xz>_i%X}dN2CB
zcCD+%$D0XIK_hY!B?Y<r_)7{YOW5D(jYJJS+j5ePLSP4A<2=a^k`KrIT2R0%)_XA}
z4zL8Bi*FNeAbHG4L9ZiQV&Tst7GpYn{LePMz76S5#_>z*f9$TJBOXr!1;p?@R|8S0
zX_DflhBLZ;y_s#jY2Gts5boYVhtGLecmM5sM?i<45Wg5VX=jt0thEhKFE${D2MuyR
z>`=VpT+imr((LcLdBBJgVi|?`B7IR1IA=#DQ8L-cUtO$gEO6B{rB0(?4$#~aZC_>v
zQlh5BzdIZ1YZXpYQP&{LSSL?;__F;l?EJ!dHZRCyo198sG2d~smbZnD&-2%;x8Yg^
zh~gPGYiq@4nMD5$G74HW5XmZeU9fBTO~y(bc9`WC-E`TO*kSSH3JIz?Y46VNHP^Ny
zTB!CbEVd0gaww(sBF*uIM7l72*jb*f+gJeHOr5|AxfdxFE;n&K5={#W7VtrZ#T+h+
zRyO3RB3ZLP%x&_1vkT%ZQicX&c#)D~S&nC1?&B=M(-jvbSlO;?1jEH<+&(DmHeg#?
zHz5zp*IKj?z)nxtItt<n)$owxPcyvS|J*Xn22L~BxEh{aa`%F2u3tb>sqvpFXQq8g
zS5McCD6(^A?u#FK-<_`_d&pGz88dJ52Y887q8#++eD?IO3tw1bjTec#!&~yRw~*v!
z>Gt4mrO(nMyk*->Yg-&n+ppKsPeQ)K%r71f$0Zxq1=7~!d=3ImfWI(d{7Vib;o?#_
z-*icoofYsBm4zZ~LJ+ShIGyMCXi%Wu+S@P7_hK}yU)n)6<84lflk)eeiKfsBMD0I_
zY{F~4^0Qcj{!$(rM91YbQD&*{v0NoB@1&UBN1~4BC$i1u`HgFj0br+)=Dg+HEq~T7
zscKdPld)HSBgOiHTGFbw{h`tZC>gc%scme;j^>n#_F*a`m@Y%y^2^I$ab`XTzB0qE
zX|(};)QH-q7uYO+|2fTHkMIJIgE?;u&~NM<J#%Zu!$A_TZ3NYft)E|??}AwbV}2tq
zXmAcn#>CxU&bB*=+(NfnjvQNoto0;B{F{x<-NNIa7QW|j-NSl(iV_;XNpW)p(Pms*
zI1yQyJ4)1L!!|{dTaWxXbsKT?Q?5q(4KP5(;8DJ+8hf;{2#tCgGi%$s@i=&Twu;@!
z)9R|P*#hN&R59qdZz|St3QRh~mre9?v{;?C5ij&vs{Lo%`F_ba`GsXZa6KwU;l$hn
z-2Uxfw}<WS1X9?|MbPOkK^?D6{y4z20TUj7L!@rS%Vbd)UjH`yx_c!WAbt8_oGQ-^
zLc<!YVOjgZonGz%SoFPCN2IH3*lR7X9S<$r`-Y;}Y7154Ow%Or%Hn-)!Lk4oBNg*)
zI{uHM9iI2D{QWo-RRzGWm*Faf8`V^|E>mF3D(Sca)ZhfWI;xCVVGnpr7~3<$G@VRL
zFWUQZUwd760u@J~=bpxI9GkeDHDCU{lI|FVS^UDRTpQGFsf`o9q6?y_MHzf^3($H;
z`sF3=(Yh=2k{NTBGy#iX$P2;|LQg36MZpulWci$eq@V*}`&xrgz#mzWwX&FYzB(B_
z78Rf;dcgx0Z*>KGP*1y>wYG1~yKFrari=-T|LY|%gACokfLjii4_UbUv*l~2^@24_
zetuXertEnK8IKW^iG$S??^vV8k>`khUf&E?$3@Sgz2Jc6NeVV>gzmFur^Dw8-5Tv8
zKM;5rhV%wz(A2xQY-87$Cr>cznissc2sYCgB+4r`@jD+c4SOR+E`wOx-MaN8J|IQ<
z;)w1ikf}ZFK*fv_Au5rPbyEDO*88-KI2Tmot=<+)KrW@c7reDgUK}EnF%{6Zh6TYT
z^$l3ltdlSv2FF-AI;Q}e+@n<TO{d?2dqNk-FvBX!gWT*Zd}X%fq`oNB(4+xaKn~&d
zK(4~Kl$6Ti&&*LhY#^0jf6P==aIc*BdA3`@h1Vnh^UxN2uozbGdaWD2`_W#5MQAX|
z4)UrrwXuUBGU3=@Y3)~bX5zi;WhSVuLjuf#V@|7FOQA)b|0@8;kg&*UVtnWPhj;s!
zhctEoUZmZxJm~(HSd50(unhAOcO2{N()+vp$gqQjw3<7=*`!3tESL_ggAnr%Q{J;1
zNz_`I;)y2ty~?lf+YOHlkvk0*WK}chZyyIIp+MGXJ5KER@js4<9d{7>Ct*Bw!ek6C
zcdb9Eo)crgL%otMWCH2$Fc?zW>2VC%q{{%Hy1@Hz1|{WyejCE}gT16IeJV~GS{+Si
zd5Pq>9ERcbmVV~N-f4b)q%pO$jK84)d@giwaoMc4Io!_fh-Qvlq(aM{y>R<b&|{C?
zo7yw6G(i3BEj$<*3C!QaQA^Ho52K|;!7;J5EJ=fLayinhY8OUkp2juDr;&<qia+|}
zLOBB&8L~(}iM}vw4V_48%tpTqRVVg<K<#8u4WY!e!V(H~octA4cz;*LBiN)ihV6!B
z*tY2?VxBzh){7?s?FH=(qD%<2=@xQWSHbV1t3?hjn04`PdJHzg*r)?pS50SkBk31B
zKaR~LmvQn^aO3Q|twW^9Onc$k^{@_R>GlDpAA71?1)X<Qet%6;s>C`|XpvzkxlY*Q
zpc<#_V|&g20<F7im@)D!yO^a?xevgHeHSZ2n!t$Rb<x|iI>8q7#Dx2Mi{e0ap5`#M
z3TpM^Gk=)jdX`HYSAOZZ#KLrnTRC|{)ACl@P3&e<E4gll501UXz{J2AI8vvbGjz)S
z2@M)^*)WCk6UiDM-P}(fL-ETj>ck1@#<%3?G~JUeNOPT{qPTVWY}vxu_CTdGqT1qh
zZxl~gOgS#R;h=_@8{jG+bg!iQH7gnS?m){<piD$R;{jv(-lG<J3ED6k$vvCHVU%^6
z&M{b~kNGFlef7gYF9Kh9>7++siyLty;k|dF;%D$?6f^c)&!%6Qw%N#vmB`pO0XIXK
z8Zh2&=^1He&|FA=EZ5HTQ}DuO|BP$rVS&F5D#=HHzp?cOdVEpK3F0XkbK*IENei86
zuEBlYZo`x3nfHxBM_@PzCtZEjpQ!>DlDS18R@I0hfM2tVU#ekH_;k&A5_9%%v2lho
z(CleqA2RZvy7p_EHQo&S2H6N=tg^~|`k2$yHJaaM`Q2Q?o<nn9mkoF!0-;WcnH@h4
zfm<Uzhnjq~T~!SPc0v3$JhTe%tY_W`_S($p(JD*nyT1t*0ak=t!vnR-$;f`Xu;0M;
zPa1S-xZa>AWA-@NS}%6_lHrAtEYz2>-vFDVvGox&)=$(ixMlo+AIzkF(Hky_^nbyu
z6|WyJE2M8d*h4`Lit&@7)N<Vx?Ht$>((L!VQ=V$@XnPMCPQlQ@4i`ya>l-oA8Kp+<
z{^v$rE1Ek&(ZqQ@m@T#VW~)x-e&<f93KK;&{1Su#z*E!yUmedLa#OdA%J&&f(NC45
z5Ophew!Ap(3}c_vQQCb1ImlW@rMXJ7v2zIRCtvOlx|1ZQ(6HG<f8@`Mm^AYcgaG?`
z3FPjEH>Sgp-&ePSam86Mw1Yr7h#l_h9aYLPZG1%RY_3;jGEtbeL>yMf`r16-hr=~R
z=MxO-y1NOMVQ?U3lSvKT9ae43%cGep6HT!e2l%Sv3B+-@8V8tx*x~3l7#tbYjVRyv
z=gV2ho>!2gKbAiF|FQ~yGDP>QQgf;QB4j4DcP!H9;%N$5S@hQ)F&Q4%QOFkbEA=7a
z3nqm(>5>P(AH3xi3phUA6k!z73w;+r>Ja*!{`yVg4=DSQnl%kWj}}jE8{PLsFC$N*
zAxr91+h6`@Xl+@@G8#_qqoL#iXExK6W+88t(tqDX&MJt`*Lol-HC~WSAlW!hLl=P^
zS@(=zBFka3+<?V4tov;gyG4m~-bd^j4`tLogmG@#rNH%|ZUly`>NDRj;T&NO9%N}2
z3@en%xVo0MmZ8CZj+|=K{emhu82XUkApO@qj0Wk7MS41nnP;p|E8SWRyhMV>T56};
z|7*_;YU@9H<8Gk`4yn#db6J_kYt_U2bc^uj>>J%i9;&;XYesPimYC0k>(srm=$M0(
zb~_~u&saZ8Ugo|1AytI!Dxk`6r%+Vk+^dadbFmq7BbK8eG)(Aces<^suT|6sVYd%e
zH*63KV3V${YtYeRl)W}Tyu(aIL!f^X%UF4d$xkoTUDhWNrv2cd1{}Rf3jQiXWY^bc
z%tqV=P1{!(lYdbXl#yn%m8}Ro#VSqX0~BK8>Gx$h@K!;&-;acr2C2<6ZaO3vlHLJg
z1$_qBp0SX)MgTS!3W(MGZQQp*3`3VgDiIr#JI5XDQ?WZ+Zk~WIkVBO*b*%EeD)L&f
z{jT$o%k8w<`8Xe+1q3m}P^UanM;urek{>+VPyQ4HKb7+buUH6U-1-{RM)j~gPOhi8
z8$DN4{k%6=#az?#>jvAZIQO0S`8|&zmE!zs7Np)^B9gqNDPiNIih!;aP<FiRcVC`R
z9q$0U+1h@q?TDY-@^|e@98%UyP<Z=jd;Y3l$Mm9cpAwo{mM8lq(Y0Z>O(+qX1EGa7
zD_bjj97>(U+&29(A1DxOWZ7rf!nFIjQOj1$Y(Y8C;~N;W2EaHOj0(j<X`Y`L{Suut
z0cn9Es8h+PSeXik&QKq3TN`Ldbe_r;Jk!=F^QrMm+r9Cxo;T)#2ru~7IO9T+)VHAT
z5z+?^HoHrp+||uQrTmSh2QzQY74{{);xb+tgKbx72w#ZGJ5n`Asx0v1A1XFZT*cTV
z1M%|pT6rqn!X#1LeD;}Hz07iH_L!Fh?_eQv>5odf(9k5lf5`3=civo7OHdEw+GQ3}
z16k18FfG*M^8wYq*BR6$&hh(+<V`LTKcuK38bVNPAHGbf{ls?C9~2U;H$DgYi3c)a
zObVhNEjRuUV@)E@RzE0YLf9^XvZH@a9B4<|+263aFX=*R={~l1xKj+xN|QPhFAa;w
zhX)lSHk5Sa-p9WqW9W>;71pA#Mtm@i=uyZ&=tul6ksMY^8m9?Fm@tvw)J`!d?TY}@
z&@3wx>qrsxZr2ACPOs$@$B~0&O#kWLAc@a}_GHOxhV$JzzoyUY!#dUMgdmD*SI5az
ztr+L$LiA;qUY-kaixjKtVvsGo3q`2VeDEgDI{t`!Phr*nF2tra2g<z=?svuA)>VyG
z5vt%kyVCDss&sny@EA#XM(I@;d`^;t`U%U783O6}jIx)lcKXZ#j%4ALQsu13hAfO~
zaT46F<@ZBy?u^0JX6oi-zTRJ5Sg<st9ukoR7?l`eOE3v&Ckrreie^hiU)nc?N)Ra&
zXB{kE9lo`jXxoK-Dx0X1zxO(He?K`X!3+P=;1L8eU$#~Rl#8B$S3fu9MZ(LeN5$oD
z&VX`YaV)>%J<sS~{iJArHdAVxmKG_ko&ll`!)97&>hogfSHpYzsF=7Nhc;dIB@9NA
z+Ck<%dqeH$%|c}EWts3N>|Q|f%P^4}*Qmu*yG8$EXigNkf3M$}(e_-{NG1aZ$69Ma
zbq*}_p5c^-1DlK>86eX>@pj#X5q}5-NC&4tlx~w1us#!VcF)S(LW%p&s&V#QFI6iY
zr6VmvF<6*Hg5~;(Rd3=?fpRhZoCcAW>8(e$Yicu%^pmMK&~sM`@|=)2Lb}1>ZszM?
zIo93*@}bzm7_zM&AfF2zSP9>;AmLD?IEtrZ1qsZv)fw|!td*iG>4D9bP#^nr1BGKX
zfS#cNp7=~%-;sE-m4@;Ylrgx32cX*yVL~mmHwGwv-HMX*j2R&UqtSqu23eADk}VEl
zLnz0=l&EV~PwffIW#==(b+=P9aA)f)`^rqxHv!7U4YkeU@_7}TK}#mi^BA9j2R;-j
z3J@FV%UPy8xoxO1#;rKNy-NNZKaSY~=1QIR8@uGnxyNN`ZI^l12qVDvwTXYU;)MGA
zU{UjUOlgmoqK`W|{1CRWd)rve#IMYRLmVOJZ~py#!Z4w)Fb>3jSW*9HX`yPs*|9|G
z-**Nj;tp^ra;w_}L3d(I(Y6ofL_kSC<;5YYz5FrRzKZh#(+uX$X>J7ny(UH2)TVt5
zl=Jb(=WH6cx}tS*+?PI1-PI?;{2amHf@wX#sI@0^WPy}hO-IK=vBnM649VW5Lp3Z(
z9@RPfX*i%79kl+ca7(KZo{WA2lt-@EjKN1-ALn8j+&Ya%kV5Xv9UnR+s?~)f>r^Ne
z=tI~fk*MDBYg7L<rYcP7`*&z+29&s)r=c4JDha!4Dls`B+(DM3H*#yCBU4{NmA=!0
zUGP-tInW&(1?56Fo=z4r6;lm$ge?IGc@G>TV!kdW$_uz-(CakeuJZswV<*M(Ug*>E
zq-U`;am+*1V9;p^lnE(eH(Ss&!C*s^7<}9=sa45u8~bt)=a=7yi(g<vjTun%zQV-b
zjuTyzJ7fZ_xDKWWcOr?G$&i=KXw3G&4$;#o6OrLkHxVoUK#%vrHK%d*3uMZzl9{+r
zOmo}ILt`>-=qeHKBJoLfh1tpP@Z)@y4TExL*ewii`0|BzrVjbPGPi3zDZTsgC}~vo
z048vLj1wx{7gXpeoB8`(x;S0)E@PCja|?&<O*5|}t|H4!>o?SHz1&zx%Ywpag)M4*
z10N=B5z#q#pD%ViH7Ro%%r6Uqrk{1gg|Dv(h|YVOSXQ+^X@<B6Bl2jd9y>;ea5ch}
zJ@NsqaSXE10TXr{)U!|ayY{5ttr1J<*vvaM&CBpc#<63FU_N%67^z5X)O7ik20^*g
zh5kXPXFP1uGL+d;@}7k!i119y(#kX}2BWF(Um2d9d<JG*QZKKB3KD-g^}vwOUhST8
zz1X$(X8AwqcryfQVMoyX#WIffu<|+(wU(olSU0+QrtHd-P$~fvXei9hb8iU-%Xis>
z8hr|Uoaa9ts6^FfM9=YXOMJHZd>}Q?8vAKvDdN+K=le1I3Q)G>A;%5~A#Asp`o75b
zMDf~Uy-w{Z20#&u>cjz!Hk`B#*Ef9MRz2DC4wa8+M7vLmM*lt2bF=f5IlZJ0z)L~}
z=Jo@DqV(YvDo8(ax4odv_UcN%8y8|9GMNpr(mIWbglo+ZiX@Q7!@^jJfa@<7?;{;Q
z7L=esB6>Mr>qaELp}ItcPiUsChy$43vSLXHaQ>QJFe8iBG#|h~R)WDmd8RN=<R;$d
z_lqzDMT;gqhY#ocCS$6i22pRun(6g5#MgKzU8B@(8-OC?^48vT!XLXA%l)(v&*c+n
ze)G+2Ulmdk$S&^p1_AA?tzvUVnyAK^&VSP`K%kHtIOixJCUI&ty{f;VBM1$Ss?V&v
z#Au$Z>Pd8lRNk6(!n=PR^va%`yU1-y#yr8vCGg*bi2RDCeUsuQ&19@v##t6Dxr|cJ
zR$!o8z`DQsF4m<PD0RY?z;I7t4#rSUAE35S{Uec=+Rp1n*O%CANMCN+wo`lj1M3nL
z>9GbYwas~=QZchz%7I>5T@<+CVUj9~o#TuCMTM0bnwgh&uBeyaoMDNM>9b(sw3r*c
z+ft3Kn649`M@$Tb>FDz+mqSdG6L53dDO$#$%oMETa72hn6>vu~JS;J^B4a(%O>8)5
zuJi-L*9ZTn9uGwbUxI-D=oG#N{6s6F4}W0df`>*Hu4{~5=iAzv_kgc@_l1D>u#Qq@
z@F=gK@mC;P^93l<CCGsnGo1B_>m9rzs8K@i*!g>+3Qv|3;&L3P=j8k@if5@BR~1-e
zs)&AFK}`c0OJ0kW<Jv}s>tY3^K8P6t@vhlVTGaBS=|a0VCVj7z<|SBZY$-LS*x_%u
zqF=t7M_SDU5h3sr92z5Go5EC9?)T*D=gKKnmc{GpCJ`%p3R^Q6G>pwBZ*l?ZUp<w0
z<9g28+W`#vRLwK^Cl$qH<tabp@pDk5JM3ODcmDu(y0f%A_AG)krzHJC5bl|o>yC8*
z?-QO<_VThi$%HH&3mSGfa@Gb#8w}~hb3i^sN0L1l6j6~u#&S>Ov30m})y+Kt*M<A%
zPDxAKmgDMPE+d*-Xz)y}`x%(lmNzZ{{3ejZKxY&?h{ga>Cgc6!dy?Ujz{!1@5r*CV
zVCRTnXL!-!M`acMJGw<F&)C5Zq60C)Dt6`17Ev`D$}><D1f?S3J7$Rjcac_|!W_VR
zCzn1Z`%rtVfg@%6x<w#8?{b0HtBIG*x92o?<Qu`CVqG<CuggZ$i0~=ELu)D>gV{cW
zCkzV0Rjbl7$9HJCtB_0J)ES~aui!%=!53=0?UXNBUd?GqWjHL=n<BX*U8e9M?K9zU
ziNQ<%ra)(tHBT+Bz~U1t_{t~#For2@6l;c;IrtvC58WTKl`4%$s*bI=3cca3Q&7YL
z^(9#Ys#b8~SDXtI@2K`7p^;Bv5uQl=57>katIhV?4h^fp)KNMez;UMU5}t!K|14Bw
zto0xVs;a#$X#ji`McHR5rZmi}w|!LH8!)S$Dszs&gr}fce5Y?w_wPN|dsDBzJKYfW
zUjwACRXSovFI^|;+*Gio<l%8O!Bzh!gqPNMJMa6E!I4?oa0QfM`gyKS(SvMT;7=r-
zO$K(Wl@m~8xSGuKfDKN`_OuHcE=^~^x1(eHcc)3j-T~EAeE6ge$LO;kt$@TR{J?@p
z1ov+O{9r^HoV|s0I1~zuR?7>SIp-5B*8JVf8v~{aiJBg_#;68i9)5L<9D&^@3x#|T
zGg8oh(Y=i1&?DPGv;BXB6e4xZhxm~??qqGkdLBn&V#*|4V@P+|JQX=$PRqqG=B^L}
ztmhBZ0uDDg3#(kFBL4IML6I?-08+DN`8>Ro2>6G_`2g!JkO*%LKU_D291I^uHcTn5
zy0<~hBjx(~4R#=95c)T|r7~SLDJ}rA+Dr5SD*<Ktn`Ni7wC4~83dqI38tDe7b(usj
zX^;a5JkiAp?aC{M1I&d@6vE)>-lkKAHlK3L^uNFk8HdAguE~ZEi6w4`ek{++O5h%0
zSxzOA|H9IWmE~~tHv;38!*1g{<r<)rRXP4=ru<&v#XUm*?5@q2eJnb}lYOU3)*0kz
zPEq{w_OSpZF9nZ~_bw}U4W}*ifmKn)>)crgL9fXonI%{k>En=d=j;duwtP-u1nLOU
z?^mUlm#ZWxo<YNbGQx|o>OZ4b1VWRR;Wh!Ig!$oEAq)B2OP>}lv{J9xz^e#KxN8Zl
zRz&jBvO6jm*JK2oH|NS0$Bi2|$!rF7x}_+kMfP1RT!I%qr`QxSo*6Sx<RAGr_of@0
zk_;ZIjH(&oNF;n2p4d%)$bE5jZ*)abss-XOx@NiNIe#NU-#o1V!(G+tOGvi>_2TMT
z6`#X7(y{9GUK|W2d+R=+w`9oD!`t3)OhA1fu`5=0%+<RQ?LGk-OTGOJ_PV5-*q7}%
zSPF1CUfSZo@PTO4p-CwtZ2F9~j@z(PNsA4&VqnR3o;i=})m<ca@UTb<`x3>#6cib4
za9ig+`KXK7SaIYM#F%9^rc1$iGJ-gRMp#7$M)$J<+4%TTe*;Qz$m$NJ(yMELL#dPv
z9FydPDsZtV8C?<|a%lt)-6`<N#UjnN#2a7-C9jN$!B|7fk}IlUZjNCkFl@ts7cMLP
zAq(PvtsWj`v<8gm?8mP+K>5<I^z>0nKm=V&9@LOoSY`5I(fLf|e@18-3>}Qt?^Hl~
z7HQ9Imk0`*{MUAUoBFs~Fo9nSK`^ae7hAs*8y0=(!wMyYZ}2`z2C70#C^rWy3tp`_
z)}n!u)=L_!j94`BB}CO!avS3nhD-J`vq)A?zqVJf#dFR?0`pAv=PFyx(VHku7Y-Gj
z#A4_57qVF%tQ@FFufc_J$_?Y)e#a{|K9%P@o41|Y{pt@5uE^@uS4%K|J^o{X7Eo$#
zoLn#dH_k=y^^-}GklzdT=lL8UzYlZ87!(;Q*Ua?GT)bD&eqp7-o#;plfQoH|RK(vb
zxRR8f>XaRTBDKfNBhNyDe(wMyJ2dog?hew+xiFEVDX;2GcY0~m$v`B18kSU*+JpW=
zn?emJCC;>9Kq|E#inc0x_W=L3S{7A#Y@fzBe@P5;53Z&x*-kMqp!7c3cJeoN%A;#j
z?r!?NP;b6(RftxoLXc9W;0rp)2Z3aN#}6Z-53#29pT@8A)!vN`?uO?o`bf|2jHC4v
zoXS%7&(c+#r6U9LY1ycYzc+98{yKLv{=3Qne#hMf>j7CGk~ua@LF|R^4i&CXD1F>?
zSyZ`KxT)Ihw|*HS03vtkq4I$-HS7tX$S$4LhK(rO6P<L%{W3d41vXii0;aF=9;Jpk
z2^m5)(q;LA`5m}X3!33f?k1MYp$;<Jk*+a036F%2Y`LKrzTg$T(f<rXKy~!|-(d*I
zf2e3CJPUpgiV?*DJ8NRVr{IOgpA8vep7&`EnLnRl%qjEtLw_Zb96mNY^<JRQi*Vv5
zx*2R;$AUaRFP3_A;mI*z#WMcm#8T108#qT`%S}Q$i~CL>-NOED0sq~>cZ$IfqWwqz
zt~07*Ta>V*%iuED{}=2E(+Li_(4t-pjK%Fa$TJFcQ7upm$<8(`((*&t7RCrvNt_0{
zmy{cjcmg}#yL0=f)_31+!xsVzH_nL5n4t(5FkGJ`&3WCu@7FQZX)uY6oMF<GQrn<p
ldPca9$P*CBRIw6wOGoexbt<KeYN|M07C!`@&d(iL{uk%VQk(z)
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_decodeOpusTail.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<meta charset="utf-8">
+<head>
+  <title>Regression test to check that opus files don't have a tail at the end.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+// This gets a 1 second Opus file and decodes it to a buffer. The opus file is
+// decoded at 48kHz, and the OfflineAudioContext is also at 48kHz, no resampling
+// is taking place.
+fetch('sweep-300-330-1sec.opus')
+.then(function(response) { return response.arrayBuffer(); })
+.then(function(buffer) {
+  var off = new OfflineAudioContext(1, 128, 48000);
+  off.decodeAudioData(buffer, function(decoded) {
+    var pcm = decoded.getChannelData(0);
+    is(pcm.length, 48000, "The length of the decoded file is correct.");
+    SimpleTest.finish();
+  });
+});
+
+</script>
--- a/dom/media/webrtc/RTCCertificate.cpp
+++ b/dom/media/webrtc/RTCCertificate.cpp
@@ -70,17 +70,17 @@ private:
                                              sizeof(randomName));
     if (rv != SECSuccess) {
       return nullptr;
     }
 
     char buf[sizeof(randomName) * 2 + 4];
     PL_strncpy(buf, "CN=", 3);
     for (size_t i = 0; i < sizeof(randomName); ++i) {
-      snprintf(&buf[i * 2 + 3], 2, "%.2x", randomName[i]);
+      snprintf(&buf[i * 2 + 3], 3, "%.2x", randomName[i]);
     }
     buf[sizeof(buf) - 1] = '\0';
 
     return CERT_AsciiToName(buf);
   }
 
   nsresult GenerateCertificate()
   {
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -388,59 +388,44 @@ MessagePort::Initialize(const nsID& aUUI
 JSObject*
 MessagePort::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MessagePortBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 MessagePort::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-                         const Optional<Sequence<JS::Value>>& aTransferable,
+                         const Sequence<JSObject*>& aTransferable,
                          ErrorResult& aRv)
 {
   // We *must* clone the data here, or the JS::Value could be modified
   // by script
 
-  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
-  if (aTransferable.WasPassed()) {
-    const Sequence<JS::Value>& realTransferable = aTransferable.Value();
-
-    // Here we want to check if the transerable object list contains
-    // this port. No other checks are done.
-    for (const JS::Value& value : realTransferable) {
-      if (!value.isObject()) {
-        continue;
-      }
-
-      MessagePort* port = nullptr;
-      nsresult rv = UNWRAP_OBJECT(MessagePort, &value.toObject(), port);
-      if (NS_FAILED(rv)) {
-        continue;
-      }
-
-      if (port == this) {
-        aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
-        return;
-      }
+  // Here we want to check if the transerable object list contains
+  // this port.
+  for (uint32_t i = 0; i < aTransferable.Length(); ++i) {
+    JSObject* object = aTransferable[i];
+    if (!object) {
+      continue;
     }
 
-    // The input sequence only comes from the generated bindings code, which
-    // ensures it is rooted.
-    JS::HandleValueArray elements =
-      JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
-                                               realTransferable.Elements());
-
-    JSObject* array =
-      JS_NewArrayObject(aCx, elements);
-    if (!array) {
-      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    MessagePort* port = nullptr;
+    nsresult rv = UNWRAP_OBJECT(MessagePort, object, port);
+    if (NS_SUCCEEDED(rv) && port == this) {
+      aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
       return;
     }
+  }
 
-    transferable.setObject(*array);
+  JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
+
+  aRv = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransferable,
+                                                          &transferable);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
   }
 
   RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
 
   UniquePtr<AbstractTimelineMarker> start;
   UniquePtr<AbstractTimelineMarker> end;
   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
   bool isTimelineRecording = timelines && !timelines->IsEmpty();
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -58,17 +58,17 @@ public:
   static void
   ForceClose(const MessagePortIdentifier& aIdentifier);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
-              const Optional<Sequence<JS::Value>>& aTransferable,
+              const Sequence<JSObject*>& aTransferable,
               ErrorResult& aRv);
 
   void Start();
 
   void Close();
 
   EventHandlerNonNull* GetOnmessage();
 
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2458,25 +2458,20 @@ NPError
   if (!url || !*url || !len) {
     return NPERR_INVALID_URL;
   }