Merge inbound to mozilla-central. a=merge on a CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Tue, 20 Feb 2018 12:15:57 +0200
changeset 443753 2c000486eac466da6623e4d7f7f1fd4e318f60e8
parent 443743 ab4200b426149ca70c7e579039a556cf5ee46e7d (current diff)
parent 443752 a35fe8a4a82de45e10627b6fc760dd13d5df64a8 (diff)
child 443762 47ac350879d518a00e64841bde3d7d890bd67983
child 443814 56a805f8ce42532d6c735e8818d14823f8e9fa0a
push idunknown
push userunknown
push dateunknown
reviewersmerge
milestone60.0a1
first release with
nightly linux32
2c000486eac4 / 60.0a1 / 20180220103456 / files
nightly linux64
2c000486eac4 / 60.0a1 / 20180220103456 / files
nightly mac
2c000486eac4 / 60.0a1 / 20180220103456 / files
nightly win32
2c000486eac4 / 60.0a1 / 20180220103456 / files
nightly win64
2c000486eac4 / 60.0a1 / 20180220103456 / 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 mozilla-central. a=merge on a CLOSED TREE
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -1,9 +1,11 @@
 [DEFAULT]
+prefs =
+    dom.animations-api.core.enabled=true
 support-files =
   head.js
   head_pageAction.js
   head_sessions.js
   head_webNavigation.js
   profilerSymbols.sjs
   context.html
   context_frame.html
@@ -205,8 +207,9 @@ tags = fullscreen
 [browser_ext_windows_create_params.js]
 [browser_ext_windows_create_tabId.js]
 [browser_ext_windows_create_url.js]
 [browser_ext_windows_events.js]
 [browser_ext_windows_size.js]
 skip-if = os == 'mac' # Fails when windows are randomly opened in fullscreen mode
 [browser_ext_windows_update.js]
 tags = fullscreen
+[browser_ext_contentscript_animate.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_contentscript_animate.js
@@ -0,0 +1,95 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(async function test_animate() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "content_scripts": [
+        {
+          "matches": ["http://mochi.test/*"],
+          "js": ["content-script.js"],
+        },
+      ],
+    },
+
+    files: {
+      "content-script.js": function() {
+        let elem = document.getElementsByTagName("body")[0];
+        elem.style.border = "2px solid red";
+
+        let anim = elem.animate({opacity: [1, 0]}, 2000);
+        let frames = anim.effect.getKeyframes();
+        browser.test.assertEq(frames.length, 2,
+                              "frames for Element.animate should be non-zero");
+        browser.test.assertEq(frames[0].opacity, "1",
+                              "first frame opacity for Element.animate should be specified value");
+        browser.test.assertEq(frames[0].computedOffset, 0,
+                              "first frame offset for Element.animate should be 0");
+        browser.test.assertEq(frames[1].opacity, "0",
+                              "last frame opacity for Element.animate should be specified value");
+        browser.test.assertEq(frames[1].computedOffset, 1,
+                              "last frame offset for Element.animate should be 1");
+
+        browser.test.notifyPass("contentScriptAnimate");
+      },
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitFinish("contentScriptAnimate");
+  await extension.unload();
+
+  await BrowserTestUtils.removeTab(tab);
+});
+
+add_task(async function test_KeyframeEffect() {
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "content_scripts": [
+        {
+          "matches": ["http://mochi.test/*"],
+          "js": ["content-script.js"],
+        },
+      ],
+    },
+
+    files: {
+      "content-script.js": function() {
+        let elem = document.getElementsByTagName("body")[0];
+        elem.style.border = "2px solid red";
+
+        let effect = new KeyframeEffect(elem, [
+          {opacity: 1, offset: 0},
+          {opacity: 0, offset: 1},
+        ], {duration: 1000, fill: "forwards"});
+        let frames = effect.getKeyframes();
+        browser.test.assertEq(frames.length, 2,
+                              "frames for KeyframeEffect ctor should be non-zero");
+        browser.test.assertEq(frames[0].opacity, "1",
+                              "first frame opacity for KeyframeEffect ctor should be specified value");
+        browser.test.assertEq(frames[0].computedOffset, 0,
+                              "first frame offset for KeyframeEffect ctor should be 0");
+        browser.test.assertEq(frames[1].opacity, "0",
+                              "last frame opacity for KeyframeEffect ctor should be specified value");
+        browser.test.assertEq(frames[1].computedOffset, 1,
+                              "last frame offset for KeyframeEffect ctor should be 1");
+
+        let animation = new Animation(effect, document.timeline);
+        animation.play();
+
+        browser.test.notifyPass("contentScriptKeyframeEffect");
+      },
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitFinish("contentScriptKeyframeEffect");
+  await extension.unload();
+
+  await BrowserTestUtils.removeTab(tab);
+});
--- a/dom/animation/AnimationUtils.cpp
+++ b/dom/animation/AnimationUtils.cpp
@@ -44,16 +44,26 @@ AnimationUtils::GetCurrentRealmDocument(
 {
   nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(aCx);
   if (!win) {
     return nullptr;
   }
   return win->GetDoc();
 }
 
+/* static */ nsIDocument*
+AnimationUtils::GetDocumentFromGlobal(JSObject* aGlobalObject)
+{
+  nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobalObject);
+  if (!win) {
+    return nullptr;
+  }
+  return win->GetDoc();
+}
+
 /* static */ bool
 AnimationUtils::IsOffscreenThrottlingEnabled()
 {
   static bool sOffscreenThrottlingEnabled;
   static bool sPrefCached = false;
 
   if (!sPrefCached) {
     sPrefCached = true;
--- a/dom/animation/AnimationUtils.h
+++ b/dom/animation/AnimationUtils.h
@@ -57,16 +57,24 @@ public:
 
   /**
    * Get the document from the JS context to use when parsing CSS properties.
    */
   static nsIDocument*
   GetCurrentRealmDocument(JSContext* aCx);
 
   /**
+   * Get the document from the global object, or nullptr if the document has
+   * no window, to use when constructing DOM object without entering the
+   * target window's compartment (see KeyframeEffect constructor).
+   */
+  static nsIDocument*
+  GetDocumentFromGlobal(JSObject* aGlobalObject);
+
+  /**
    * Checks if offscreen animation throttling is enabled.
    */
   static bool
   IsOffscreenThrottlingEnabled();
 
   /**
    * Returns true if the given EffectSet contains a current effect that animates
    * scale. |aFrame| is used for calculation of scale values.
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -898,17 +898,27 @@ template <class KeyframeEffectType, clas
 /* static */ already_AddRefed<KeyframeEffectType>
 KeyframeEffectReadOnly::ConstructKeyframeEffect(
     const GlobalObject& aGlobal,
     const Nullable<ElementOrCSSPseudoElement>& aTarget,
     JS::Handle<JSObject*> aKeyframes,
     const OptionsType& aOptions,
     ErrorResult& aRv)
 {
-  nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context());
+  // We should get the document from `aGlobal` instead of the current Realm
+  // to make this works in Xray case.
+  //
+  // In all non-Xray cases, `aGlobal` matches the current Realm, so this
+  // matches the spec behavior.
+  //
+  // In Xray case, the new objects should be created using the document of
+  // the target global, but KeyframeEffect and KeyframeEffectReadOnly
+  // constructors are called in the caller's compartment to access
+  // `aKeyframes` object.
+  nsIDocument* doc = AnimationUtils::GetDocumentFromGlobal(aGlobal.Get());
   if (!doc) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   TimingParams timingParams =
     TimingParams::FromOptionsUnion(aOptions, doc, aRv);
   if (aRv.Failed()) {
--- a/dom/animation/test/chrome.ini
+++ b/dom/animation/test/chrome.ini
@@ -3,16 +3,17 @@ support-files =
   testcommon.js
   ../../imptests/testharness.js
   ../../imptests/testharnessreport.js
   !/dom/animation/test/chrome/file_animate_xrays.html
 
 [chrome/test_animate_xrays.html]
 # file_animate_xrays.html needs to go in mochitest.ini since it is served
 # over HTTP
+[chrome/test_keyframe_effect_xrays.html]
 [chrome/test_animation_observers_async.html]
 [chrome/test_animation_observers_sync.html]
 [chrome/test_animation_performance_warning.html]
 [chrome/test_animation_properties.html]
 [chrome/test_cssanimation_missing_keyframes.html]
 [chrome/test_generated_content_getAnimations.html]
 [chrome/test_running_on_compositor.html]
 [chrome/test_simulate_compute_values_failure.html]
--- a/dom/animation/test/chrome/file_animate_xrays.html
+++ b/dom/animation/test/chrome/file_animate_xrays.html
@@ -1,19 +1,18 @@
 <!doctype html>
 <html>
 <head>
 <meta charset=utf-8>
 <script>
 Element.prototype.animate = function() {
   throw 'Called animate() as defined in content document';
 }
-// Bug 1211783: Use KeyframeEffect (not KeyframeEffectReadOnly) here
-for (var obj of [KeyframeEffectReadOnly, Animation]) {
-  obj = function() {
-    throw 'Called overridden ' + String(obj) + ' constructor';
+for (let name of ["KeyframeEffect", "KeyframeEffectReadOnly", "Animation"]) {
+  this[name] = function() {
+    throw `Called overridden ${name} constructor`;
   };
 }
 </script>
 <body>
 <div id="target"></div>
 </body>
 </html>
--- a/dom/animation/test/chrome/test_animate_xrays.html
+++ b/dom/animation/test/chrome/test_animate_xrays.html
@@ -1,31 +1,40 @@
 <!doctype html>
 <head>
 <meta charset=utf-8>
 <script type="application/javascript" src="../testharness.js"></script>
 <script type="application/javascript" src="../testharnessreport.js"></script>
 <script type="application/javascript" src="../testcommon.js"></script>
 </head>
 <body>
-<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1045994"
-  target="_blank">Mozilla Bug 1045994</a>
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1414674"
+  target="_blank">Mozilla Bug 1414674</a>
 <div id="log"></div>
 <iframe id="iframe"
   src="http://example.org/tests/dom/animation/test/chrome/file_animate_xrays.html"></iframe>
 <script>
 'use strict';
 
 var win = document.getElementById('iframe').contentWindow;
 
 async_test(function(t) {
   window.addEventListener('load', t.step_func(function() {
     var target = win.document.getElementById('target');
-    var anim = target.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
-    // In the x-ray case, the frames object will be given an opaque wrapper
-    // so it won't be possible to fetch any frames from it.
-    assert_equals(anim.effect.getKeyframes().length, 0);
+    var anim = target.animate({opacity: [ 1, 0 ]}, 100 * MS_PER_SEC);
+    // The frames object should be accessible via x-ray.
+    var frames = anim.effect.getKeyframes();
+    assert_equals(frames.length, 2,
+                  "frames for Element.animate should be non-zero");
+    assert_equals(frames[0].opacity, "1",
+                  "first frame opacity for Element.animate should be specified value");
+    assert_equals(frames[0].computedOffset, 0,
+                  "first frame offset for Element.animate should be 0");
+    assert_equals(frames[1].opacity, "0",
+                  "last frame opacity for Element.animate should be specified value");
+    assert_equals(frames[1].computedOffset, 1,
+                  "last frame offset for Element.animate should be 1");
     t.done();
   }));
 }, 'Calling animate() across x-rays');
 
 </script>
 </body>
new file mode 100644
--- /dev/null
+++ b/dom/animation/test/chrome/test_keyframe_effect_xrays.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<head>
+<meta charset=utf-8>
+<script type="application/javascript" src="../testharness.js"></script>
+<script type="application/javascript" src="../testharnessreport.js"></script>
+<script type="application/javascript" src="../testcommon.js"></script>
+</head>
+<body>
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1414674"
+  target="_blank">Mozilla Bug 1414674</a>
+<div id="log"></div>
+<iframe id="iframe"
+  src="http://example.org/tests/dom/animation/test/chrome/file_animate_xrays.html"></iframe>
+<script>
+'use strict';
+
+var win = document.getElementById('iframe').contentWindow;
+
+async_test(function(t) {
+  window.addEventListener('load', t.step_func(function() {
+    var target = win.document.getElementById('target');
+    var effect = new win.KeyframeEffect(target, [
+      {opacity: 1, offset: 0},
+      {opacity: 0, offset: 1},
+    ], {duration: 100 * MS_PER_SEC, fill: "forwards"});
+    // The frames object should be accessible via x-ray.
+    var frames = effect.getKeyframes();
+    assert_equals(frames.length, 2,
+                  "frames for KeyframeEffect ctor should be non-zero");
+    assert_equals(frames[0].opacity, "1",
+                  "first frame opacity for KeyframeEffect ctor should be specified value");
+    assert_equals(frames[0].computedOffset, 0,
+                  "first frame offset for KeyframeEffect ctor should be 0");
+    assert_equals(frames[1].opacity, "0",
+                  "last frame opacity for KeyframeEffect ctor should be specified value");
+    assert_equals(frames[1].computedOffset, 1,
+                  "last frame offset for KeyframeEffect ctor should be 1");
+    var animation = new win.Animation(effect, document.timeline);
+    animation.play();
+    t.done();
+  }));
+}, 'Calling KeyframeEffect() ctor across x-rays');
+
+</script>
+</body>
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3799,32 +3799,33 @@ Element::Animate(const Nullable<ElementO
   nsCOMPtr<nsIGlobalObject> ownerGlobal = referenceElement->GetOwnerGlobal();
   if (!ownerGlobal) {
     aError.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
   GlobalObject global(aContext, ownerGlobal->GetGlobalJSObject());
   MOZ_ASSERT(!global.Failed());
 
-  // Wrap the aKeyframes object for the cross-compartment case.
-  JS::Rooted<JSObject*> keyframes(aContext);
-  keyframes = aKeyframes;
+  // KeyframeEffect constructor doesn't follow the standard Xray calling
+  // convention and needs to be called in caller's compartment.
+  // This should match to RunConstructorInCallerCompartment attribute in
+  // KeyframeEffect.webidl.
+  RefPtr<KeyframeEffect> effect =
+    KeyframeEffect::Constructor(global, aTarget, aKeyframes, aOptions,
+                                aError);
+  if (aError.Failed()) {
+    return nullptr;
+  }
+
+  // Animation constructor follows the standard Xray calling convention and
+  // needs to be called in the target element's compartment.
   Maybe<JSAutoCompartment> ac;
   if (js::GetContextCompartment(aContext) !=
       js::GetObjectCompartment(ownerGlobal->GetGlobalJSObject())) {
     ac.emplace(aContext, ownerGlobal->GetGlobalJSObject());
-    if (!JS_WrapObject(aContext, &keyframes)) {
-      return nullptr;
-    }
-  }
-
-  RefPtr<KeyframeEffect> effect =
-    KeyframeEffect::Constructor(global, aTarget, keyframes, aOptions, aError);
-  if (aError.Failed()) {
-    return nullptr;
   }
 
   AnimationTimeline* timeline = referenceElement->OwnerDoc()->Timeline();
   RefPtr<Animation> animation =
     Animation::Constructor(global, effect,
                            Optional<AnimationTimeline*>(timeline), aError);
   if (aError.Failed()) {
     return nullptr;
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -6389,17 +6389,17 @@ nsGlobalWindowInner::GetController() con
 }
 
 RefPtr<ServiceWorker>
 nsGlobalWindowInner::GetOrCreateServiceWorker(const ServiceWorkerDescriptor& aDescriptor)
 {
   MOZ_ASSERT(NS_IsMainThread());
   RefPtr<ServiceWorker> ref;
   for (auto sw : mServiceWorkerList) {
-    if (sw->MatchesDescriptor(aDescriptor)) {
+    if (sw->Descriptor().Matches(aDescriptor)) {
       ref = sw;
       return ref.forget();
     }
   }
   ref = ServiceWorker::Create(this, aDescriptor);
   return ref.forget();
 }
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7801,17 +7801,20 @@ class CGPerSignatureCall(CGThing):
         # since GlobalObject already contains the context.
         needsCx = needCx(returnType, arguments, self.extendedAttributes,
                          not descriptor.interface.isJSImplemented(), static)
         if needsCx:
             argsPre.append("cx")
 
         needsUnwrap = False
         argsPost = []
-        if isConstructor:
+        runConstructorInCallerCompartment = \
+            descriptor.interface.getExtendedAttribute(
+                'RunConstructorInCallerCompartment')
+        if isConstructor and not runConstructorInCallerCompartment:
             needsUnwrap = True
             needsUnwrappedVar = False
             unwrappedVar = "obj"
             if descriptor.interface.isJSImplemented():
                 # We need the desired proto in our constructor, because the
                 # constructor will actually construct our reflector.
                 argsPost.append("desiredProto")
         elif descriptor.interface.isJSImplemented():
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -1738,16 +1738,17 @@ class IDLInterface(IDLInterfaceOrNamespa
                     member.addExtendedAttributes([attr])
             elif (identifier == "NeedResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
                   identifier == "Unforgeable" or
                   identifier == "LegacyEventInit" or
                   identifier == "ProbablyShortLivingWrapper" or
                   identifier == "LegacyUnenumerableNamedProperties" or
+                  identifier == "RunConstructorInCallerCompartment" or
                   identifier == "NonOrdinaryGetPrototypeOf"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
             elif identifier == "Exposed":
                 convertExposedAttrToGlobalNameSet(attr,
                                                   self._exposureGlobalNames)
--- a/dom/clients/manager/ClientSource.cpp
+++ b/dom/clients/manager/ClientSource.cpp
@@ -564,20 +564,19 @@ ClientSource::PostMessage(const ClientPo
     // Shutting down. Just don't deliver this message.
     ref = ClientOpPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
     return ref.forget();
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> reg =
     swm->GetRegistration(principal, source.Scope());
   if (reg) {
-    RefPtr<ServiceWorkerInfo> serviceWorker = reg->GetByID(source.Id());
-    if (serviceWorker) {
-      RefPtr<ServiceWorker> instance =
-        globalObject->GetOrCreateServiceWorker(source);
+    RefPtr<ServiceWorker> instance =
+      globalObject->GetOrCreateServiceWorker(source);
+    if (instance) {
       init.mSource.SetValue().SetAsServiceWorker() = instance;
     }
   }
 
   RefPtr<MessageEvent> event =
     MessageEvent::Constructor(target, NS_LITERAL_STRING("message"), init);
   event->SetTrusted(true);
 
--- a/dom/serviceworkers/ServiceWorker.cpp
+++ b/dom/serviceworkers/ServiceWorker.cpp
@@ -51,17 +51,17 @@ ServiceWorker::Create(nsIGlobalObject* a
   }
 
   RefPtr<ServiceWorkerRegistrationInfo> reg =
     swm->GetRegistration(aDescriptor.PrincipalInfo(), aDescriptor.Scope());
   if (!reg) {
     return ref.forget();
   }
 
-  RefPtr<ServiceWorkerInfo> info = reg->GetByID(aDescriptor.Id());
+  RefPtr<ServiceWorkerInfo> info = reg->GetByDescriptor(aDescriptor);
   if (!info) {
     return ref.forget();
   }
 
   ref = new ServiceWorker(aOwner, aDescriptor, info);
   return ref.forget();
 }
 
@@ -136,25 +136,21 @@ ServiceWorker::PostMessage(JSContext* aC
   if (State() == ServiceWorkerState::Redundant) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   mInner->PostMessage(GetParentObject(), aCx, aMessage, aTransferable, aRv);
 }
 
-bool
-ServiceWorker::MatchesDescriptor(const ServiceWorkerDescriptor& aDescriptor) const
+
+const ServiceWorkerDescriptor&
+ServiceWorker::Descriptor() const
 {
-  // Compare everything in the descriptor except the state.  That is mutable
-  // and may not exactly match.
-  return mDescriptor.PrincipalInfo() == aDescriptor.PrincipalInfo() &&
-         mDescriptor.Scope() == aDescriptor.Scope() &&
-         mDescriptor.ScriptURL() == aDescriptor.ScriptURL() &&
-         mDescriptor.Id() == aDescriptor.Id();
+  return mDescriptor;
 }
 
 void
 ServiceWorker::DisconnectFromOwner()
 {
   nsIGlobalObject* global = GetParentObject();
   if (global) {
     global->RemoveServiceWorker(this);
--- a/dom/serviceworkers/ServiceWorker.h
+++ b/dom/serviceworkers/ServiceWorker.h
@@ -78,18 +78,18 @@ public:
 
   void
   GetScriptURL(nsString& aURL) const;
 
   void
   PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
               const Sequence<JSObject*>& aTransferable, ErrorResult& aRv);
 
-  bool
-  MatchesDescriptor(const ServiceWorkerDescriptor& aDescriptor) const;
+  const ServiceWorkerDescriptor&
+  Descriptor() const;
 
   void
   DisconnectFromOwner() override;
 
 private:
   ServiceWorker(nsIGlobalObject* aWindow,
                 const ServiceWorkerDescriptor& aDescriptor,
                 Inner* aInner);
--- a/dom/serviceworkers/ServiceWorkerDescriptor.cpp
+++ b/dom/serviceworkers/ServiceWorkerDescriptor.cpp
@@ -114,16 +114,25 @@ ServiceWorkerDescriptor::State() const
 }
 
 void
 ServiceWorkerDescriptor::SetState(ServiceWorkerState aState)
 {
   mData->state() = aState;
 }
 
+bool
+ServiceWorkerDescriptor::Matches(const ServiceWorkerDescriptor& aDescriptor) const
+{
+  return Id() == aDescriptor.Id() &&
+         Scope() == aDescriptor.Scope() &&
+         ScriptURL() == aDescriptor.ScriptURL() &&
+         PrincipalInfo() == aDescriptor.PrincipalInfo();
+}
+
 const IPCServiceWorkerDescriptor&
 ServiceWorkerDescriptor::ToIPC() const
 {
   return *mData;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/serviceworkers/ServiceWorkerDescriptor.h
+++ b/dom/serviceworkers/ServiceWorkerDescriptor.h
@@ -76,16 +76,21 @@ public:
   ScriptURL() const;
 
   ServiceWorkerState
   State() const;
 
   void
   SetState(ServiceWorkerState aState);
 
+  // Try to determine if two workers match each other.  This is less strict
+  // than an operator==() call since it ignores mutable values like State().
+  bool
+  Matches(const ServiceWorkerDescriptor& aDescriptor) const;
+
   // Expose the underlying IPC type so that it can be passed via IPC.
   const IPCServiceWorkerDescriptor&
   ToIPC() const;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationImpl.cpp
@@ -985,16 +985,20 @@ WorkerListener::UpdateFound()
 
 // Notification API extension.
 already_AddRefed<Promise>
 ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
                                                         const nsAString& aTitle,
                                                         const NotificationOptions& aOptions,
                                                         ErrorResult& aRv)
 {
+  if (!mWorkerPrivate) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
 
   // Until Bug 1131324 exposes ServiceWorkerContainer on workers,
   // ShowPersistentNotification() checks for valid active worker while it is
   // also verifying scope so that we block the worker on the main thread only
   // once.
   RefPtr<Promise> p =
     Notification::ShowPersistentNotification(aCx, mWorkerPrivate->GlobalScope(),
                                              mScope, aTitle, aOptions, aRv);
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.cpp
@@ -487,28 +487,28 @@ ServiceWorkerRegistrationInfo::GetWaitin
 ServiceWorkerInfo*
 ServiceWorkerRegistrationInfo::GetActive() const
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mActiveWorker;
 }
 
 ServiceWorkerInfo*
-ServiceWorkerRegistrationInfo::GetByID(uint64_t aID) const
+ServiceWorkerRegistrationInfo::GetByDescriptor(const ServiceWorkerDescriptor& aDescriptor) const
 {
-  if (mActiveWorker && mActiveWorker->ID() == aID) {
+  if (mActiveWorker && mActiveWorker->Descriptor().Matches(aDescriptor)) {
     return mActiveWorker;
   }
-  if (mWaitingWorker && mWaitingWorker->ID() == aID) {
+  if (mWaitingWorker && mWaitingWorker->Descriptor().Matches(aDescriptor)) {
     return mWaitingWorker;
   }
-  if (mInstallingWorker && mInstallingWorker->ID() == aID) {
+  if (mInstallingWorker && mInstallingWorker->Descriptor().Matches(aDescriptor)) {
     return mInstallingWorker;
   }
-  if (mEvaluatingWorker && mEvaluatingWorker->ID() == aID) {
+  if (mEvaluatingWorker && mEvaluatingWorker->Descriptor().Matches(aDescriptor)) {
     return mEvaluatingWorker;
   }
   return nullptr;
 }
 
 void
 ServiceWorkerRegistrationInfo::SetEvaluating(ServiceWorkerInfo* aServiceWorker)
 {
--- a/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
+++ b/dom/serviceworkers/ServiceWorkerRegistrationInfo.h
@@ -147,17 +147,17 @@ public:
 
   ServiceWorkerInfo*
   GetWaiting() const;
 
   ServiceWorkerInfo*
   GetActive() const;
 
   ServiceWorkerInfo*
-  GetByID(uint64_t aID) const;
+  GetByDescriptor(const ServiceWorkerDescriptor& aDescriptor) const;
 
   // Set the given worker as the evaluating service worker.  The worker
   // state is not changed.
   void
   SetEvaluating(ServiceWorkerInfo* aServiceWorker);
 
   // Remove an existing evaluating worker, if present.  The worker will
   // be transitioned to the Redundant state.
--- a/dom/webidl/KeyframeEffect.webidl
+++ b/dom/webidl/KeyframeEffect.webidl
@@ -15,17 +15,20 @@ enum IterationCompositeOperation {
   "accumulate"
 };
 
 dictionary KeyframeEffectOptions : AnimationEffectTimingProperties {
   IterationCompositeOperation iterationComposite = "replace";
   CompositeOperation          composite = "replace";
 };
 
+// KeyframeEffectReadOnly should run in the caller's compartment to do custom
+// processing on the `keyframes` object.
 [Func="nsDocument::IsWebAnimationsEnabled",
+ RunConstructorInCallerCompartment,
  Constructor ((Element or CSSPseudoElement)? target,
               object? keyframes,
               optional (unrestricted double or KeyframeEffectOptions) options),
  Constructor (KeyframeEffectReadOnly source)]
 interface KeyframeEffectReadOnly : AnimationEffectReadOnly {
   readonly attribute (Element or CSSPseudoElement)?  target;
   readonly attribute IterationCompositeOperation iterationComposite;
   readonly attribute CompositeOperation          composite;
@@ -49,17 +52,20 @@ dictionary AnimationPropertyDetails {
            DOMString                               warning;
   required sequence<AnimationPropertyValueDetails> values;
 };
 
 partial interface KeyframeEffectReadOnly {
   [ChromeOnly, Throws] sequence<AnimationPropertyDetails> getProperties();
 };
 
+// KeyframeEffect should run in the caller's compartment to do custom
+// processing on the `keyframes` object.
 [Func="nsDocument::IsWebAnimationsEnabled",
+ RunConstructorInCallerCompartment,
  Constructor ((Element or CSSPseudoElement)? target,
               object? keyframes,
               optional (unrestricted double or KeyframeEffectOptions) options),
  Constructor (KeyframeEffectReadOnly source)]
 interface KeyframeEffect : KeyframeEffectReadOnly {
   inherit attribute (Element or CSSPseudoElement)? target;
   [NeedsCallerType]
   inherit attribute IterationCompositeOperation    iterationComposite;
--- a/gfx/2d/RecordedEventImpl.h
+++ b/gfx/2d/RecordedEventImpl.h
@@ -976,17 +976,17 @@ public:
     recordedFontData->SetFontData(aData, aSize, aIndex);
   }
 
   explicit RecordedFontData(UnscaledFont *aUnscaledFont)
     : RecordedEventDerived(FONTDATA)
     , mType(aUnscaledFont->GetType())
     , mData(nullptr)
   {
-    mGetFontFileDataSucceeded = aUnscaledFont->GetFontFileData(&FontDataProc, this);
+    mGetFontFileDataSucceeded = aUnscaledFont->GetFontFileData(&FontDataProc, this) && mData;
   }
 
   ~RecordedFontData();
 
   bool IsValid() const { return mGetFontFileDataSucceeded; }
 
   virtual bool PlayEvent(Translator *aTranslator) const override;
 
@@ -2680,21 +2680,22 @@ RecordedSourceSurfaceCreation::Record(S 
 
 template<class S>
 RecordedSourceSurfaceCreation::RecordedSourceSurfaceCreation(S &aStream)
   : RecordedEventDerived(SOURCESURFACECREATION), mDataOwned(true)
 {
   ReadElement(aStream, mRefPtr);
   ReadElement(aStream, mSize);
   ReadElement(aStream, mFormat);
-  mData = (uint8_t*)new (fallible) char[mSize.width * mSize.height * BytesPerPixel(mFormat)];
+  size_t size = mSize.width * mSize.height * BytesPerPixel(mFormat);
+  mData = new (fallible) uint8_t[size];
   if (!mData) {
-    gfxWarning() << "RecordedSourceSurfaceCreation failed to allocate data";
+    gfxCriticalNote << "RecordedSourceSurfaceCreation failed to allocate data of size " << size;
   } else {
-    aStream.read((char*)mData, mSize.width * mSize.height * BytesPerPixel(mFormat));
+    aStream.read((char*)mData, size);
   }
 }
 
 inline void
 RecordedSourceSurfaceCreation::OutputSimpleEventInfo(std::stringstream &aStringStream) const
 {
   aStringStream << "[" << mRefPtr << "] SourceSurface created (Size: " << mSize.width << "x" << mSize.height << ")";
 }
@@ -2929,16 +2930,20 @@ inline
 RecordedFontData::~RecordedFontData()
 {
   delete[] mData;
 }
 
 inline bool
 RecordedFontData::PlayEvent(Translator *aTranslator) const
 {
+  if (!mData) {
+    return false;
+  }
+
   RefPtr<NativeFontResource> fontResource =
     Factory::CreateNativeFontResource(mData, mFontDetails.size,
                                       aTranslator->GetReferenceDrawTarget()->GetBackendType(),
                                       mType, aTranslator->GetFontContext());
   if (!fontResource) {
     return false;
   }
 
@@ -2962,18 +2967,22 @@ inline void
 RecordedFontData::OutputSimpleEventInfo(std::stringstream &aStringStream) const
 {
   aStringStream << "Font Data of size " << mFontDetails.size;
 }
 
 inline void
 RecordedFontData::SetFontData(const uint8_t *aData, uint32_t aSize, uint32_t aIndex)
 {
-  mData = new uint8_t[aSize];
-  memcpy(mData, aData, aSize);
+  mData = new (fallible) uint8_t[aSize];
+  if (!mData) {
+    gfxCriticalNote << "RecordedFontData failed to allocate data for recording of size " << aSize;
+  } else {
+    memcpy(mData, aData, aSize);
+  }
   mFontDetails.fontDataKey =
     SFNTData::GetUniqueKey(aData, aSize, 0, nullptr);
   mFontDetails.size = aSize;
   mFontDetails.index = aIndex;
 }
 
 inline bool
 RecordedFontData::GetFontDetails(RecordedFontDetails& fontDetails)
@@ -2992,18 +3001,22 @@ template<class S>
 RecordedFontData::RecordedFontData(S &aStream)
   : RecordedEventDerived(FONTDATA)
   , mType(FontType::SKIA)
   , mData(nullptr)
 {
   ReadElement(aStream, mType);
   ReadElement(aStream, mFontDetails.fontDataKey);
   ReadElement(aStream, mFontDetails.size);
-  mData = new uint8_t[mFontDetails.size];
-  aStream.read((char*)mData, mFontDetails.size);
+  mData = new (fallible) uint8_t[mFontDetails.size];
+  if (!mData) {
+    gfxCriticalNote << "RecordedFontData failed to allocate data for playback of size " << mFontDetails.size;
+  } else {
+    aStream.read((char*)mData, mFontDetails.size);
+  }
 }
 
 inline
 RecordedFontDescriptor::~RecordedFontDescriptor()
 {
 }
 
 inline bool