Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 19 Dec 2016 16:46:20 -0800
changeset 371427 567894f026558e6dada617a3998f29aed06ac7d8
parent 371403 5182a866ae5ab3558d43c6d348588a1269052b5e (current diff)
parent 371426 636a1e09a0e4f3fac9ce4b893deeab4da5af9367 (diff)
child 371428 a1d5d589332782e527d489d6a031ac838907d14a
child 371459 82f8b26c207261cbd580675f0aca7afea1bb9ee2
child 371557 63a0646e036ebef6ba2521d83f52fadb9deb2512
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly linux32
567894f02655 / 53.0a1 / 20161220030215 / files
nightly linux64
567894f02655 / 53.0a1 / 20161220030215 / files
nightly mac
567894f02655 / 53.0a1 / 20161220030215 / files
nightly win32
567894f02655 / 53.0a1 / 20161220030215 / files
nightly win64
567894f02655 / 53.0a1 / 20161220030215 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge MozReview-Commit-ID: 5Ytu9rQ8iD5
--- a/browser/themes/shared/devedition.inc.css
+++ b/browser/themes/shared/devedition.inc.css
@@ -226,17 +226,16 @@ window:not([chromehidden~="toolbar"]) #u
   margin-top: 0 !important;
   border-top: none !important;
   border-bottom: none !important;
   border-radius: 0 !important;
   box-shadow: 0 -1px var(--chrome-nav-bar-separator-color) !important;
 }
 
 /* No extra vertical padding for nav bar */
-#nav-bar-customization-target,
 #nav-bar {
   padding-top: 0;
   padding-bottom: 0;
 }
 
 /* Use smaller back button icon */
 #back-button {
   -moz-image-region: rect(0, 54px, 18px, 36px);
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -93,17 +93,17 @@
 
           :root[tabsintitlebar] .tab-label:-moz-window-inactive {
             /* Calculated to match the opacity change of Windows Explorer
                titlebar text change for inactive windows. */
             opacity: .6;
           }
         }
 
-        @media not all and (-moz-windows-default-theme) {
+        @media (-moz-windows-default-theme: 0) {
           #main-window {
             background-color: transparent;
           }
         }
 
         #titlebar-buttonbox,
         .titlebar-button {
           -moz-appearance: none !important;
@@ -220,17 +220,17 @@
           #titlebar-close:hover {
             background-color: hsl(355, 86%, 49%);
           }
 
           #titlebar-close:hover:active {
             background-color: hsl(355, 82%, 69%);
           }
         }
-        @media not all and (-moz-windows-default-theme) {
+        @media (-moz-windows-default-theme: 0) {
           .titlebar-button {
             background-color: -moz-field;
           }
           .titlebar-button:hover {
             background-color: Highlight;
           }
 
           #titlebar-min {
@@ -439,17 +439,17 @@
   #main-window[sizemode=normal] .tabbrowser-arrowscrollbox > .arrowscrollbox-scrollbox > .scrollbox-innerbox:not(:-moz-lwtheme) {
     position: relative;
   }
 
   /* End Glass Fog */
 }
 
 /* Aero Basic */
-@media not all and (-moz-windows-compositor) {
+@media (-moz-windows-compositor: 0) {
   @media (-moz-windows-default-theme) {
     #main-window {
       background-color: rgb(185,209,234);
     }
     #main-window:-moz-window-inactive {
       background-color: rgb(215,228,242);
     }
 
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -182,33 +182,33 @@ toolbar:-moz-lwtheme {
 }
 
 #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar)[collapsed=true] {
   min-height: 0.1px;
   max-height: 0;
   transition: min-height 170ms ease-out, max-height 170ms ease-out, visibility 170ms linear;
 }
 
-@media not all and (-moz-windows-compositor),
-       not all and (-moz-windows-default-theme) {
+@media (-moz-windows-compositor: 0),
+       (-moz-windows-default-theme: 0) {
   /* Please keep the menu text colors in this media block in sync with
    * devedition.css, minus the :not(:-moz-lwtheme) condition - see Bug 1165718.
    */
   #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:not(:-moz-lwtheme),
   #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme) {
     color: CaptionText;
   }
 
   #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:not(:-moz-lwtheme):-moz-window-inactive,
   #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:not(:-moz-lwtheme):-moz-window-inactive {
     color: InactiveCaptionText;
   }
 }
 
-@media not all and (-moz-windows-compositor) {
+@media (-moz-windows-compositor: 0) {
   #main-window[tabsintitlebar] #titlebar:-moz-lwtheme {
     visibility: hidden;
   }
 
   #main-window[tabsintitlebar] #titlebar-content:-moz-lwtheme {
     -moz-binding: url("chrome://global/content/bindings/general.xml#windowdragbox");
     visibility: visible;
   }
@@ -378,17 +378,17 @@ toolbar:-moz-lwtheme {
   position: relative;
   z-index: 1;
 }
 
 #nav-bar {
   border-top: 1px solid @toolbarShadowColor@ !important;
   box-shadow: 0 1px 0 @toolbarHighlight@ inset;
 }
-@media not all and (-moz-windows-compositor) {
+@media (-moz-windows-compositor: 0) {
   #TabsToolbar[collapsed="true"] + #nav-bar {
     border-top-style: none !important;
   }
 }
 
 #personal-bookmarks {
   min-height: 24px;
 }
@@ -1609,17 +1609,17 @@ html|span.ac-tag {
 }
 
 html|span.ac-emphasize-text-title,
 html|span.ac-emphasize-text-tag,
 html|span.ac-emphasize-text-url {
   font-weight: 600;
 }
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   .autocomplete-richlistitem[selected=true] {
     background-color: Highlight;
   }
 
   .ac-title {
     color: inherit;
   }
 
@@ -2555,17 +2555,17 @@ notification.pluginVulnerable > .notific
     }
   }
 
   /**
    * This next block targets Aero Basic, which has different positioning for the
    * window caption buttons, and therefore needs to be special-cased.
    */
   @media (-moz-windows-default-theme) {
-    @media not all and (-moz-windows-compositor) {
+    @media (-moz-windows-compositor: 0) {
       #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #titlebar-buttonbox-container > #private-browsing-indicator-titlebar > .private-browsing-indicator {
         background-image: url("chrome://browser/skin/privatebrowsing-mask-titlebar-XPVista7-tall.png");
         height: 28px;
       }
     }
   }
 }
 
--- a/browser/themes/windows/customizableui/panelUI.css
+++ b/browser/themes/windows/customizableui/panelUI.css
@@ -115,17 +115,17 @@ menu.subviewbutton > .menu-right {
   /* Reset the rect we inherit from the button: */
   -moz-image-region: auto;
 }
 
 menu[disabled="true"].subviewbutton > .menu-right {
   list-style-image: url(chrome://browser/skin/customizableui/menu-arrow.svg#arrow-disabled);
 }
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   menu[_moz-menuactive].subviewbutton > .menu-right {
     list-style-image: url(chrome://browser/skin/customizableui/menu-arrow.svg#arrow-hover);
   }
 }
 
 menu.subviewbutton > .menu-right:-moz-locale-dir(rtl) {
   transform: scaleX(-1);
 }
--- a/browser/themes/windows/devedition.css
+++ b/browser/themes/windows/devedition.css
@@ -14,17 +14,17 @@
   --chrome-nav-bar-separator-color: rgba(10, 31, 51, 0.35);
 }
 
 /* The window background is white due to no accentcolor in the lightweight
    theme. It can't be changed to transparent when there is no compositor
    (Win XP or 7 in classic / basic theme), or else dragging and focus become
    broken. So instead just show the normal titlebar in that case, and override
    the window color as transparent when the compositor is available. */
-@media not all and (-moz-windows-compositor) {
+@media (-moz-windows-compositor: 0) {
   #main-window[tabsintitlebar] #titlebar:-moz-lwtheme {
     visibility: visible;
   }
 
   #main-window {
     background: var(--chrome-background-color) !important;
   }
 }
@@ -186,18 +186,18 @@
     background-color: transparent;
   }
 
   #main-window[sizemode="maximized"] #main-menubar > menu:not(:-moz-window-inactive) {
     color: inherit;
   }
 
   /* Use proper menu text styling in Win7 classic mode (copied from browser.css) */
-  @media not all and (-moz-windows-compositor),
-         not all and (-moz-windows-default-theme) {
+  @media (-moz-windows-compositor: 0),
+         (-moz-windows-default-theme: 0) {
     #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar,
     #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar {
       color: CaptionText;
     }
 
     #main-window[tabsintitlebar]:not([inFullscreen]) #toolbar-menubar:-moz-window-inactive,
     #main-window[tabsintitlebar]:not([inFullscreen]) #TabsToolbar:-moz-window-inactive {
       color: InactiveCaptionText;
--- a/browser/themes/windows/preferences/in-content/preferences.css
+++ b/browser/themes/windows/preferences/in-content/preferences.css
@@ -1,15 +1,15 @@
 /* - This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this file,
    - You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../../../shared/incontentprefs/preferences.inc.css
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   #category-general > .category-icon {
     list-style-image: url("chrome://browser/skin/preferences/in-content/icons.svg#general-native");
   }
 
   #category-search > .category-icon {
     list-style-image: url("chrome://browser/skin/preferences/in-content/icons.svg#search-native");
   }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -3565,16 +3565,21 @@ nsresult nsContentUtils::FormatLocalized
                                                const char16_t **aParams,
                                                uint32_t aParamsLength,
                                                nsXPIDLString& aResult)
 {
   nsresult rv = EnsureStringBundle(aFile);
   NS_ENSURE_SUCCESS(rv, rv);
   nsIStringBundle *bundle = sStringBundles[aFile];
 
+  if (!aParams || !aParamsLength) {
+    return bundle->GetStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
+                                     getter_Copies(aResult));
+  }
+
   return bundle->FormatStringFromName(NS_ConvertASCIItoUTF16(aKey).get(),
                                       aParams, aParamsLength,
                                       getter_Copies(aResult));
 }
 
 /* static */
 nsresult nsContentUtils::FormatLocalizedString(
                                           PropertiesFile aFile,
--- a/dom/events/Event.cpp
+++ b/dom/events/Event.cpp
@@ -563,16 +563,44 @@ Event::SetEventType(const nsAString& aEv
     mEvent->mSpecifiedEventType = nullptr;
     mEvent->mMessage = eUnidentifiedEvent;
     mEvent->mSpecifiedEventTypeString = aEventTypeArg;
     mEvent->SetComposed(aEventTypeArg);
   }
   mEvent->SetDefaultComposedInNativeAnonymousContent();
 }
 
+already_AddRefed<EventTarget>
+Event::EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget)
+{
+  nsCOMPtr<EventTarget> relatedTarget = aRelatedTarget;
+  if (relatedTarget) {
+    nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
+    nsCOMPtr<nsIContent> currentTarget =
+      do_QueryInterface(mEvent->mCurrentTarget);
+
+    if (content && content->ChromeOnlyAccess() &&
+        !nsContentUtils::CanAccessNativeAnon()) {
+      content = content->FindFirstNonChromeOnlyAccessContent();
+      relatedTarget = do_QueryInterface(content);
+    }
+
+    nsIContent* shadowRelatedTarget =
+      GetShadowRelatedTarget(currentTarget, content);
+    if (shadowRelatedTarget) {
+      relatedTarget = shadowRelatedTarget;
+    }
+
+    if (relatedTarget) {
+      relatedTarget = relatedTarget->GetTargetForDOMEvent();
+    }
+  }
+  return relatedTarget.forget();
+}
+
 void
 Event::InitEvent(const nsAString& aEventTypeArg,
                  bool aCanBubbleArg,
                  bool aCancelableArg)
 {
   // Make sure this event isn't already being dispatched.
   NS_ENSURE_TRUE_VOID(!mEvent->mFlags.mIsBeingDispatched);
 
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -279,16 +279,19 @@ protected:
     return IsTrusted() && mWantsPopupControlCheck;
   }
 
   void SetComposed(bool aComposed)
   {
     mEvent->SetComposed(aComposed);
   }
 
+  already_AddRefed<EventTarget>
+  EnsureWebAccessibleRelatedTarget(EventTarget* aRelatedTarget);
+
   mozilla::WidgetEvent*       mEvent;
   RefPtr<nsPresContext>     mPresContext;
   nsCOMPtr<EventTarget>       mExplicitOriginalTarget;
   nsCOMPtr<nsIGlobalObject>   mOwner;
   bool                        mEventIsInternal;
   bool                        mPrivateDataDuplicated;
   bool                        mIsMainThreadEvent;
   // True when popup control check should rely on event.type, not
--- a/dom/events/FocusEvent.cpp
+++ b/dom/events/FocusEvent.cpp
@@ -26,24 +26,25 @@ FocusEvent::FocusEvent(EventTarget* aOwn
     mEvent->mTime = PR_Now();
   }
 }
 
 NS_IMETHODIMP
 FocusEvent::GetRelatedTarget(nsIDOMEventTarget** aRelatedTarget)
 {
   NS_ENSURE_ARG_POINTER(aRelatedTarget);
-  NS_IF_ADDREF(*aRelatedTarget = GetRelatedTarget());
+  *aRelatedTarget = GetRelatedTarget().take();
   return NS_OK;
 }
 
-EventTarget*
+already_AddRefed<EventTarget>
 FocusEvent::GetRelatedTarget()
 {
-  return mEvent->AsFocusEvent()->mRelatedTarget;
+  return
+    EnsureWebAccessibleRelatedTarget(mEvent->AsFocusEvent()->mRelatedTarget);
 }
 
 void
 FocusEvent::InitFocusEvent(const nsAString& aType,
                            bool aCanBubble,
                            bool aCancelable,
                            nsGlobalWindow* aView,
                            int32_t aDetail,
--- a/dom/events/FocusEvent.h
+++ b/dom/events/FocusEvent.h
@@ -28,17 +28,17 @@ public:
   {
     return FocusEventBinding::Wrap(aCx, this, aGivenProto);
   }
 
   FocusEvent(EventTarget* aOwner,
              nsPresContext* aPresContext,
              InternalFocusEvent* aEvent);
 
-  EventTarget* GetRelatedTarget();
+  already_AddRefed<EventTarget> GetRelatedTarget();
 
   static already_AddRefed<FocusEvent> Constructor(const GlobalObject& aGlobal,
                                                   const nsAString& aType,
                                                   const FocusEventInit& aParam,
                                                   ErrorResult& aRv);
 protected:
   ~FocusEvent() {}
 
--- a/dom/events/MouseEvent.cpp
+++ b/dom/events/MouseEvent.cpp
@@ -297,37 +297,17 @@ MouseEvent::GetRelatedTarget()
     case eSimpleGestureEventClass:
       relatedTarget =
         do_QueryInterface(mEvent->AsMouseEventBase()->relatedTarget);
       break;
     default:
       break;
   }
 
-  if (relatedTarget) {
-    nsCOMPtr<nsIContent> content = do_QueryInterface(relatedTarget);
-    nsCOMPtr<nsIContent> currentTarget =
-      do_QueryInterface(mEvent->mCurrentTarget);
-
-    nsIContent* shadowRelatedTarget = GetShadowRelatedTarget(currentTarget, content);
-    if (shadowRelatedTarget) {
-      relatedTarget = shadowRelatedTarget;
-    }
-
-    if (content && content->ChromeOnlyAccess() &&
-        !nsContentUtils::CanAccessNativeAnon()) {
-      relatedTarget = do_QueryInterface(content->FindFirstNonChromeOnlyAccessContent());
-    }
-
-    if (relatedTarget) {
-      relatedTarget = relatedTarget->GetTargetForDOMEvent();
-    }
-    return relatedTarget.forget();
-  }
-  return nullptr;
+  return EnsureWebAccessibleRelatedTarget(relatedTarget);
 }
 
 void
 MouseEvent::GetRegion(nsAString& aRegion)
 {
   SetDOMStringToNull(aRegion);
   WidgetMouseEventBase* mouseEventBase = mEvent->AsMouseEventBase();
   if (mouseEventBase) {
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -418,16 +418,17 @@ support-files =
 [test_bug95530.html]
 [test_bug969346.html]
 [test_bug982039.html]
 [test_bug1003539.html]
 [test_bug1045270.html]
 [test_bug1146116.html]
 [test_bug1264157.html]
 [test_bug1287321.html]
+[test_bug1323815.html]
 [test_change_crossorigin.html]
 [test_checked.html]
 [test_dir_attributes_reflection.html]
 [test_dl_attributes_reflection.html]
 [test_element_prototype.html]
 [test_embed_attributes_reflection.html]
 [test_focusshift_button.html]
 [test_formData.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/test_bug1323815.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1323815
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1323815</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1323815 **/
+
+SimpleTest.waitForExplicitFinish();
+function test() {
+  var n = document.getElementById("number");
+  var t = document.getElementById("text");
+  t.focus();
+  var gotBlur = false;
+  t.onblur = function(e) {
+    try {
+      is(e.relatedTarget.localName, "input");
+    } catch(ex) {
+      ok(false, "Accessing properties on the relatedTarget shouldn't throw! " + ex);
+    }
+    gotBlur = true;
+  }
+
+  n.focus();
+  ok(gotBlur);
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(test);
+
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1323815">Mozilla Bug 1323815</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<input type="number" id="number"><input type="text" id="text">
+</body>
+</html>
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -224,16 +224,18 @@ PrefixedImageSmoothingEnabledWarning=Use
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "Service-Worker-Allowed" or "HTTP". %1$S and %2$S are URLs.
 ServiceWorkerScopePathMismatch=Failed to register a ServiceWorker: The path of the provided scope ‘%1$S’ is not under the max scope allowed ‘%2$S’. Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is a URL representing the scope of the ServiceWorker, %2$S is a stringified numeric HTTP status code like "404" and %3$S is a URL.
 ServiceWorkerRegisterNetworkError=Failed to register/update a ServiceWorker for scope ‘%1$S’: Load failed with status %2$S for script ‘%3$S’.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is a URL representing the scope of the ServiceWorker, %2$S is a MIME Media Type like "text/plain" and %3$S is a URL.
 ServiceWorkerRegisterMimeTypeError=Failed to register/update a ServiceWorker for scope ‘%1$S’: Bad Content-Type of ‘%2$S’ received for script ‘%3$S’.  Must be ‘text/javascript’, ‘application/x-javascript’, or ‘application/javascript’.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker". %1$S is a URL representing the scope of the ServiceWorker.
 ServiceWorkerGraceTimeoutTermination=Terminating ServiceWorker for scope ‘%1$S’ with pending waitUntil/respondWith promises because of grace timeout.
+# LOCALIZATION NOTE (ServiceWorkerNoFetchHandler): Do not translate "Fetch".
+ServiceWorkerNoFetchHandler=Fetch event handlers must be added during the worker script’s initial evaluation.
 ExecCommandCutCopyDeniedNotInputDriven=document.execCommand(‘cut’/‘copy’) was denied because it was not called from inside a short running user-generated event handler.
 ManifestShouldBeObject=Manifest should be an object.
 ManifestScopeURLInvalid=The scope URL is invalid.
 ManifestScopeNotSameOrigin=The scope URL must be same origin as document.
 ManifestStartURLOutsideScope=The start URL is outside the scope, so the scope is invalid.
 ManifestStartURLInvalid=The start URL is invalid.
 ManifestStartURLShouldBeSameOrigin=The start URL must be same origin as document.
 # LOCALIZATION NOTE: %1$S is the name of the object whose property is invalid. %2$S is the name of the invalid property. %3$S is the expected type of the property value. E.g. "Expected the manifest's start_url member to be a string."
--- a/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineAdapter.cpp
@@ -113,17 +113,17 @@ WidevineAdapter::GMPGetAPI(const char* a
              &GetCdmHost,
              decryptor));
     if (!cdm) {
       Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p FAILED to create cdm",
           aAPIName, aHostAPI, aPluginAPI, this, aDecryptorId);
       return GMPGenericErr;
     }
     Log("cdm: 0x%x", cdm);
-    RefPtr<CDMWrapper> wrapper(new CDMWrapper(cdm));
+    RefPtr<CDMWrapper> wrapper(new CDMWrapper(cdm, decryptor));
     decryptor->SetCDM(wrapper, aDecryptorId);
     *aPluginAPI = decryptor;
 
   } else if (!strcmp(aAPIName, GMP_API_VIDEO_DECODER)) {
     RefPtr<CDMWrapper> wrapper = WidevineDecryptor::GetInstance(aDecryptorId);
     if (!wrapper) {
       Log("WidevineAdapter::GMPGetAPI(%s, 0x%p, 0x%p, %u) this=0x%p No cdm for video decoder",
           aAPIName, aHostAPI, aPluginAPI, thiss, aDecryptorId);
--- a/dom/media/gmp/widevine-adapter/WidevineUtils.cpp
+++ b/dom/media/gmp/widevine-adapter/WidevineUtils.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WidevineUtils.h"
+#include "WidevineDecryptor.h"
 
 #include "gmp-api/gmp-errors.h"
 #include <stdarg.h>
 #include <stdio.h>
 
 namespace mozilla {
 
 #ifdef ENABLE_WIDEVINE_LOG
@@ -71,9 +72,24 @@ void InitInputBuffer(const GMPEncryptedB
     }
   }
   aInputBuffer.data = aData;
   aInputBuffer.data_size = aDataSize;
   aInputBuffer.subsamples = aSubsamples.Elements();
   aInputBuffer.timestamp = aTimestamp;
 }
 
+CDMWrapper::CDMWrapper(cdm::ContentDecryptionModule_8* aCDM,
+                       WidevineDecryptor* aDecryptor)
+  : mCDM(aCDM)
+  , mDecryptor(aDecryptor)
+{
+  MOZ_ASSERT(mCDM);
+}
+
+CDMWrapper::~CDMWrapper()
+{
+  Log("CDMWrapper destroying CDM=%p", mCDM);
+  mCDM->Destroy();
+  mCDM = nullptr;
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/widevine-adapter/WidevineUtils.h
+++ b/dom/media/gmp/widevine-adapter/WidevineUtils.h
@@ -37,33 +37,29 @@ Log(const char* aFormat, ...);
     Log("ENSURE_GMP_SUCCESS FAILED %s:%d", __FILE__, __LINE__); \
     return rv; \
     } \
 } \
 
 GMPErr
 ToGMPErr(cdm::Status aStatus);
 
+class WidevineDecryptor;
+
 class CDMWrapper {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CDMWrapper)
 
-  explicit CDMWrapper(cdm::ContentDecryptionModule_8* aCDM)
-    : mCDM(aCDM)
-  {
-    MOZ_ASSERT(mCDM);
-  }
+  explicit CDMWrapper(cdm::ContentDecryptionModule_8* aCDM,
+                      WidevineDecryptor* aDecryptor);
   cdm::ContentDecryptionModule_8* GetCDM() const { return mCDM; }
 private:
+  ~CDMWrapper();
   cdm::ContentDecryptionModule_8* mCDM;
-  ~CDMWrapper() {
-    Log("CDMWrapper destroying CDM=%p", mCDM);
-    mCDM->Destroy();
-    mCDM = nullptr;
-  }
+  RefPtr<WidevineDecryptor> mDecryptor;
 };
 
 void InitInputBuffer(const GMPEncryptedBufferMetadata* aCrypto,
                      int64_t aTimestamp,
                      const uint8_t* aData,
                      size_t aDataSize,
                      cdm::InputBuffer &aInputBuffer,
                      nsTArray<cdm::SubsampleEntry> &aSubsamples);
--- a/dom/workers/ServiceWorkerInfo.cpp
+++ b/dom/workers/ServiceWorkerInfo.cpp
@@ -175,16 +175,17 @@ ServiceWorkerInfo::ServiceWorkerInfo(nsI
   : mPrincipal(aPrincipal)
   , mScope(aScope)
   , mScriptSpec(aScriptSpec)
   , mCacheName(aCacheName)
   , mState(ServiceWorkerState::EndGuard_)
   , mServiceWorkerID(GetNextID())
   , mServiceWorkerPrivate(new ServiceWorkerPrivate(this))
   , mSkipWaitingFlag(false)
+  , mHandlesFetch(Unknown)
 {
   MOZ_ASSERT(mPrincipal);
   // cache origin attributes so we can use them off main thread
   mOriginAttributes = BasePrincipal::Cast(mPrincipal)->OriginAttributesRef();
   MOZ_ASSERT(!mScope.IsEmpty());
   MOZ_ASSERT(!mScriptSpec.IsEmpty());
   MOZ_ASSERT(!mCacheName.IsEmpty());
 }
--- a/dom/workers/ServiceWorkerInfo.h
+++ b/dom/workers/ServiceWorkerInfo.h
@@ -41,16 +41,22 @@ private:
   // addition and removal.
   // There is a high chance of there being at least one ServiceWorker
   // associated with this all the time.
   AutoTArray<ServiceWorker*, 1> mInstances;
 
   RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
   bool mSkipWaitingFlag;
 
+  enum {
+    Unknown,
+    Enabled,
+    Disabled
+  } mHandlesFetch;
+
   ~ServiceWorkerInfo();
 
   // Generates a unique id for the service worker, with zero being treated as
   // invalid.
   uint64_t
   GetNextID() const;
 
 public:
@@ -130,16 +136,32 @@ public:
   void
   SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState)
   {
     AssertIsOnMainThread();
     mState = aState;
   }
 
   void
+  SetHandlesFetch(bool aHandlesFetch)
+  {
+    AssertIsOnMainThread();
+    MOZ_DIAGNOSTIC_ASSERT(mHandlesFetch == Unknown);
+    mHandlesFetch = aHandlesFetch ? Enabled : Disabled;
+  }
+
+  bool
+  HandlesFetch() const
+  {
+    AssertIsOnMainThread();
+    MOZ_DIAGNOSTIC_ASSERT(mHandlesFetch != Unknown);
+    return mHandlesFetch != Disabled;
+  }
+
+  void
   AppendWorker(ServiceWorker* aWorker);
 
   void
   RemoveWorker(ServiceWorker* aWorker);
 
   already_AddRefed<ServiceWorker>
   GetOrCreateInstance(nsPIDOMWindowInner* aWindow);
 };
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -179,16 +179,17 @@ PopulateRegistrationData(nsIPrincipal* a
   RefPtr<ServiceWorkerInfo> newest = aRegistration->Newest();
   if (NS_WARN_IF(!newest)) {
     return NS_ERROR_FAILURE;
   }
 
   if (aRegistration->GetActive()) {
     aData.currentWorkerURL() = aRegistration->GetActive()->ScriptSpec();
     aData.cacheName() = aRegistration->GetActive()->CacheName();
+    aData.currentWorkerHandlesFetch() = aRegistration->GetActive()->HandlesFetch();
   }
 
   return NS_OK;
 }
 
 class TeardownRunnable final : public Runnable
 {
 public:
@@ -1692,16 +1693,18 @@ ServiceWorkerManager::LoadRegistration(
     }
   }
 
   const nsCString& currentWorkerURL = aRegistration.currentWorkerURL();
   if (!currentWorkerURL.IsEmpty()) {
     registration->SetActive(
       new ServiceWorkerInfo(registration->mPrincipal, registration->mScope,
                             currentWorkerURL, aRegistration.cacheName()));
+
+    registration->GetActive()->SetHandlesFetch(aRegistration.currentWorkerHandlesFetch());
     registration->GetActive()->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
   }
 }
 
 void
 ServiceWorkerManager::LoadRegistrations(
                   const nsTArray<ServiceWorkerRegistrationData>& aRegistrations)
 {
--- a/dom/workers/ServiceWorkerPrivate.cpp
+++ b/dom/workers/ServiceWorkerPrivate.cpp
@@ -102,81 +102,101 @@ ServiceWorkerPrivate::~ServiceWorkerPriv
 
   mIdleWorkerTimer->Cancel();
 }
 
 namespace {
 
 class CheckScriptEvaluationWithCallback final : public WorkerRunnable
 {
+  nsMainThreadPtrHandle<ServiceWorkerPrivate> mServiceWorkerPrivate;
   nsMainThreadPtrHandle<KeepAliveToken> mKeepAliveToken;
-  RefPtr<LifeCycleEventCallback> mCallback;
+
+  // The script evaluation result must be reported even if the runnable
+  // is cancelled.
+  RefPtr<LifeCycleEventCallback> mScriptEvaluationCallback;
+
 #ifdef DEBUG
   bool mDone;
 #endif
 
 public:
   CheckScriptEvaluationWithCallback(WorkerPrivate* aWorkerPrivate,
+                                    ServiceWorkerPrivate* aServiceWorkerPrivate,
                                     KeepAliveToken* aKeepAliveToken,
-                                    LifeCycleEventCallback* aCallback)
+                                    LifeCycleEventCallback* aScriptEvaluationCallback)
     : WorkerRunnable(aWorkerPrivate)
+    , mServiceWorkerPrivate(new nsMainThreadPtrHolder<ServiceWorkerPrivate>(aServiceWorkerPrivate))
     , mKeepAliveToken(new nsMainThreadPtrHolder<KeepAliveToken>(aKeepAliveToken))
-    , mCallback(aCallback)
+    , mScriptEvaluationCallback(aScriptEvaluationCallback)
 #ifdef DEBUG
     , mDone(false)
 #endif
   {
     AssertIsOnMainThread();
   }
 
   ~CheckScriptEvaluationWithCallback()
   {
     MOZ_ASSERT(mDone);
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     aWorkerPrivate->AssertIsOnWorkerThread();
-    Done(aWorkerPrivate->WorkerScriptExecutedSuccessfully());
+
+    bool fetchHandlerWasAdded = aWorkerPrivate->FetchHandlerWasAdded();
+    nsCOMPtr<nsIRunnable> runnable = NewRunnableMethod<bool>(this,
+      &CheckScriptEvaluationWithCallback::ReportFetchFlag, fetchHandlerWasAdded);
+    aWorkerPrivate->DispatchToMainThread(runnable.forget());
+
+    ReportScriptEvaluationResult(aWorkerPrivate->WorkerScriptExecutedSuccessfully());
 
     return true;
   }
 
+  void
+  ReportFetchFlag(bool aFetchHandlerWasAdded)
+  {
+    AssertIsOnMainThread();
+    mServiceWorkerPrivate->SetHandlesFetch(aFetchHandlerWasAdded);
+  }
+
   nsresult
   Cancel() override
   {
-    Done(false);
+    ReportScriptEvaluationResult(false);
     return WorkerRunnable::Cancel();
   }
 
 private:
   void
-  Done(bool aResult)
+  ReportScriptEvaluationResult(bool aScriptEvaluationResult)
   {
 #ifdef DEBUG
     mDone = true;
 #endif
-    mCallback->SetResult(aResult);
-    MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mCallback));
+    mScriptEvaluationCallback->SetResult(aScriptEvaluationResult);
+    MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(mScriptEvaluationCallback));
   }
 };
 
 } // anonymous namespace
 
 nsresult
-ServiceWorkerPrivate::CheckScriptEvaluation(LifeCycleEventCallback* aCallback)
+ServiceWorkerPrivate::CheckScriptEvaluation(LifeCycleEventCallback* aScriptEvaluationCallback)
 {
   nsresult rv = SpawnWorkerIfNeeded(LifeCycleEvent, nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
   RefPtr<WorkerRunnable> r = new CheckScriptEvaluationWithCallback(mWorkerPrivate,
-                                                                   token,
-                                                                   aCallback);
+                                                                   this, token,
+                                                                   aScriptEvaluationCallback);
   if (NS_WARN_IF(!r->Dispatch())) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 namespace {
@@ -1677,37 +1697,49 @@ NS_IMPL_ISUPPORTS_INHERITED(FetchEventRu
 nsresult
 ServiceWorkerPrivate::SendFetchEvent(nsIInterceptedChannel* aChannel,
                                      nsILoadGroup* aLoadGroup,
                                      const nsAString& aDocumentId,
                                      bool aIsReload)
 {
   AssertIsOnMainThread();
 
+  if (NS_WARN_IF(!mInfo)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  MOZ_ASSERT(swm);
+
+  RefPtr<ServiceWorkerRegistrationInfo> registration =
+    swm->GetRegistration(mInfo->GetPrincipal(), mInfo->Scope());
+
+  // Handle Fetch algorithm - step 16. If the service worker didn't register
+  // any fetch event handlers, then abort the interception and maybe trigger
+  // the soft update algorithm.
+  if (!mInfo->HandlesFetch()) {
+    aChannel->ResetInterception();
+
+    // Trigger soft updates if necessary.
+    registration->MaybeScheduleTimeCheckAndUpdate();
+
+    return NS_OK;
+  }
+
   // if the ServiceWorker script fails to load for some reason, just resume
   // the original channel.
   nsCOMPtr<nsIRunnable> failRunnable =
     NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
 
   nsresult rv = SpawnWorkerIfNeeded(FetchEvent, failRunnable, aLoadGroup);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsMainThreadPtrHandle<nsIInterceptedChannel> handle(
     new nsMainThreadPtrHolder<nsIInterceptedChannel>(aChannel, false));
 
-  if (NS_WARN_IF(!mInfo)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  MOZ_ASSERT(swm);
-
-  RefPtr<ServiceWorkerRegistrationInfo> registration =
-    swm->GetRegistration(mInfo->GetPrincipal(), mInfo->Scope());
-
   nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> regInfo(
     new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(registration, false));
 
   RefPtr<KeepAliveToken> token = CreateEventKeepAliveToken();
 
   RefPtr<FetchEventRunnable> r =
     new FetchEventRunnable(mWorkerPrivate, token, handle,
                            mInfo->ScriptSpec(), regInfo,
@@ -2115,9 +2147,21 @@ ServiceWorkerPrivate::Observe(nsISupport
     RefPtr<Runnable> runnable = pendingWindows[i];
     MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
     pendingWindows.RemoveElementAt(i);
   }
 
   return NS_OK;
 }
 
+void
+ServiceWorkerPrivate::SetHandlesFetch(bool aValue)
+{
+  AssertIsOnMainThread();
+
+  if (NS_WARN_IF(!mInfo)) {
+    return;
+  }
+
+  mInfo->SetHandlesFetch(aValue);
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerPrivate.h
+++ b/dom/workers/ServiceWorkerPrivate.h
@@ -148,16 +148,19 @@ public:
   DetachDebugger();
 
   bool
   IsIdle() const;
 
   void
   AddPendingWindow(Runnable* aPendingWindow);
 
+  void
+  SetHandlesFetch(bool aValue);
+
 private:
   enum WakeUpReason {
     FetchEvent = 0,
     PushEvent,
     PushSubscriptionChangeEvent,
     MessageEvent,
     NotificationClickEvent,
     NotificationCloseEvent,
--- a/dom/workers/ServiceWorkerRegistrar.cpp
+++ b/dom/workers/ServiceWorkerRegistrar.cpp
@@ -36,16 +36,17 @@ using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 static const char* gSupportedRegistrarVersions[] = {
   SERVICEWORKERREGISTRAR_VERSION,
+  "4",
   "3",
   "2"
 };
 
 StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
 
 } // namespace
 
@@ -346,16 +347,50 @@ ServiceWorkerRegistrar::ReadData()
 
       GET_LINE(entry->scope());
 
       entry->principal() =
         mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
 
       GET_LINE(entry->currentWorkerURL());
 
+      nsAutoCString fetchFlag;
+      GET_LINE(fetchFlag);
+      if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
+          !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
+        return NS_ERROR_INVALID_ARG;
+      }
+      entry->currentWorkerHandlesFetch() =
+        fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
+
+      nsAutoCString cacheName;
+      GET_LINE(cacheName);
+      CopyUTF8toUTF16(cacheName, entry->cacheName());
+    } else if (version.EqualsLiteral("4")) {
+      overwrite = true;
+      dedupe = true;
+
+      nsAutoCString suffix;
+      GET_LINE(suffix);
+
+      PrincipalOriginAttributes attrs;
+      if (!attrs.PopulateFromSuffix(suffix)) {
+        return NS_ERROR_INVALID_ARG;
+      }
+
+      GET_LINE(entry->scope());
+
+      entry->principal() =
+        mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
+
+      GET_LINE(entry->currentWorkerURL());
+
+      // default handlesFetch flag to Enabled
+      entry->currentWorkerHandlesFetch() = true;
+
       nsAutoCString cacheName;
       GET_LINE(cacheName);
       CopyUTF8toUTF16(cacheName, entry->cacheName());
     } else if (version.EqualsLiteral("3")) {
       overwrite = true;
       dedupe = true;
 
       nsAutoCString suffix;
@@ -371,16 +406,19 @@ ServiceWorkerRegistrar::ReadData()
 
       GET_LINE(entry->scope());
 
       entry->principal() =
         mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
 
       GET_LINE(entry->currentWorkerURL());
 
+      // default handlesFetch flag to Enabled
+      entry->currentWorkerHandlesFetch() = true;
+
       nsAutoCString cacheName;
       GET_LINE(cacheName);
       CopyUTF8toUTF16(cacheName, entry->cacheName());
     } else if (version.EqualsLiteral("2")) {
       overwrite = true;
       dedupe = true;
 
       nsAutoCString suffix;
@@ -399,16 +437,19 @@ ServiceWorkerRegistrar::ReadData()
       entry->principal() =
         mozilla::ipc::ContentPrincipalInfo(attrs, entry->scope());
 
       // scriptSpec is no more used in latest version.
       GET_LINE(unused);
 
       GET_LINE(entry->currentWorkerURL());
 
+      // default handlesFetch flag to Enabled
+      entry->currentWorkerHandlesFetch() = true;
+
       nsAutoCString cacheName;
       GET_LINE(cacheName);
       CopyUTF8toUTF16(cacheName, entry->cacheName());
 
       // waitingCacheName is no more used in latest version.
       GET_LINE(unused);
     } else {
       MOZ_ASSERT_UNREACHABLE("Should never get here!");
@@ -694,16 +735,20 @@ ServiceWorkerRegistrar::WriteData()
     buffer.Append('\n');
 
     buffer.Append(data[i].scope());
     buffer.Append('\n');
 
     buffer.Append(data[i].currentWorkerURL());
     buffer.Append('\n');
 
+    buffer.Append(data[i].currentWorkerHandlesFetch() ?
+                    SERVICEWORKERREGISTRAR_TRUE : SERVICEWORKERREGISTRAR_FALSE);
+    buffer.Append('\n');
+
     buffer.Append(NS_ConvertUTF16toUTF8(data[i].cacheName()));
     buffer.Append('\n');
 
     buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
     buffer.Append('\n');
 
     rv = stream->Write(buffer.Data(), buffer.Length(), &count);
     if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/workers/ServiceWorkerRegistrar.h
+++ b/dom/workers/ServiceWorkerRegistrar.h
@@ -11,17 +11,17 @@
 #include "mozilla/Telemetry.h"
 #include "nsClassHashtable.h"
 #include "nsIObserver.h"
 #include "nsCOMPtr.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 #define SERVICEWORKERREGISTRAR_FILE "serviceworker.txt"
-#define SERVICEWORKERREGISTRAR_VERSION "4"
+#define SERVICEWORKERREGISTRAR_VERSION "5"
 #define SERVICEWORKERREGISTRAR_TERMINATOR "#"
 #define SERVICEWORKERREGISTRAR_TRUE "true"
 #define SERVICEWORKERREGISTRAR_FALSE "false"
 
 class nsIFile;
 
 namespace mozilla {
 
--- a/dom/workers/ServiceWorkerRegistrarTypes.ipdlh
+++ b/dom/workers/ServiceWorkerRegistrarTypes.ipdlh
@@ -8,16 +8,17 @@ include PBackgroundSharedTypes;
 
 namespace mozilla {
 namespace dom {
 
 struct ServiceWorkerRegistrationData
 {
   nsCString scope;
   nsCString currentWorkerURL;
+  bool currentWorkerHandlesFetch;
 
   nsString cacheName;
 
   PrincipalInfo principal;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -4014,16 +4014,17 @@ WorkerPrivate::WorkerPrivate(WorkerPriva
   , mFrozen(false)
   , mTimerRunning(false)
   , mRunningExpiredTimeouts(false)
   , mPendingEventQueueClearing(false)
   , mCancelAllPendingRunnables(false)
   , mPeriodicGCTimerRunning(false)
   , mIdleGCTimerRunning(false)
   , mWorkerScriptExecutedSuccessfully(false)
+  , mFetchHandlerWasAdded(false)
   , mOnLine(false)
 {
   MOZ_ASSERT_IF(!IsDedicatedWorker(), !aWorkerName.IsVoid());
   MOZ_ASSERT_IF(IsDedicatedWorker(), aWorkerName.IsEmpty());
 
   if (aParent) {
     aParent->AssertIsOnWorkerThread();
     aParent->GetAllPreferences(mPreferences);
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -968,16 +968,17 @@ class WorkerPrivate : public WorkerPriva
   bool mFrozen;
   bool mTimerRunning;
   bool mRunningExpiredTimeouts;
   bool mPendingEventQueueClearing;
   bool mCancelAllPendingRunnables;
   bool mPeriodicGCTimerRunning;
   bool mIdleGCTimerRunning;
   bool mWorkerScriptExecutedSuccessfully;
+  bool mFetchHandlerWasAdded;
   bool mPreferences[WORKERPREF_COUNT];
   bool mOnLine;
 
 protected:
   ~WorkerPrivate();
 
 public:
   static already_AddRefed<WorkerPrivate>
@@ -1206,16 +1207,32 @@ public:
   CycleCollectInternal(bool aCollectChildren);
 
   void
   OfflineStatusChangeEventInternal(bool aIsOffline);
 
   void
   MemoryPressureInternal();
 
+  void
+  SetFetchHandlerWasAdded()
+  {
+    MOZ_ASSERT(IsServiceWorker());
+    AssertIsOnWorkerThread();
+    mFetchHandlerWasAdded = true;
+  }
+
+  bool
+  FetchHandlerWasAdded() const
+  {
+    MOZ_ASSERT(IsServiceWorker());
+    AssertIsOnWorkerThread();
+    return mFetchHandlerWasAdded;
+  }
+
   JSContext*
   GetJSContext() const
   {
     AssertIsOnWorkerThread();
     return mJSContext;
   }
 
   WorkerGlobalScope*
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -606,16 +606,99 @@ ServiceWorkerGlobalScope::Registration()
   if (!mRegistration) {
     mRegistration =
       ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, mScope);
   }
 
   return mRegistration;
 }
 
+EventHandlerNonNull*
+ServiceWorkerGlobalScope::GetOnfetch()
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  return GetEventHandler(nullptr, NS_LITERAL_STRING("fetch"));
+}
+
+namespace {
+
+class ReportFetchListenerWarningRunnable final : public Runnable
+{
+  const nsCString mScope;
+  nsCString mSourceSpec;
+  uint32_t mLine;
+  uint32_t mColumn;
+
+public:
+  explicit ReportFetchListenerWarningRunnable(const nsString& aScope)
+    : mScope(NS_ConvertUTF16toUTF8(aScope))
+  {
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    JSContext* cx = workerPrivate->GetJSContext();
+    MOZ_ASSERT(cx);
+
+    nsJSUtils::GetCallingLocation(cx, mSourceSpec, &mLine, &mColumn);
+  }
+
+  NS_IMETHOD
+  Run() override
+  {
+    AssertIsOnMainThread();
+
+    ServiceWorkerManager::LocalizeAndReportToAllClients(mScope, "ServiceWorkerNoFetchHandler",
+        nsTArray<nsString>{}, nsIScriptError::warningFlag, NS_ConvertUTF8toUTF16(mSourceSpec),
+        EmptyString(), mLine, mColumn);
+
+    return NS_OK;
+  }
+};
+
+} // anonymous namespace
+
+void
+ServiceWorkerGlobalScope::SetOnfetch(mozilla::dom::EventHandlerNonNull* aCallback)
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  if (aCallback) {
+    if (mWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
+      RefPtr<Runnable> r = new ReportFetchListenerWarningRunnable(mScope);
+      mWorkerPrivate->DispatchToMainThread(r.forget());
+    }
+    mWorkerPrivate->SetFetchHandlerWasAdded();
+  }
+  SetEventHandler(nullptr, NS_LITERAL_STRING("fetch"), aCallback);
+}
+
+void
+ServiceWorkerGlobalScope::AddEventListener(
+                          const nsAString& aType,
+                          dom::EventListener* aListener,
+                          const dom::AddEventListenerOptionsOrBoolean& aOptions,
+                          const dom::Nullable<bool>& aWantsUntrusted,
+                          ErrorResult& aRv)
+{
+  MOZ_ASSERT(mWorkerPrivate);
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  if (mWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
+    RefPtr<Runnable> r = new ReportFetchListenerWarningRunnable(mScope);
+    mWorkerPrivate->DispatchToMainThread(r.forget());
+  }
+  DOMEventTargetHelper::AddEventListener(aType, aListener, aOptions,
+                                         aWantsUntrusted, aRv);
+  if (!aRv.Failed()) {
+    mWorkerPrivate->SetFetchHandlerWasAdded();
+  }
+}
+
 namespace {
 
 class SkipWaitingResultRunnable final : public WorkerRunnable
 {
   RefPtr<PromiseWorkerProxy> mPromiseProxy;
 
 public:
   SkipWaitingResultRunnable(WorkerPrivate* aWorkerPrivate,
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -276,23 +276,35 @@ public:
 
   ServiceWorkerRegistration*
   Registration();
 
   already_AddRefed<Promise>
   SkipWaiting(ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(activate)
-  IMPL_EVENT_HANDLER(fetch)
   IMPL_EVENT_HANDLER(install)
   IMPL_EVENT_HANDLER(message)
 
   IMPL_EVENT_HANDLER(push)
   IMPL_EVENT_HANDLER(pushsubscriptionchange)
 
+  EventHandlerNonNull*
+  GetOnfetch();
+
+  void
+  SetOnfetch(mozilla::dom::EventHandlerNonNull* aCallback);
+
+  using DOMEventTargetHelper::AddEventListener;
+  virtual void
+  AddEventListener(const nsAString& aType,
+                   dom::EventListener* aListener,
+                   const dom::AddEventListenerOptionsOrBoolean& aOptions,
+                   const dom::Nullable<bool>& aWantsUntrusted,
+                   ErrorResult& aRv) override;
 };
 
 class WorkerDebuggerGlobalScope final : public DOMEventTargetHelper,
                                         public nsIGlobalObject
 {
   typedef mozilla::dom::workers::WorkerPrivate WorkerPrivate;
 
   WorkerPrivate* mWorkerPrivate;
--- a/dom/workers/test/gtest/TestReadWrite.cpp
+++ b/dom/workers/test/gtest/TestReadWrite.cpp
@@ -143,21 +143,25 @@ TEST(ServiceWorkerRegistrar, TestWrongVe
   ASSERT_EQ((uint32_t)0, data.Length()) << "No data should be found in an empty file";
 }
 
 TEST(ServiceWorkerRegistrar, TestReadData)
 {
   nsAutoCString buffer(SERVICEWORKERREGISTRAR_VERSION "\n");
 
   buffer.Append("^appId=123&inBrowser=1\n");
-  buffer.Append("scope 0\ncurrentWorkerURL 0\ncacheName 0\n");
+  buffer.Append("scope 0\ncurrentWorkerURL 0\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TRUE "\n");
+  buffer.Append("cacheName 0\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
 
   buffer.Append("\n");
-  buffer.Append("scope 1\ncurrentWorkerURL 1\ncacheName 1\n");
+  buffer.Append("scope 1\ncurrentWorkerURL 1\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_FALSE "\n");
+  buffer.Append("cacheName 1\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
 
   ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail";
 
   RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
 
   nsresult rv = swr->TestReadData();
   ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
@@ -171,29 +175,31 @@ TEST(ServiceWorkerRegistrar, TestReadDat
 
   nsAutoCString suffix0;
   cInfo0.attrs().CreateSuffix(suffix0);
 
   ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
   ASSERT_STREQ("scope 0", cInfo0.spec().get());
   ASSERT_STREQ("scope 0", data[0].scope().get());
   ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
+  ASSERT_TRUE(data[0].currentWorkerHandlesFetch());
   ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get());
 
   const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
   ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
   const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
 
   nsAutoCString suffix1;
   cInfo1.attrs().CreateSuffix(suffix1);
 
   ASSERT_STREQ("", suffix1.get());
   ASSERT_STREQ("scope 1", cInfo1.spec().get());
   ASSERT_STREQ("scope 1", data[1].scope().get());
   ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
+  ASSERT_FALSE(data[1].currentWorkerHandlesFetch());
   ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get());
 }
 
 TEST(ServiceWorkerRegistrar, TestDeleteData)
 {
   ASSERT_TRUE(CreateFile(NS_LITERAL_CSTRING("Foobar"))) << "CreateFile should not fail";
 
   RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
@@ -365,16 +371,69 @@ TEST(ServiceWorkerRegistrar, TestVersion
 
   ASSERT_STREQ("", suffix1.get());
   ASSERT_STREQ("scope 1", cInfo1.spec().get());
   ASSERT_STREQ("scope 1", data[1].scope().get());
   ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
   ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get());
 }
 
+TEST(ServiceWorkerRegistrar, TestVersion4Migration)
+{
+  nsAutoCString buffer("4" "\n");
+
+  buffer.Append("^appId=123&inBrowser=1\n");
+  buffer.Append("scope 0\ncurrentWorkerURL 0\ncacheName 0\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  buffer.Append("\n");
+  buffer.Append("scope 1\ncurrentWorkerURL 1\ncacheName 1\n");
+  buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
+
+  ASSERT_TRUE(CreateFile(buffer)) << "CreateFile should not fail";
+
+  RefPtr<ServiceWorkerRegistrarTest> swr = new ServiceWorkerRegistrarTest;
+
+  nsresult rv = swr->TestReadData();
+  ASSERT_EQ(NS_OK, rv) << "ReadData() should not fail";
+
+  const nsTArray<ServiceWorkerRegistrationData>& data = swr->TestGetData();
+  ASSERT_EQ((uint32_t)2, data.Length()) << "2 entries should be found";
+
+  const mozilla::ipc::PrincipalInfo& info0 = data[0].principal();
+  ASSERT_EQ(info0.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo0 = data[0].principal();
+
+  nsAutoCString suffix0;
+  cInfo0.attrs().CreateSuffix(suffix0);
+
+  ASSERT_STREQ("^appId=123&inBrowser=1", suffix0.get());
+  ASSERT_STREQ("scope 0", cInfo0.spec().get());
+  ASSERT_STREQ("scope 0", data[0].scope().get());
+  ASSERT_STREQ("currentWorkerURL 0", data[0].currentWorkerURL().get());
+  // default is true
+  ASSERT_EQ(true, data[1].currentWorkerHandlesFetch());
+  ASSERT_STREQ("cacheName 0", NS_ConvertUTF16toUTF8(data[0].cacheName()).get());
+
+  const mozilla::ipc::PrincipalInfo& info1 = data[1].principal();
+  ASSERT_EQ(info1.type(), mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) << "First principal must be content";
+  const mozilla::ipc::ContentPrincipalInfo& cInfo1 = data[1].principal();
+
+  nsAutoCString suffix1;
+  cInfo1.attrs().CreateSuffix(suffix1);
+
+  ASSERT_STREQ("", suffix1.get());
+  ASSERT_STREQ("scope 1", cInfo1.spec().get());
+  ASSERT_STREQ("scope 1", data[1].scope().get());
+  ASSERT_STREQ("currentWorkerURL 1", data[1].currentWorkerURL().get());
+  // default is true
+  ASSERT_EQ(true, data[1].currentWorkerHandlesFetch());
+  ASSERT_STREQ("cacheName 1", NS_ConvertUTF16toUTF8(data[1].cacheName()).get());
+}
+
 TEST(ServiceWorkerRegistrar, TestDedupeRead)
 {
   nsAutoCString buffer("3" "\n");
 
   // unique entries
   buffer.Append("^appId=123&inBrowser=1\n");
   buffer.Append("spec 0\nscope 0\ncurrentWorkerURL 0\ncacheName 0\n");
   buffer.Append(SERVICEWORKERREGISTRAR_TERMINATOR "\n");
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -209,16 +209,17 @@ support-files =
   sw_bad_mime_type.js^headers^
   error_reporting_helpers.js
   fetch.js
   hello.html
   create_another_sharedWorker.html
   sharedWorker_fetch.js
   async_waituntil_worker.js
   lazy_worker.js
+  nofetch_handler_worker.js
 
 [test_bug1151916.html]
 [test_bug1240436.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
 [test_close.html]
 [test_controller.html]
@@ -314,8 +315,9 @@ tags = openwindow
 [test_unregister.html]
 [test_unresolved_fetch_interception.html]
 [test_workerUnregister.html]
 [test_workerUpdate.html]
 [test_workerupdatefoundevent.html]
 [test_xslt.html]
 [test_async_waituntil.html]
 [test_worker_reference_gc_timeout.html]
+[test_nofetch_handler.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/nofetch_handler_worker.js
@@ -0,0 +1,8 @@
+function handleFetch(event) {
+  event.respondWith(new Response('intercepted'));
+}
+
+self.oninstall = function(event) {
+      addEventListener('fetch', handleFetch);
+      self.onfetch = handleFetch;
+}
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_nofetch_handler.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1181127</title>
+  <script src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script src="error_reporting_helpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
+</head>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1181127">Mozilla Bug 1181127</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+
+<script class="testbody" type="text/javascript">
+
+add_task(function setupPrefs() {
+  return SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true],
+    // Make sure the event handler during the install event persists. This ensures
+    // the reason for which the interception doesn't occur is because of the
+    // handlesFetch=false flag from ServiceWorkerInfo.
+    ["dom.serviceWorkers.idle_timeout", 299999],
+  ]});
+});
+
+var iframeg;
+function create_iframe(url) {
+  return new Promise(function(res) {
+    iframe = document.createElement('iframe');
+    iframe.src = url;
+    iframe.onload = function() { res(iframe) }
+    document.body.appendChild(iframe);
+    iframeg = iframe;
+  })
+}
+
+add_task(function* test_nofetch_worker() {
+  let registration = yield navigator.serviceWorker.register(
+    "nofetch_handler_worker.js", { scope: "./nofetch_handler_worker/"} )
+    .then(function(registration) {
+      var worker = registration.installing;
+      return new Promise(function(resolve) {
+        worker.addEventListener('statechange', function() {
+          if (worker.state === 'activated') {
+            resolve(registration);
+          }
+        });
+      });
+    });
+
+  let iframe = yield create_iframe("./nofetch_handler_worker/doesnt_exist.html");
+  ok(!iframe.contentDocument.body.innerHTML.includes("intercepted"), "Request was not intercepted.");
+
+  yield SpecialPowers.popPrefEnv();
+  yield registration.unregister();
+});
+</script>
+</body>
+</html>
--- a/ipc/glue/CrashReporterHost.cpp
+++ b/ipc/glue/CrashReporterHost.cpp
@@ -89,17 +89,17 @@ public:
     , mChildDumpID(aChildDumpID)
     , mName(NS_LITERAL_STRING("Crash reporter: blocking on minidump analysis"))
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     nsresult rv = GetShutdownBarrier()->AddBlocker(
       this, NS_LITERAL_STRING(__FILE__), __LINE__,
       NS_LITERAL_STRING("Minidump analysis"));
-    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+    Unused << NS_WARN_IF(NS_FAILED(rv));
   }
 
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     if (mProcessType == nsICrashService::PROCESS_TYPE_CONTENT) {
       CrashReporter::RunMinidumpAnalyzer(mChildDumpID);
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4513,16 +4513,22 @@ nsIPresShell::RestyleForCSSRuleChanges()
   }
 
   if (!root) {
     // No content to restyle
     return;
   }
 
   RestyleManagerHandle restyleManager = mPresContext->RestyleManager();
+
+  if (mStyleSet->IsServo()) {
+    // Tell Servo that the contents of style sheets have changed.
+    mStyleSet->AsServo()->NoteStyleSheetsChanged();
+  }
+
   if (scopeRoots.IsEmpty()) {
     // If scopeRoots is empty, we know that mStylesHaveChanged was true at
     // the beginning of this function, and that we need to restyle the whole
     // document.
     restyleManager->PostRestyleEvent(root, eRestyle_Subtree,
                                      nsChangeHint(0));
   } else {
     for (Element* scopeRoot : scopeRoots) {
@@ -4545,17 +4551,16 @@ PresShell::RecordStyleSheetChange(StyleS
     // XXXheycam ServoStyleSheets don't support <style scoped> yet.
     Element* scopeElement = aStyleSheet->AsGecko()->GetScopeElement();
     if (scopeElement) {
       mChangedScopeStyleRoots.AppendElement(scopeElement);
       return;
     }
   } else {
     NS_WARNING("stylo: ServoStyleSheets don't support <style scoped>");
-    return;
   }
 
   mStylesHaveChanged = true;
 }
 
 void
 PresShell::StyleSheetAdded(StyleSheet* aStyleSheet,
                            bool aDocumentSheet)
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -54,16 +54,26 @@ ServoRestyleManager::PostRestyleEvent(El
 
   // XXX This is a temporary hack to make style attribute change works.
   //     In the future, we should be able to use this hint directly.
   if (aRestyleHint & eRestyle_StyleAttribute) {
     aRestyleHint &= ~eRestyle_StyleAttribute;
     aRestyleHint |= eRestyle_Self | eRestyle_Subtree;
   }
 
+  // XXX For now, convert eRestyle_Subtree into (eRestyle_Self |
+  // eRestyle_SomeDescendants), which Servo will interpret as
+  // RESTYLE_SELF | RESTYLE_DESCENDANTS, since this is a commonly
+  // posted restyle hint that doesn't yet align with RestyleHint's
+  // bits.
+  if (aRestyleHint & eRestyle_Subtree) {
+    aRestyleHint &= ~eRestyle_Subtree;
+    aRestyleHint |= eRestyle_Self | eRestyle_SomeDescendants;
+  }
+
   if (aRestyleHint || aMinChangeHint) {
     Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
   }
 
   PostRestyleEventInternal(false);
 }
 
 void
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -42,16 +42,18 @@ SERVO_BINDING_FUNC(Servo_StyleSet_Append
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_PrependStyleSheet, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_RemoveStyleSheet, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet)
 SERVO_BINDING_FUNC(Servo_StyleSet_InsertStyleSheetBefore, void,
                    RawServoStyleSetBorrowed set, RawServoStyleSheetBorrowed sheet,
                    RawServoStyleSheetBorrowed reference)
+SERVO_BINDING_FUNC(Servo_StyleSet_NoteStyleSheetsChanged, void,
+                   RawServoStyleSetBorrowed set)
 
 // CSSRuleList
 SERVO_BINDING_FUNC(Servo_CssRules_ListTypes, void,
                    ServoCssRulesBorrowed rules,
                    nsTArrayBorrowed_uintptr_t result)
 SERVO_BINDING_FUNC(Servo_CssRules_GetStyleRuleAt, RawServoStyleRuleStrong,
                    ServoCssRulesBorrowed rules, uint32_t index)
 SERVO_BINDING_FUNC(Servo_CssRules_InsertRule, nsresult,
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -469,16 +469,22 @@ ServoStyleSet::StyleNewSubtree(Element* 
 
 void
 ServoStyleSet::StyleNewChildren(Element* aParent)
 {
   Servo_TraverseSubtree(aParent, mRawSet.get(),
                         TraversalRootBehavior::UnstyledChildrenOnly);
 }
 
+void
+ServoStyleSet::NoteStyleSheetsChanged()
+{
+  Servo_StyleSet_NoteStyleSheetsChanged(mRawSet.get());
+}
+
 #ifdef DEBUG
 void
 ServoStyleSet::AssertTreeIsClean()
 {
   if (Element* root = mPresContext->Document()->GetRootElement()) {
     Servo_AssertTreeIsClean(root);
   }
 }
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -137,16 +137,23 @@ public:
   /**
    * Like the above, but skips the root node, and only styles unstyled children.
    * When potentially appending multiple children, it's preferable to call
    * StyleNewChildren on the node rather than making multiple calls to
    * StyleNewSubtree on each child, since it allows for more parallelism.
    */
   void StyleNewChildren(Element* aParent);
 
+  /**
+   * Records that the contents of style sheets have changed since the last
+   * restyle.  Calling this will ensure that the Stylist rebuilds its
+   * selector maps.
+   */
+  void NoteStyleSheetsChanged();
+
 #ifdef DEBUG
   void AssertTreeIsClean();
 #else
   void AssertTreeIsClean() {}
 #endif
 
 private:
   already_AddRefed<nsStyleContext> GetContext(already_AddRefed<ServoComputedValues>,
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -623,17 +623,21 @@ pref("media.video-queue.send-to-composit
 // Allow to check if the decoder supports recycling only on Fennec nightly build.
 pref("media.decoder.recycle.enabled", true);
 #endif
 
 // Enable the MediaCodec PlatformDecoderModule by default.
 pref("media.android-media-codec.enabled", true);
 pref("media.android-media-codec.preferred", true);
 // Run decoder in seperate process.
+#ifdef NIGHTLY_BUILD
+pref("media.android-remote-codec.enabled", true);
+#else
 pref("media.android-remote-codec.enabled", false);
+#endif
 
 // Enable MSE
 pref("media.mediasource.enabled", true);
 
 pref("media.mediadrm-widevinecdm.visible", true);
 
 // optimize images memory usage
 pref("image.downscale-during-decode.enabled", true);
--- a/toolkit/components/telemetry/moz.build
+++ b/toolkit/components/telemetry/moz.build
@@ -17,16 +17,19 @@ LOCAL_INCLUDES += [
     '/xpcom/threads',
 ]
 
 SPHINX_TREES['telemetry'] = 'docs'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
 
+if CONFIG['ENABLE_TESTS']:
+    DIRS += ['tests/gtest']
+
 XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
 BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
 
 XPIDL_SOURCES += [
     'nsITelemetry.idl',
 ]
 
 XPIDL_MODULE = 'telemetry'
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/gtest/TelemetryFixture.h
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#ifndef TelemetryFixture_h_
+#define TelemetryFixture_h_
+
+#include "mozilla/CycleCollectedJSContext.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/SimpleGlobalObject.h"
+
+using namespace mozilla;
+
+class TelemetryTestFixture: public ::testing::Test {
+protected:
+  TelemetryTestFixture() : mCleanGlobal(nullptr) {}
+  virtual void SetUp();
+
+  JSObject* mCleanGlobal;
+
+  nsCOMPtr<nsITelemetry> mTelemetry;
+};
+
+void
+TelemetryTestFixture::SetUp()
+{
+  mTelemetry = do_GetService("@mozilla.org/base/telemetry;1");
+
+  mCleanGlobal =
+    dom::SimpleGlobalObject::Create(dom::SimpleGlobalObject::GlobalType::BindingDetail);
+
+  // The test must fail if we failed getting the global.
+  ASSERT_NE(mCleanGlobal, nullptr) << "SimpleGlobalObject must return a valid global object.";
+}
+
+
+// AutoJSAPI is annotated with MOZ_STACK_CLASS and thus cannot be
+// used as a member of TelemetryTestFixture, since gtest instantiates
+// that on the heap. To work around the problem, use the following class
+// at the beginning of each Telemetry test.
+// Note: this is very similar to AutoJSContext, but it allows to pass a
+// global JS object in.
+class MOZ_RAII AutoJSContextWithGlobal {
+public:
+  explicit AutoJSContextWithGlobal(JSObject* aGlobalObject);
+  JSContext* GetJSContext() const;
+
+protected:
+  dom::AutoJSAPI mJsAPI;
+  JSContext* mCx;
+};
+
+AutoJSContextWithGlobal::AutoJSContextWithGlobal(JSObject* aGlobalObject)
+  : mCx(nullptr)
+{
+  // The JS API must initialize correctly.
+  MOZ_ALWAYS_TRUE(mJsAPI.Init(aGlobalObject));
+}
+
+JSContext* AutoJSContextWithGlobal::GetJSContext() const
+{
+  return mJsAPI.cx();
+}
+
+#endif //TelemetryFixture_h_
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/gtest/TestScalars.cpp
@@ -0,0 +1,288 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+
+#include "js/Conversions.h"
+#include "mozilla/Unused.h"
+#include "nsJSUtils.h" // nsAutoJSString
+#include "nsITelemetry.h"
+#include "Telemetry.h"
+#include "TelemetryFixture.h"
+
+using namespace mozilla;
+
+#define EXPECTED_STRING "Nice, expected and creative string."
+
+namespace {
+
+void
+CheckUintScalar(const char* aName, JSContext* aCx, JS::HandleValue aSnapshot, uint32_t expectedValue)
+{
+  // Validate the value of the test scalar.
+  JS::RootedValue value(aCx);
+  JS::RootedObject scalarObj(aCx, &aSnapshot.toObject());
+  ASSERT_TRUE(JS_GetProperty(aCx, scalarObj, aName, &value)) << "The test scalar must be reported.";
+  ASSERT_TRUE(value.isInt32()) << "The scalar value must be of the correct type.";
+  ASSERT_TRUE(value.toInt32() >= 0) << "The uint scalar type must contain a value >= 0.";
+  ASSERT_EQ(static_cast<uint32_t>(value.toInt32()), expectedValue) << "The scalar value must match the expected value.";
+}
+
+void
+CheckBoolScalar(const char* aName, JSContext* aCx, JS::HandleValue aSnapshot, bool expectedValue)
+{
+  // Validate the value of the test scalar.
+  JS::RootedValue value(aCx);
+  JS::RootedObject scalarObj(aCx, &aSnapshot.toObject());
+  ASSERT_TRUE(JS_GetProperty(aCx, scalarObj, aName, &value)) << "The test scalar must be reported.";
+  ASSERT_TRUE(value.isBoolean()) << "The scalar value must be of the correct type.";
+  ASSERT_EQ(static_cast<bool>(value.toBoolean()), expectedValue) << "The scalar value must match the expected value.";
+}
+
+void
+CheckStringScalar(const char* aName, JSContext* aCx, JS::HandleValue aSnapshot, const char* expectedValue)
+{
+  // Validate the value of the test scalar.
+  JS::RootedValue value(aCx);
+  JS::RootedObject scalarObj(aCx, &aSnapshot.toObject());
+  ASSERT_TRUE(JS_GetProperty(aCx, scalarObj, aName, &value)) << "The test scalar must be reported.";
+  ASSERT_TRUE(value.isString()) << "The scalar value must be of the correct type.";
+
+  bool sameString;
+  ASSERT_TRUE(JS_StringEqualsAscii(aCx, value.toString(), expectedValue, &sameString)) << "JS String comparison failed";
+  ASSERT_TRUE(sameString) << "The scalar value must match the expected string";
+}
+
+void
+CheckKeyedUintScalar(const char* aName, const char* aKey, JSContext* aCx, JS::HandleValue aSnapshot,
+                     uint32_t expectedValue)
+{
+  JS::RootedValue keyedScalar(aCx);
+  JS::RootedObject scalarObj(aCx, &aSnapshot.toObject());
+  // Get the aName keyed scalar object from the scalars snapshot.
+  ASSERT_TRUE(JS_GetProperty(aCx, scalarObj, aName, &keyedScalar))
+    << "The keyed scalar must be reported.";
+
+  CheckUintScalar(aKey, aCx, keyedScalar, expectedValue);
+}
+
+void
+CheckKeyedBoolScalar(const char* aName, const char* aKey, JSContext* aCx, JS::HandleValue aSnapshot,
+                     bool expectedValue)
+{
+  JS::RootedValue keyedScalar(aCx);
+  JS::RootedObject scalarObj(aCx, &aSnapshot.toObject());
+  // Get the aName keyed scalar object from the scalars snapshot.
+  ASSERT_TRUE(JS_GetProperty(aCx, scalarObj, aName, &keyedScalar))
+    << "The keyed scalar must be reported.";
+
+  CheckBoolScalar(aKey, aCx, keyedScalar, expectedValue);
+}
+
+void
+CheckNumberOfProperties(const char* aName, JSContext* aCx, JS::HandleValue aSnapshot,
+                        uint32_t expectedNumProperties)
+{
+  JS::RootedValue keyedScalar(aCx);
+  JS::RootedObject scalarObj(aCx, &aSnapshot.toObject());
+  // Get the aName keyed scalar object from the scalars snapshot.
+  ASSERT_TRUE(JS_GetProperty(aCx, scalarObj, aName, &keyedScalar))
+    << "The keyed scalar must be reported.";
+
+  JS::RootedObject keyedScalarObj(aCx, &keyedScalar.toObject());
+  JS::Rooted<JS::IdVector> ids(aCx, JS::IdVector(aCx));
+  ASSERT_TRUE(JS_Enumerate(aCx, keyedScalarObj, &ids))
+    << "We must be able to get keyed scalar members.";
+
+  ASSERT_EQ(expectedNumProperties, ids.length())
+    << "The scalar must report the expected number of properties.";
+}
+
+void
+GetScalarsSnapshot(bool aKeyed, JSContext* aCx, JS::MutableHandle<JS::Value> aResult)
+{
+  nsCOMPtr<nsITelemetry> telemetry = do_GetService("@mozilla.org/base/telemetry;1");
+
+  // Get a snapshot of the scalars.
+  JS::RootedValue scalarsSnapshot(aCx);
+  nsresult rv;
+
+  if (aKeyed) {
+    rv = telemetry->SnapshotKeyedScalars(nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN,
+                                         false, aCx, 0, &scalarsSnapshot);
+  } else {
+    rv = telemetry->SnapshotScalars(nsITelemetry::DATASET_RELEASE_CHANNEL_OPTIN,
+                                    false, aCx, 0, &scalarsSnapshot);
+  }
+
+  // Validate the snapshot.
+  ASSERT_EQ(rv, NS_OK) << "Creating a snapshot of the data must not fail.";
+  ASSERT_TRUE(scalarsSnapshot.isObject()) << "The snapshot must be an object.";
+
+  aResult.set(scalarsSnapshot);
+}
+
+} // Anonymous namespace.
+
+// Test that we can properly write unsigned scalars using the C++ API.
+TEST_F(TelemetryTestFixture, ScalarUnsigned) {
+  AutoJSContextWithGlobal cx(mCleanGlobal);
+
+  // Make sure we don't get scalars from other tests.
+  Unused << mTelemetry->ClearScalars();
+
+  // Set the test scalar to a known value.
+  const uint32_t kInitialValue = 1172015;
+  const uint32_t kExpectedUint = 1172017;
+  Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, kInitialValue);
+  Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, kExpectedUint - kInitialValue);
+
+  // Check the recorded value.
+  JS::RootedValue scalarsSnapshot(cx.GetJSContext());
+  GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+  CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(), scalarsSnapshot, kExpectedUint);
+
+  // Try to use SetMaximum.
+  const uint32_t kExpectedUintMaximum = kExpectedUint * 2;
+  Telemetry::ScalarSetMaximum(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, kExpectedUintMaximum);
+
+  // Make sure that calls of the unsupported type don't corrupt the stored value.
+  // Don't run this part in debug builds as that intentionally asserts.
+  #ifndef DEBUG
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, false);
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, NS_LITERAL_STRING("test"));
+  #endif
+
+  // Check the recorded value.
+  GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+  CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(), scalarsSnapshot, kExpectedUintMaximum);
+}
+
+// Test that we can properly write boolean scalars using the C++ API.
+TEST_F(TelemetryTestFixture, ScalarBoolean) {
+  AutoJSContextWithGlobal cx(mCleanGlobal);
+
+  Unused << mTelemetry->ClearScalars();
+
+  // Set the test scalar to a known value.
+  Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, true);
+
+  // Make sure that calls of the unsupported type don't corrupt the stored value.
+  // Don't run this part in debug builds as that intentionally asserts.
+  #ifndef DEBUG
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, static_cast<uint32_t>(12));
+    Telemetry::ScalarSetMaximum(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, 20);
+    Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, 2);
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, NS_LITERAL_STRING("test"));
+  #endif
+
+  // Check the recorded value.
+  JS::RootedValue scalarsSnapshot(cx.GetJSContext());
+  GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+  CheckBoolScalar("telemetry.test.boolean_kind", cx.GetJSContext(), scalarsSnapshot, true);
+}
+
+// Test that we can properly write string scalars using the C++ API.
+TEST_F(TelemetryTestFixture, ScalarString) {
+  AutoJSContextWithGlobal cx(mCleanGlobal);
+
+  Unused << mTelemetry->ClearScalars();
+
+  // Set the test scalar to a known value.
+  Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, NS_LITERAL_STRING(EXPECTED_STRING));
+
+  // Make sure that calls of the unsupported type don't corrupt the stored value.
+  // Don't run this part in debug builds as that intentionally asserts.
+  #ifndef DEBUG
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, static_cast<uint32_t>(12));
+    Telemetry::ScalarSetMaximum(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, 20);
+    Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, 2);
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, true);
+  #endif
+
+  // Check the recorded value.
+  JS::RootedValue scalarsSnapshot(cx.GetJSContext());
+  GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot);
+  CheckStringScalar("telemetry.test.string_kind", cx.GetJSContext(), scalarsSnapshot, EXPECTED_STRING);
+}
+
+// Test that we can properly write keyed unsigned scalars using the C++ API.
+TEST_F(TelemetryTestFixture, KeyedScalarUnsigned) {
+  AutoJSContextWithGlobal cx(mCleanGlobal);
+
+  Unused << mTelemetry->ClearScalars();
+
+  // Set the test scalar to a known value.
+  const char* kScalarName = "telemetry.test.keyed_unsigned_int";
+  const uint32_t kKey1Value = 1172015;
+  const uint32_t kKey2Value = 1172017;
+  Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+                       NS_LITERAL_STRING("key1"), kKey1Value);
+  Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+                       NS_LITERAL_STRING("key2"), kKey1Value);
+  Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+                       NS_LITERAL_STRING("key2"), 2);
+
+  // Make sure that calls of the unsupported type don't corrupt the stored value.
+  // Don't run this part in debug builds as that intentionally asserts.
+  #ifndef DEBUG
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+                         NS_LITERAL_STRING("key1"), false);
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT, NS_LITERAL_STRING("test"));
+  #endif
+
+  // Check the recorded value.
+  JS::RootedValue scalarsSnapshot(cx.GetJSContext());
+  GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+  // Check the keyed scalar we're interested in.
+  CheckKeyedUintScalar(kScalarName, "key1", cx.GetJSContext(), scalarsSnapshot, kKey1Value);
+  CheckKeyedUintScalar(kScalarName, "key2", cx.GetJSContext(), scalarsSnapshot, kKey2Value);
+  CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 2);
+
+  // Try to use SetMaximum.
+  const uint32_t kExpectedUintMaximum = kKey1Value * 2;
+  Telemetry::ScalarSetMaximum(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT,
+                              NS_LITERAL_STRING("key1"), kExpectedUintMaximum);
+
+  GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+  // The first key should be different and te second is expected to be the same.
+  CheckKeyedUintScalar(kScalarName, "key1", cx.GetJSContext(), scalarsSnapshot, kExpectedUintMaximum);
+  CheckKeyedUintScalar(kScalarName, "key2", cx.GetJSContext(), scalarsSnapshot, kKey2Value);
+  CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 2);
+}
+
+TEST_F(TelemetryTestFixture, KeyedScalarBoolean) {
+  AutoJSContextWithGlobal cx(mCleanGlobal);
+
+  Unused << mTelemetry->ClearScalars();
+
+  // Set the test scalar to a known value.
+  Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+                       NS_LITERAL_STRING("key1"), false);
+  Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+                       NS_LITERAL_STRING("key2"), true);
+
+  // Make sure that calls of the unsupported type don't corrupt the stored value.
+  // Don't run this part in debug builds as that intentionally asserts.
+  #ifndef DEBUG
+    Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+                         NS_LITERAL_STRING("key1"), static_cast<uint32_t>(12));
+    Telemetry::ScalarSetMaximum(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+                                NS_LITERAL_STRING("key1"), 20);
+    Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND,
+                         NS_LITERAL_STRING("key1"), 2);
+  #endif
+
+  // Check the recorded value.
+  JS::RootedValue scalarsSnapshot(cx.GetJSContext());
+  GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot);
+
+  // Make sure that the keys contain the expected values.
+  const char* kScalarName = "telemetry.test.keyed_boolean_kind";
+  CheckKeyedBoolScalar(kScalarName, "key1", cx.GetJSContext(), scalarsSnapshot, false);
+  CheckKeyedBoolScalar(kScalarName, "key2", cx.GetJSContext(), scalarsSnapshot, true);
+  CheckNumberOfProperties(kScalarName, cx.GetJSContext(), scalarsSnapshot, 2);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/telemetry/tests/gtest/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, you can obtain one at http://mozilla.org/MPL/2.0/.
+
+Library('telemetrytest')
+
+LOCAL_INCLUDES += [
+    '../..',
+]
+
+UNIFIED_SOURCES = [
+    'TestScalars.cpp',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/toolkit/themes/windows/global/button.css
+++ b/toolkit/themes/windows/global/button.css
@@ -54,17 +54,17 @@ button:-moz-focusring > .button-box {
 
 button[default="true"] {
   -moz-border-top-colors: ThreeDDarkShadow ThreeDHighlight ThreeDLightShadow;
   -moz-border-right-colors: ThreeDDarkShadow ThreeDDarkShadow ThreeDShadow;
   -moz-border-bottom-colors: ThreeDDarkShadow ThreeDDarkShadow ThreeDShadow;
   -moz-border-left-colors: ThreeDDarkShadow ThreeDHighlight ThreeDLightShadow;
 }
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   @media (-moz-windows-compositor) {
     /* This is for high-contrast themes on Windows 8 and later */
     button[default="true"],
     button:hover {
       color: HighlightText;
     }
   }
 }
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -19,33 +19,33 @@ xul|menulist:not([editable="true"]) > xu
   margin-top: 1px;
   margin-bottom: 1px;
 }
 
 xul|checkbox {
   padding-inline-start: 0;
 }
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   xul|*.checkbox-check {
     background-image: none !important;
   }
 
   xul|*.checkbox-check[checked] {
     list-style-image: url("chrome://global/skin/in-content/check.svg#check-native");
     background-color: -moz-dialog;
   }
 }
 
 xul|radio {
   -moz-binding: url("chrome://global/content/bindings/radio.xml#radio");
   padding-inline-start: 0;
 }
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   xul|*.radio-check {
     background-image: none;
   }
 
   xul|*.radio-check[selected] {
     list-style-image: url("chrome://global/skin/in-content/radio.svg#radio-native");
     background-color: -moz-dialog;
   }
@@ -69,14 +69,14 @@ xul|menulist:-moz-focusring > xul|*.menu
 xul|radio[focused="true"] > xul|*.radio-label-box,
 html|input[type="checkbox"]:-moz-focusring + html|label:before,
 xul|checkbox:-moz-focusring > xul|*.checkbox-label-box {
   outline: 1px dotted;
 }
 
 /* Use a 2px border so that selected row highlight is still visible behind
     an existing high-contrast border that uses the background color */
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   xul|treechildren::-moz-tree-row(selected),
   xul|listbox xul|listitem[selected="true"] {
      border: 2px dotted Highlight;
   }
 }
--- a/toolkit/themes/windows/global/toolbarbutton.css
+++ b/toolkit/themes/windows/global/toolbarbutton.css
@@ -92,17 +92,17 @@ toolbarbutton[checked="true"]:not([disab
   @media not all and (-moz-os-version: windows-xp) {
     toolbarbutton:-moz-lwtheme:not([disabled="true"]) {
       color: inherit;
       text-shadow: inherit;
     }
   }
 }
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   toolbarbutton:-moz-lwtheme {
     -moz-appearance: none;
   }
 
   toolbarbutton:-moz-lwtheme:not([disabled="true"]) {
     color: inherit;
     text-shadow: inherit;
   }
--- a/toolkit/themes/windows/mozapps/extensions/extensions.css
+++ b/toolkit/themes/windows/mozapps/extensions/extensions.css
@@ -4,17 +4,17 @@
 
 %include ../../../shared/extensions/extensions.inc.css
 
 #header-utils-btn {
   list-style-image: url("chrome://mozapps/skin/extensions/utilities.svg#utilities");
   margin-inline-end: 16px;
 }
 
-@media not all and (-moz-windows-default-theme) {
+@media (-moz-windows-default-theme: 0) {
   #header-utils-btn {
     list-style-image: url("chrome://mozapps/skin/extensions/utilities.svg#utilities-native");
   }
 }
 
 .sorter[checkState="1"] {
   list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
 }