Merge inbound to mozilla-central. a=merge on a CLOSED TREE

This commit is contained in:
Ciure Andrei 2018-02-20 12:15:57 +02:00
commit 47cb352984
23 changed files with 271 additions and 54 deletions

View File

@ -1,4 +1,6 @@
[DEFAULT]
prefs =
dom.animations-api.core.enabled=true
support-files =
head.js
head_pageAction.js
@ -210,3 +212,4 @@ tags = fullscreen
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]

View File

@ -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);
});

View File

@ -49,6 +49,16 @@ AnimationUtils::GetCurrentRealmDocument(JSContext* aCx)
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()
{

View File

@ -61,6 +61,14 @@ public:
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.
*/

View File

@ -903,7 +903,17 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect(
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;

View File

@ -8,6 +8,7 @@ support-files =
[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]

View File

@ -6,10 +6,9 @@
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>

View File

@ -6,8 +6,8 @@
<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>
@ -19,10 +19,19 @@ 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');

View File

@ -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>

View File

@ -3804,22 +3804,23 @@ Element::Animate(const Nullable<ElementOrCSSPseudoElement>& aTarget,
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();

View File

@ -6394,7 +6394,7 @@ nsGlobalWindowInner::GetOrCreateServiceWorker(const ServiceWorkerDescriptor& aDe
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();
}

View File

@ -7806,7 +7806,10 @@ class CGPerSignatureCall(CGThing):
needsUnwrap = False
argsPost = []
if isConstructor:
runConstructorInCallerCompartment = \
descriptor.interface.getExtendedAttribute(
'RunConstructorInCallerCompartment')
if isConstructor and not runConstructorInCallerCompartment:
needsUnwrap = True
needsUnwrappedVar = False
unwrappedVar = "obj"

View File

@ -1743,6 +1743,7 @@ class IDLInterface(IDLInterfaceOrNamespace):
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():

View File

@ -569,10 +569,9 @@ ClientSource::PostMessage(const ClientPostMessageArgs& aArgs)
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;
}
}

View File

@ -56,7 +56,7 @@ ServiceWorker::Create(nsIGlobalObject* aOwner,
return ref.forget();
}
RefPtr<ServiceWorkerInfo> info = reg->GetByID(aDescriptor.Id());
RefPtr<ServiceWorkerInfo> info = reg->GetByDescriptor(aDescriptor);
if (!info) {
return ref.forget();
}
@ -141,15 +141,11 @@ ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
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

View File

@ -83,8 +83,8 @@ public:
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;

View File

@ -119,6 +119,15 @@ 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
{

View File

@ -81,6 +81,11 @@ public:
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;

View File

@ -990,6 +990,10 @@ ServiceWorkerRegistrationWorkerThread::ShowNotification(JSContext* aCx,
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

View File

@ -492,18 +492,18 @@ ServiceWorkerRegistrationInfo::GetActive() const
}
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;

View File

@ -152,7 +152,7 @@ public:
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.

View File

@ -20,7 +20,10 @@ dictionary KeyframeEffectOptions : AnimationEffectTimingProperties {
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),
@ -54,7 +57,10 @@ 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),

View File

@ -981,7 +981,7 @@ public:
, mType(aUnscaledFont->GetType())
, mData(nullptr)
{
mGetFontFileDataSucceeded = aUnscaledFont->GetFontFileData(&FontDataProc, this);
mGetFontFileDataSucceeded = aUnscaledFont->GetFontFileData(&FontDataProc, this) && mData;
}
~RecordedFontData();
@ -2685,11 +2685,12 @@ RecordedSourceSurfaceCreation::RecordedSourceSurfaceCreation(S &aStream)
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);
}
}
@ -2934,6 +2935,10 @@ RecordedFontData::~RecordedFontData()
inline bool
RecordedFontData::PlayEvent(Translator *aTranslator) const
{
if (!mData) {
return false;
}
RefPtr<NativeFontResource> fontResource =
Factory::CreateNativeFontResource(mData, mFontDetails.size,
aTranslator->GetReferenceDrawTarget()->GetBackendType(),
@ -2967,8 +2972,12 @@ RecordedFontData::OutputSimpleEventInfo(std::stringstream &aStringStream) const
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;
@ -2997,8 +3006,12 @@ RecordedFontData::RecordedFontData(S &aStream)
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