Merge m-c to fx-team. a=merge

This commit is contained in:
Ryan VanderMeulen 2015-07-09 13:53:46 -04:00
commit 85394e2690
179 changed files with 3424 additions and 1656 deletions

View File

@ -710,12 +710,13 @@ exports["test Listeners"] = createProxyTest(html, function (helper) {
});
exports["test MozRequestAnimationFrame"] = createProxyTest("", function (helper) {
exports["test requestAnimationFrame"] = createProxyTest("", function (helper) {
helper.createWorker(
'new ' + function ContentScriptScope() {
window.mozRequestAnimationFrame(function callback() {
assert(callback == this, "callback is equal to `this`");
var self = (function() { return this; })();
window.requestAnimationFrame(function callback() {
assert(self == this, "self is equal to `this`");
done();
});
}

View File

@ -710,12 +710,13 @@ exports["test Listeners"] = createProxyTest(html, function (helper) {
});
exports["test MozRequestAnimationFrame"] = createProxyTest("", function (helper) {
exports["test requestAnimationFrame"] = createProxyTest("", function (helper) {
helper.createWorker(
'new ' + function ContentScriptScope() {
window.mozRequestAnimationFrame(function callback() {
assert(callback == this, "callback is equal to `this`");
var self = (function() { return this; })();
window.requestAnimationFrame(function callback() {
assert(self == this, "self is equal to `this`");
done();
});
}

View File

@ -26,9 +26,11 @@ let librecovery = (function() {
log("Unable to open librecovery.so");
throw Cr.NS_ERROR_FAILURE;
}
// Bug 1163956, modify updatePath from ctyps.char.ptr to ctype.char.array(4096)
// align with librecovery.h. 4096 comes from PATH_MAX
let FotaUpdateStatus = new ctypes.StructType("FotaUpdateStatus", [
{ result: ctypes.int },
{ updatePath: ctypes.char.ptr }
{ updatePath: ctypes.char.array(4096) }
]);
return {

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -135,7 +135,7 @@
<project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
<project name="kernel/common" path="kernel" revision="0f36762ab0c1d8ce10c6a5eda948b05d5d6cc379"/>
<project name="platform/system/core" path="system/core" revision="21aded5b180f7bcc9e2cefbe6e570b1d51b6065b"/>
<project name="platform/system/core" path="system/core" revision="4b989b1bec28b0838420c4d5bb454c78afa62bea"/>
<project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="7d6e1269be7186b2073fa568958b357826692c4b"/>
<project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="e503b1d14d7fdee532b8f391407299da193c1b2d"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="47f48a42502b1c694db2b162011a67a2ff888c5d"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="87a2d8ab9248540910e56921654367b78a587095"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

View File

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "fc6643dd3da2ccdf2ab284479643836bb3698644",
"git_revision": "bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "40fc25355c4367b9e6b73384c070742ae6ff7c0c",
"revision": "4d65a0a88ccd51c22e9b591320d1fe682642dc0a",
"repo_path": "integration/gaia-central"
}

View File

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="47f48a42502b1c694db2b162011a67a2ff888c5d"/>

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="fc6643dd3da2ccdf2ab284479643836bb3698644"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bdddfe1ebb796e2bc1c048d5c4e0f97f3d06f98b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="3477513bcd385571aa01c0d074849e35bd5e2376"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
@ -141,7 +141,7 @@
<default remote="caf" revision="refs/tags/android-5.1.0_r1" sync-j="4"/>
<!-- Nexus 5 specific things -->
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="fe7df1bc8dd0fd71571505d7be1c31a4ad1e40fb"/>
<project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="74919c3e2742b3237b2f30a8cefe182d0caf1e31"/>
<project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="1ad7478309fd43981973e297f5a68a480f3a3c07"/>
<project name="device_lge_hammerhead-kernel" path="device/lge/hammerhead-kernel" remote="b2g" revision="1d42cfba2e91a07b1bea1d1591b8aed4db9cb9a5"/>
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="5d0ae53d9588c3d70c005aec9be94af9a534de16"/>
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="c15b6e266136cd0cdd9b94d0bbed1962d9dd6672"/>

View File

@ -84,4 +84,6 @@ tabbrowser[pendingpaint] {
background-image: url(chrome://browser/skin/tabbrowser/pendingpaint.png);
background-repeat: no-repeat;
background-position: center center;
background-color: #fff;
opacity: 0.3;
}

View File

@ -3004,7 +3004,7 @@
// How long to wait for a tab's layers to load. After this
// time elapses, we're free to put up the spinner and start
// trying to load a different tab.
TAB_SWITCH_TIMEOUT: 300 /* ms */,
TAB_SWITCH_TIMEOUT: 400 /* ms */,
// When the user hasn't switched tabs for this long, we unload
// layers for all tabs that aren't in use.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -18,7 +18,10 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Animation, mTimeline,
// Static members
uint64_t Animation::sNextSequenceNum = 0;
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Animation, mGlobal, mTimeline,
mEffect, mReady, mFinished)
NS_IMPL_CYCLE_COLLECTING_ADDREF(Animation)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Animation)
@ -52,16 +55,31 @@ Animation::SetEffect(KeyframeEffectReadOnly* aEffect)
UpdateRelevance();
}
void
Animation::SetTimeline(AnimationTimeline* aTimeline)
{
if (mTimeline == aTimeline) {
return;
}
if (mTimeline) {
mTimeline->RemoveAnimation(*this);
}
mTimeline = aTimeline;
// FIXME(spec): Once we implement the seeking defined in the spec
// surely this should be SeekFlag::DidSeek but the spec says otherwise.
UpdateTiming(SeekFlag::NoSeek);
// FIXME: When we expose this method to script we'll need to call PostUpdate
// (but *not* when this method gets called from style).
}
void
Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
{
#if 1
// Bug 1096776: once we support inactive/missing timelines we'll want to take
// the disabled branch.
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
"We don't support inactive/missing timelines yet");
#else
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
Nullable<TimeDuration> timelineTime;
if (mTimeline) {
// The spec says to check if the timeline is active (has a resolved time)
// before using it here, but we don't need to since it's harmless to set
@ -71,7 +89,7 @@ Animation::SetStartTime(const Nullable<TimeDuration>& aNewStartTime)
if (timelineTime.IsNull() && !aNewStartTime.IsNull()) {
mHoldTime.SetNull();
}
#endif
Nullable<TimeDuration> previousCurrentTime = GetCurrentTime();
mStartTime = aNewStartTime;
if (!aNewStartTime.IsNull()) {
@ -103,7 +121,7 @@ Animation::GetCurrentTime() const
return result;
}
if (!mStartTime.IsNull()) {
if (mTimeline && !mStartTime.IsNull()) {
Nullable<TimeDuration> timelineTime = mTimeline->GetCurrentTime();
if (!timelineTime.IsNull()) {
result.SetValue((timelineTime.Value() - mStartTime.Value())
@ -170,21 +188,11 @@ Animation::PlayState() const
return AnimationPlayState::Running;
}
static inline already_AddRefed<Promise>
CreatePromise(DocumentTimeline* aTimeline, ErrorResult& aRv)
{
nsIGlobalObject* global = aTimeline->GetParentObject();
if (global) {
return Promise::Create(global, aRv);
}
return nullptr;
}
Promise*
Animation::GetReady(ErrorResult& aRv)
{
if (!mReady) {
mReady = CreatePromise(mTimeline, aRv); // Lazily create on demand
if (!mReady && mGlobal) {
mReady = Promise::Create(mGlobal, aRv); // Lazily create on demand
}
if (!mReady) {
aRv.Throw(NS_ERROR_FAILURE);
@ -197,8 +205,8 @@ Animation::GetReady(ErrorResult& aRv)
Promise*
Animation::GetFinished(ErrorResult& aRv)
{
if (!mFinished) {
mFinished = CreatePromise(mTimeline, aRv); // Lazily create on demand
if (!mFinished && mGlobal) {
mFinished = Promise::Create(mGlobal, aRv); // Lazily create on demand
}
if (!mFinished) {
aRv.Throw(NS_ERROR_FAILURE);
@ -322,6 +330,8 @@ Animation::Tick()
// resuming.
if (mPendingState != PendingState::NotPending &&
!mPendingReadyTime.IsNull() &&
mTimeline &&
!mTimeline->GetCurrentTime().IsNull() &&
mPendingReadyTime.Value() <= mTimeline->GetCurrentTime().Value()) {
FinishPendingAt(mPendingReadyTime.Value());
mPendingReadyTime.SetNull();
@ -355,10 +365,22 @@ Animation::TriggerOnNextTick(const Nullable<TimeDuration>& aReadyTime)
void
Animation::TriggerNow()
{
MOZ_ASSERT(PlayState() == AnimationPlayState::Pending,
"Expected to start a pending animation");
MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
"Expected an active timeline");
// Normally we expect the play state to be pending but when an animation
// is cancelled and its rendered document can't be reached, we can end up
// with the animation still in a pending player tracker even after it is
// no longer pending.
if (PlayState() != AnimationPlayState::Pending) {
return;
}
// If we don't have an active timeline we can't trigger the animation.
// However, this is a test-only method that we don't expect to be used in
// conjunction with animations without an active timeline so generate
// a warning if we do find ourselves in that situation.
if (!mTimeline || mTimeline->GetCurrentTime().IsNull()) {
NS_WARNING("Failed to trigger an animation with an active timeline");
return;
}
FinishPendingAt(mTimeline->GetCurrentTime().Value());
}
@ -436,7 +458,7 @@ Animation::DoCancel()
mHoldTime.SetNull();
mStartTime.SetNull();
UpdateEffect();
UpdateTiming(SeekFlag::NoSeek);
}
void
@ -453,6 +475,20 @@ Animation::UpdateRelevance()
}
}
bool
Animation::HasLowerCompositeOrderThan(const Animation& aOther) const
{
// We only ever sort non-idle animations so we don't ever expect
// mSequenceNum to be set to kUnsequenced
MOZ_ASSERT(mSequenceNum != kUnsequenced &&
aOther.mSequenceNum != kUnsequenced,
"Animations to compare should not be idle");
MOZ_ASSERT(mSequenceNum != aOther.mSequenceNum || &aOther == this,
"Sequence numbers should be unique");
return mSequenceNum < aOther.mSequenceNum;
}
bool
Animation::CanThrottle() const
{
@ -539,7 +575,7 @@ Animation::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
Nullable<TimeDuration> timeToUse = mPendingReadyTime;
if (timeToUse.IsNull() &&
mTimeline &&
!mTimeline->IsUnderTestControl()) {
mTimeline->TracksWallclockTime()) {
timeToUse = mTimeline->ToTimelineTime(TimeStamp::Now());
}
if (!timeToUse.IsNull()) {
@ -625,15 +661,14 @@ Animation::DoPlay(ErrorResult& aRv, LimitBehavior aLimitBehavior)
mPendingState = PendingState::PlayPending;
nsIDocument* doc = GetRenderedDocument();
if (!doc) {
if (doc) {
PendingAnimationTracker* tracker =
doc->GetOrCreatePendingAnimationTracker();
tracker->AddPlayPending(*this);
} else {
TriggerOnNextTick(Nullable<TimeDuration>());
return;
}
PendingAnimationTracker* tracker = doc->GetOrCreatePendingAnimationTracker();
tracker->AddPlayPending(*this);
// We may have updated the current time when we set the hold time above.
UpdateTiming(SeekFlag::NoSeek);
}
@ -677,14 +712,14 @@ Animation::DoPause(ErrorResult& aRv)
mPendingState = PendingState::PausePending;
nsIDocument* doc = GetRenderedDocument();
if (!doc) {
if (doc) {
PendingAnimationTracker* tracker =
doc->GetOrCreatePendingAnimationTracker();
tracker->AddPausePending(*this);
} else {
TriggerOnNextTick(Nullable<TimeDuration>());
return;
}
PendingAnimationTracker* tracker = doc->GetOrCreatePendingAnimationTracker();
tracker->AddPausePending(*this);
UpdateTiming(SeekFlag::NoSeek);
}
@ -743,10 +778,55 @@ Animation::PauseAt(const TimeDuration& aReadyTime)
void
Animation::UpdateTiming(SeekFlag aSeekFlag)
{
// Update the sequence number each time we transition in or out of the
// idle state
if (!IsUsingCustomCompositeOrder()) {
if (PlayState() == AnimationPlayState::Idle) {
mSequenceNum = kUnsequenced;
} else if (mSequenceNum == kUnsequenced) {
mSequenceNum = sNextSequenceNum++;
}
}
// We call UpdateFinishedState before UpdateEffect because the former
// can change the current time, which is used by the latter.
UpdateFinishedState(aSeekFlag);
UpdateEffect();
// Unconditionally Add/Remove from the timeline. This is ok because if the
// animation has already been added/removed (which will be true more often
// than not) the work done by AnimationTimeline/DocumentTimeline is still
// negligible and its easier than trying to detect whenever we are switching
// to/from being relevant.
//
// We need to do this after calling UpdateEffect since it updates some
// cached state used by IsRelevant.
//
// Note that we only store relevant animations on the timeline since they
// are the only ones that need ticks and are the only ones returned from
// AnimationTimeline::GetAnimations. Storing any more than that would mean
// that we fail to garbage collect irrelevant animations since the timeline
// keeps a strong reference to each animation.
//
// Once we tick animations from the their timeline, and once we expect
// timelines to go in and out of being inactive, we will also need to store
// non-idle animations that are waiting for their timeline to become active
// on their timeline (as otherwise once the timeline becomes active it will
// have no way of notifying its animations). For now, however, we can
// simply store just the relevant animations.
if (mTimeline) {
// FIXME: Once we expect animations to go back and forth betweeen being
// inactive and active, we will need to store more than just relevant
// animations on the timeline. This is because an animation might be
// deemed irrelevant because its timeline is inactive. If it is removed
// from the timeline at that point the timeline will have no way of
// getting the animation to add itself again once it becomes active.
if (IsRelevant()) {
mTimeline->AddAnimation(*this);
} else {
mTimeline->RemoveAnimation(*this);
}
}
}
void
@ -776,10 +856,12 @@ Animation::UpdateFinishedState(SeekFlag aSeekFlag)
mHoldTime.SetValue(0);
}
} else if (mPlaybackRate != 0.0 &&
!currentTime.IsNull()) {
!currentTime.IsNull() &&
mTimeline &&
!mTimeline->GetCurrentTime().IsNull()) {
if (aSeekFlag == SeekFlag::DidSeek && !mHoldTime.IsNull()) {
mStartTime.SetValue(mTimeline->GetCurrentTime().Value() -
(mHoldTime.Value().MultDouble(1 / mPlaybackRate)));
(mHoldTime.Value().MultDouble(1 / mPlaybackRate)));
}
mHoldTime.SetNull();
}
@ -898,7 +980,7 @@ Animation::IsPossiblyOrphanedPendingAnimation() const
// never starting/pausing the animation and is unlikely.
nsIDocument* doc = GetRenderedDocument();
if (!doc) {
return false;
return true;
}
PendingAnimationTracker* tracker = doc->GetPendingAnimationTracker();

View File

@ -12,10 +12,11 @@
#include "mozilla/Attributes.h"
#include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
#include "mozilla/dom/AnimationBinding.h" // for AnimationPlayState
#include "mozilla/dom/DocumentTimeline.h" // for DocumentTimeline
#include "mozilla/dom/AnimationTimeline.h" // for AnimationTimeline
#include "mozilla/dom/KeyframeEffect.h" // for KeyframeEffectReadOnly
#include "mozilla/dom/Promise.h" // for Promise
#include "nsCSSProperty.h" // for nsCSSProperty
#include "nsIGlobalObject.h"
// X11 has a #define for CurrentTime.
#ifdef CurrentTime
@ -53,10 +54,11 @@ protected:
virtual ~Animation() {}
public:
explicit Animation(DocumentTimeline* aTimeline)
: mTimeline(aTimeline)
explicit Animation(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal)
, mPlaybackRate(1.0)
, mPendingState(PendingState::NotPending)
, mSequenceNum(kUnsequenced)
, mIsRunningOnCompositor(false)
, mIsPreviousStateFinished(false)
, mFinishedAtLastComposeStyle(false)
@ -67,12 +69,14 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Animation)
DocumentTimeline* GetParentObject() const { return mTimeline; }
AnimationTimeline* GetParentObject() const { return mTimeline; }
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
virtual CSSAnimation* AsCSSAnimation() { return nullptr; }
virtual const CSSAnimation* AsCSSAnimation() const { return nullptr; }
virtual CSSTransition* AsCSSTransition() { return nullptr; }
virtual const CSSTransition* AsCSSTransition() const { return nullptr; }
/**
* Flag to pass to Play to indicate whether or not it should automatically
@ -89,7 +93,8 @@ public:
KeyframeEffectReadOnly* GetEffect() const { return mEffect; }
void SetEffect(KeyframeEffectReadOnly* aEffect);
DocumentTimeline* Timeline() const { return mTimeline; }
AnimationTimeline* GetTimeline() const { return mTimeline; }
void SetTimeline(AnimationTimeline* aTimeline);
Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
void SetStartTime(const Nullable<TimeDuration>& aNewStartTime);
Nullable<TimeDuration> GetCurrentTime() const;
@ -127,11 +132,10 @@ public:
* CSSAnimation::PauseFromJS so we leave it for now.
*/
void PauseFromJS(ErrorResult& aRv) { Pause(aRv); }
// Wrapper functions for Animation DOM methods when called from style.
//
// Typically these DOM methods also notify style of changes but when
// we are calling from style we don't need to do this.
void CancelFromStyle() { DoCancel(); }
virtual void CancelFromStyle() { DoCancel(); }
void Tick();
@ -254,6 +258,20 @@ public:
}
bool IsRelevant() const { return mIsRelevant; }
void UpdateRelevance();
/**
* Returns true if this Animation has a lower composite order than aOther.
*/
virtual bool HasLowerCompositeOrderThan(const Animation& aOther) const;
/**
* Returns true if this Animation is involved in some sort of
* custom composite ordering (such as the ordering defined for CSS
* animations or CSS transitions).
*
* When this is true, this class will not update the sequence number.
*/
virtual bool IsUsingCustomCompositeOrder() const { return false; }
void SetIsRunningOnCompositor() { mIsRunningOnCompositor = true; }
void ClearIsRunningOnCompositor() { mIsRunningOnCompositor = false; }
/**
@ -323,7 +341,8 @@ protected:
virtual css::CommonAnimationManager* GetAnimationManager() const = 0;
AnimationCollection* GetCollection() const;
nsRefPtr<DocumentTimeline> mTimeline;
nsCOMPtr<nsIGlobalObject> mGlobal;
nsRefPtr<AnimationTimeline> mTimeline;
nsRefPtr<KeyframeEffectReadOnly> mEffect;
// The beginning of the delay period.
Nullable<TimeDuration> mStartTime; // Timeline timescale
@ -354,6 +373,14 @@ protected:
enum class PendingState { NotPending, PlayPending, PausePending };
PendingState mPendingState;
static uint64_t sNextSequenceNum;
static const uint64_t kUnsequenced = UINT64_MAX;
// The sequence number assigned to this animation. This is kUnsequenced
// while the animation is in the idle state and is updated each time
// the animation transitions out of the idle state.
uint64_t mSequenceNum;
bool mIsRunningOnCompositor;
// Indicates whether we were in the finished state during our
// most recent unthrottled sample (our last ComposeStyle call).

View File

@ -0,0 +1,32 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_AnimationComparator_h
#define mozilla_AnimationComparator_h
namespace mozilla {
// Although this file is called AnimationComparator, we don't actually
// implement AnimationComparator (to compare const Animation& parameters)
// since it's not actually needed (yet).
template<typename AnimationPtrType>
class AnimationPtrComparator {
public:
bool Equals(const AnimationPtrType& a, const AnimationPtrType& b) const
{
return a == b;
}
bool LessThan(const AnimationPtrType& a, const AnimationPtrType& b) const
{
return a->HasLowerCompositeOrderThan(*b);
}
};
} // namespace mozilla
#endif // mozilla_AnimationComparator_h

View File

@ -5,11 +5,12 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "AnimationTimeline.h"
#include "mozilla/AnimationComparator.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationTimeline, mWindow)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationTimeline, mWindow, mAnimations)
NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationTimeline)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationTimeline)
@ -19,5 +20,79 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationTimeline)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
namespace {
struct AddAnimationParams {
AnimationTimeline::AnimationSequence& mSequence;
#ifdef DEBUG
// This is only used for a pointer-equality assertion
AnimationTimeline* mTimeline;
#endif
};
}
static PLDHashOperator
AppendAnimationToSequence(nsRefPtrHashKey<dom::Animation>* aKey,
void* aParams)
{
Animation* animation = aKey->GetKey();
AddAnimationParams* params = static_cast<AddAnimationParams*>(aParams);
MOZ_ASSERT(animation->IsRelevant(),
"Animations registered with a timeline should be relevant");
MOZ_ASSERT(animation->GetTimeline() == params->mTimeline,
"Animation should refer to this timeline");
// Bug 1174575: Until we implement a suitable PseudoElement interface we
// don't have anything to return for the |target| attribute of
// KeyframeEffect(ReadOnly) objects that refer to pseudo-elements.
// Rather than return some half-baked version of these objects (e.g.
// we a null effect attribute) we simply don't provide access to animations
// whose effect refers to a pseudo-element until we can support them properly.
Element* target;
nsCSSPseudoElements::Type pseudoType;
animation->GetEffect()->GetTarget(target, pseudoType);
if (pseudoType != nsCSSPseudoElements::ePseudo_NotPseudoElement) {
return PL_DHASH_NEXT;
}
params->mSequence.AppendElement(animation);
return PL_DHASH_NEXT;
}
void
AnimationTimeline::GetAnimations(AnimationSequence& aAnimations)
{
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mWindow);
if (mWindow) {
nsIDocument* doc = window->GetDoc();
if (doc) {
doc->FlushPendingNotifications(Flush_Style);
}
}
#ifdef DEBUG
AddAnimationParams params{ aAnimations, this };
#else
AddAnimationParams params{ aAnimations };
#endif
mAnimations.EnumerateEntries(AppendAnimationToSequence, &params);
// Sort animations by priority
aAnimations.Sort(AnimationPtrComparator<nsRefPtr<Animation>>());
}
void
AnimationTimeline::AddAnimation(Animation& aAnimation)
{
mAnimations.PutEntry(&aAnimation);
}
void
AnimationTimeline::RemoveAnimation(Animation& aAnimation)
{
mAnimations.RemoveEntry(&aAnimation);
}
} // namespace dom
} // namespace mozilla

View File

@ -10,10 +10,12 @@
#include "nsISupports.h"
#include "nsWrapperCache.h"
#include "nsCycleCollectionParticipant.h"
#include "js/TypeDecls.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/Attributes.h"
#include "nsHashKeys.h"
#include "nsIGlobalObject.h"
#include "js/TypeDecls.h"
#include "nsTHashtable.h"
namespace mozilla {
namespace dom {
@ -38,8 +40,11 @@ public:
nsIGlobalObject* GetParentObject() const { return mWindow; }
typedef nsTArray<nsRefPtr<Animation>> AnimationSequence;
// AnimationTimeline methods
virtual Nullable<TimeDuration> GetCurrentTime() const = 0;
void GetAnimations(AnimationSequence& aAnimations);
// Wrapper functions for AnimationTimeline DOM methods when called from
// script.
@ -47,8 +52,38 @@ public:
return AnimationUtils::TimeDurationToDouble(GetCurrentTime());
}
/**
* Returns true if the times returned by GetCurrentTime() are convertible
* to and from wallclock-based TimeStamp (e.g. from TimeStamp::Now()) values
* using ToTimelineTime() and ToTimeStamp().
*
* Typically this is true, but it will be false in the case when this
* timeline has no refresh driver or is tied to a refresh driver under test
* control.
*/
virtual bool TracksWallclockTime() const = 0;
/**
* Converts a TimeStamp to the equivalent value in timeline time.
* Note that when TracksWallclockTime() is false, there is no correspondence
* between timeline time and wallclock time. In such a case, passing a
* timestamp from TimeStamp::Now() to this method will not return a
* meaningful result.
*/
virtual Nullable<TimeDuration> ToTimelineTime(const TimeStamp&
aTimeStamp) const = 0;
virtual TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const = 0;
void AddAnimation(Animation& aAnimation);
void RemoveAnimation(Animation& aAnimation);
protected:
nsCOMPtr<nsIGlobalObject> mWindow;
// Animations observing this timeline
typedef nsTHashtable<nsRefPtrHashKey<dom::Animation>> AnimationSet;
AnimationSet mAnimations;
};
} // namespace dom

View File

@ -37,31 +37,22 @@ public:
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
// DocumentTimeline methods
// AnimationTimeline methods
virtual Nullable<TimeDuration> GetCurrentTime() const override;
// Converts a TimeStamp to the equivalent value in timeline time.
// Note that when IsUnderTestControl() is true, there is no correspondence
// between timeline time and wallclock time. In such a case, passing a
// timestamp from TimeStamp::Now() to this method will not return a
// meaningful result.
Nullable<TimeDuration> ToTimelineTime(const TimeStamp& aTimeStamp) const;
TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const;
nsRefreshDriver* GetRefreshDriver() const;
// Returns true if this timeline is driven by a refresh driver that is
// under test control. In such a case, there is no correspondence between
// TimeStamp values returned by the refresh driver and wallclock time.
// As a result, passing a value from TimeStamp::Now() to ToTimelineTime()
// would not return a meaningful result.
bool IsUnderTestControl() const
bool TracksWallclockTime() const override
{
nsRefreshDriver* refreshDriver = GetRefreshDriver();
return refreshDriver && refreshDriver->IsTestControllingRefreshesEnabled();
return !refreshDriver ||
!refreshDriver->IsTestControllingRefreshesEnabled();
}
Nullable<TimeDuration> ToTimelineTime(const TimeStamp& aTimeStamp) const
override;
TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const override;
protected:
TimeStamp GetCurrentTimeStamp() const;
nsRefreshDriver* GetRefreshDriver() const;
nsCOMPtr<nsIDocument> mDocument;

View File

@ -211,15 +211,16 @@ public:
JS::Handle<JSObject*> aGivenProto) override;
virtual ElementPropertyTransition* AsTransition() { return nullptr; }
virtual const ElementPropertyTransition* AsTransition() const {
virtual const ElementPropertyTransition* AsTransition() const
{
return nullptr;
}
// KeyframeEffectReadOnly interface
Element* GetTarget() const {
// Currently we only implement Element.getAnimations() which only
// returns animations targetting Elements so this should never
// be called for an animation that targets a pseudo-element.
// Currently we never return animations from the API whose effect
// targets a pseudo-element so this should never be called when
// mPseudoType is not 'none' (see bug 1174575).
MOZ_ASSERT(mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
"Requesting the target of a KeyframeEffect that targets a"
" pseudo-element is not yet supported.");
@ -227,7 +228,7 @@ public:
}
// Temporary workaround to return both the target element and pseudo-type
// until we implement PseudoElement.
// until we implement PseudoElement (bug 1174575).
void GetTarget(Element*& aTarget,
nsCSSPseudoElements::Type& aPseudoType) const {
aTarget = mTarget;

View File

@ -6,7 +6,7 @@
#include "PendingAnimationTracker.h"
#include "mozilla/dom/DocumentTimeline.h"
#include "mozilla/dom/AnimationTimeline.h"
#include "nsIFrame.h"
#include "nsIPresShell.h"
@ -53,14 +53,21 @@ TriggerAnimationAtTime(nsRefPtrHashKey<dom::Animation>* aKey,
void* aReadyTime)
{
dom::Animation* animation = aKey->GetKey();
dom::DocumentTimeline* timeline = animation->Timeline();
dom::AnimationTimeline* timeline = animation->GetTimeline();
// If the animation does not have a timeline, just drop it from the map.
// The animation will detect that it is not being tracked and will trigger
// itself on the next tick where it has a timeline.
if (!timeline) {
return PL_DHASH_REMOVE;
}
// When the timeline's refresh driver is under test control, its values
// have no correspondance to wallclock times so we shouldn't try to convert
// aReadyTime (which is a wallclock time) to a timeline value. Instead, the
// animation will be started/paused when the refresh driver is next
// advanced since this will trigger a call to TriggerPendingAnimationsNow.
if (timeline->IsUnderTestControl()) {
if (!timeline->TracksWallclockTime()) {
return PL_DHASH_NEXT;
}

View File

@ -16,6 +16,7 @@ EXPORTS.mozilla.dom += [
]
EXPORTS.mozilla += [
'AnimationComparator.h',
'AnimationUtils.h',
'PendingAnimationTracker.h',
]

View File

@ -274,6 +274,36 @@ test(function(t) {
}, 'getAnimations for CSS Animations that are cancelled');
async_test(function(t) {
var div = addDiv(t);
div.style.animation = 'anim2 100s';
div.getAnimations()[0].ready.then(t.step_func(function() {
// Prepend to the list and test that even though anim1 was triggered
// *after* anim2, it should come first because it appears first
// in the animation-name property.
div.style.animation = 'anim1 100s, anim2 100s';
var anims = div.getAnimations();
assert_equals(anims[0].animationName, 'anim1',
'animation order after prepending to list');
assert_equals(anims[1].animationName, 'anim2',
'animation order after prepending to list');
// Normally calling cancel and play would this push anim1 to the top of
// the stack but it shouldn't for CSS animations that map an the
// animation-name property.
var anim1 = anims[0];
anim1.cancel();
anim1.play();
anims = div.getAnimations();
assert_equals(anims[0].animationName, 'anim1',
'animation order after cancelling and restarting');
assert_equals(anims[1].animationName, 'anim2',
'animation order after cancelling and restarting');
t.done();
}));
}, 'getAnimations for CSS Animations follows animation-name order');
done();
</script>
</body>

View File

@ -0,0 +1,215 @@
<!doctype html>
<meta charset=utf-8>
<script src="../testcommon.js"></script>
<style>
@keyframes animLeft {
to { left: 100px }
}
@keyframes animTop {
to { top: 100px }
}
@keyframes animBottom {
to { bottom: 100px }
}
@keyframes animRight {
to { right: 100px }
}
.with-before-animation::before {
content: " ";
animation: animLeft 100s;
}
</style>
<body>
<script>
'use strict';
test(function(t) {
assert_equals(document.timeline.getAnimations().length, 0,
'getAnimations returns an empty sequence for a document'
+ ' with no animations');
}, 'getAnimations for non-animated content');
test(function(t) {
var div = addDiv(t);
// Add an animation
div.style.animation = 'animLeft 100s';
assert_equals(document.timeline.getAnimations().length, 1,
'getAnimations returns a running CSS Animation');
// Add another animation
div.style.animation = 'animLeft 100s, animTop 100s';
assert_equals(document.timeline.getAnimations().length, 2,
'getAnimations returns two running CSS Animations');
// Remove both
div.style.animation = '';
assert_equals(document.timeline.getAnimations().length, 0,
'getAnimations returns no running CSS Animations');
}, 'getAnimations for CSS Animations');
test(function(t) {
var div = addDiv(t);
div.style.animation = 'animLeft 100s, animTop 100s, animRight 100s, ' +
'animBottom 100s';
var animations = document.timeline.getAnimations();
assert_equals(animations.length, 4,
'getAnimations returns all running CSS Animations');
assert_equals(animations[0].animationName, 'animLeft',
'Order of first animation returned');
assert_equals(animations[1].animationName, 'animTop',
'Order of second animation returned');
assert_equals(animations[2].animationName, 'animRight',
'Order of third animation returned');
assert_equals(animations[3].animationName, 'animBottom',
'Order of fourth animation returned');
}, 'Order of CSS Animations - within an element');
test(function(t) {
var div1 = addDiv(t, { style: 'animation: animLeft 100s' });
var div2 = addDiv(t, { style: 'animation: animLeft 100s' });
var div3 = addDiv(t, { style: 'animation: animLeft 100s' });
var div4 = addDiv(t, { style: 'animation: animLeft 100s' });
var animations = document.timeline.getAnimations();
assert_equals(animations.length, 4,
'getAnimations returns all running CSS Animations');
assert_equals(animations[0].effect.target, div1,
'Order of first animation returned');
assert_equals(animations[1].effect.target, div2,
'Order of second animation returned');
assert_equals(animations[2].effect.target, div3,
'Order of third animation returned');
assert_equals(animations[3].effect.target, div4,
'Order of fourth animation returned');
// Order should be depth-first pre-order so add some depth as follows:
//
// <parent>
// / |
// 2 3
// / \
// 1 4
//
// Which should give: 2, 1, 4, 3
div2.appendChild(div1);
div2.appendChild(div4);
animations = document.timeline.getAnimations();
assert_equals(animations[0].effect.target, div2,
'Order of first animation returned after tree surgery');
assert_equals(animations[1].effect.target, div1,
'Order of second animation returned after tree surgery');
assert_equals(animations[2].effect.target, div4,
'Order of third animation returned after tree surgery');
assert_equals(animations[3].effect.target, div3,
'Order of fourth animation returned after tree surgery');
}, 'Order of CSS Animations - across elements');
test(function(t) {
var div1 = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
var div2 = addDiv(t, { style: 'animation: animBottom 100s' });
var expectedResults = [ [ div1, 'animLeft' ],
[ div1, 'animTop' ],
[ div2, 'animBottom' ] ];
var animations = document.timeline.getAnimations();
assert_equals(animations.length, expectedResults.length,
'getAnimations returns all running CSS Animations');
animations.forEach(function(anim, i) {
assert_equals(anim.effect.target, expectedResults[i][0],
'Target of animation in position ' + i);
assert_equals(anim.animationName, expectedResults[i][1],
'Name of animation in position ' + i);
});
// Modify tree structure and animation list
div2.appendChild(div1);
div1.style.animation = 'animLeft 100s, animRight 100s, animTop 100s';
expectedResults = [ [ div2, 'animBottom' ],
[ div1, 'animLeft' ],
[ div1, 'animRight' ],
[ div1, 'animTop' ] ];
animations = document.timeline.getAnimations();
assert_equals(animations.length, expectedResults.length,
'getAnimations returns all running CSS Animations after ' +
'making changes');
animations.forEach(function(anim, i) {
assert_equals(anim.effect.target, expectedResults[i][0],
'Target of animation in position ' + i + ' after changes');
assert_equals(anim.animationName, expectedResults[i][1],
'Name of animation in position ' + i + ' after changes');
});
}, 'Order of CSS Animations - across and within elements');
test(function(t) {
// Add an animation first
var div = addDiv(t, { style: 'animation: animLeft 100s' });
div.style.top = '0px';
div.style.transition = 'all 100s';
flushComputedStyle(div);
// *Then* add a transition
div.style.top = '100px';
flushComputedStyle(div);
// Although the transition was added later, it should come first in the list
var animations = document.timeline.getAnimations();
assert_equals(animations.length, 2,
'Both CSS animations and transitions are returned');
assert_class_string(animations[0], 'CSSTransition', 'Transition comes first');
assert_class_string(animations[1], 'CSSAnimation', 'Animation comes second');
}, 'Order of CSS Animations and CSS Transitions');
test(function(t) {
var div = addDiv(t, { style: 'animation: animLeft 100s forwards' });
div.getAnimations()[0].finish();
assert_equals(document.timeline.getAnimations().length, 1,
'Forwards-filling CSS animations are returned');
}, 'Finished but filling CSS Animations are returned');
test(function(t) {
var div = addDiv(t, { style: 'animation: animLeft 100s' });
div.getAnimations()[0].finish();
assert_equals(document.timeline.getAnimations().length, 0,
'Non-filling finished CSS animations are not returned');
}, 'Finished but not filling CSS Animations are not returned');
test(function(t) {
var div = addDiv(t, { style: 'animation: animLeft 100s 100s' });
assert_equals(document.timeline.getAnimations().length, 1,
'Yet-to-start CSS animations are returned');
}, 'Yet-to-start CSS Animations are returned');
test(function(t) {
var div = addDiv(t, { style: 'animation: animLeft 100s' });
div.getAnimations()[0].cancel();
assert_equals(document.timeline.getAnimations().length, 0,
'CSS animations cancelled by the API are not returned');
}, 'CSS Animations cancelled via the API are not returned');
test(function(t) {
var div = addDiv(t, { style: 'animation: animLeft 100s' });
var anim = div.getAnimations()[0];
anim.cancel();
anim.play();
assert_equals(document.timeline.getAnimations().length, 1,
'CSS animations cancelled and restarted by the API are ' +
'returned');
}, 'CSS Animations cancelled and restarted via the API are returned');
test(function(t) {
var div = addDiv(t, { class: 'with-before-animation' });
// FIXME: This should actually return the animation on the pseudo element
// but we haven't implemented a PseudoElement interface to use as
// animation's target (bug 1174575) so we simply don't return these animations
// until we can support them properly.
assert_equals(document.timeline.getAnimations().length, 0,
'CSS animations on pseudo elements are not returned');
}, 'CSS Animations targetting pseudo-elements are not returned');
done();
</script>
</body>

View File

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
'use strict';
setup({explicit_done: true});
SpecialPowers.pushPrefEnv(
{ "set": [["dom.animations-api.core.enabled", true]]},
function() {
window.open("file_timeline-get-animations.html");
});
</script>
</html>

View File

@ -37,7 +37,7 @@ async_test(function(t) {
'getAnimations returns Animations for all running CSS Transitions');
return waitForAllAnimations(animations);
})).then(t.step_func(function() {
assert_true(animations[1].startTime < animations[2].startTime,
assert_less_than(animations[1].startTime, animations[2].startTime,
'Animation for additional CSS transition starts after the original'
+ ' transitions and appears later in the list');
t.done();
@ -101,6 +101,47 @@ test(function(t) {
+ ' of an unsupported property');
}, 'getAnimations for transition on unsupported property');
test(function(t) {
var div = addDiv(t, { style: 'transform: translate(0px); ' +
'opacity: 0; ' +
'border-width: 0px; ' + // Shorthand
'border-style: solid' });
getComputedStyle(div).transform;
div.style.transition = 'all 100s';
div.style.transform = 'translate(100px)';
div.style.opacity = '1';
div.style.borderWidth = '1px';
var animations = div.getAnimations();
assert_equals(animations.length, 6,
'Generated expected number of transitions');
assert_equals(animations[0].transitionProperty, 'border-bottom-width');
assert_equals(animations[1].transitionProperty, 'border-left-width');
assert_equals(animations[2].transitionProperty, 'border-right-width');
assert_equals(animations[3].transitionProperty, 'border-top-width');
assert_equals(animations[4].transitionProperty, 'opacity');
assert_equals(animations[5].transitionProperty, 'transform');
}, 'getAnimations sorts simultaneous transitions by name');
test(function(t) {
var div = addDiv(t, { style: 'transform: translate(0px); ' +
'opacity: 0' });
getComputedStyle(div).transform;
div.style.transition = 'all 100s';
div.style.transform = 'translate(100px)';
assert_equals(div.getAnimations().length, 1,
'Initially there is only one (transform) transition');
div.style.opacity = '1';
assert_equals(div.getAnimations().length, 2,
'Then a second (opacity) transition is added');
var animations = div.getAnimations();
assert_equals(animations[0].transitionProperty, 'transform');
assert_equals(animations[1].transitionProperty, 'opacity');
}, 'getAnimations sorts transitions by when they were generated');
done();
</script>
</body>

View File

@ -0,0 +1,50 @@
<!doctype html>
<meta charset=utf-8>
<script src="../testcommon.js"></script>
<body>
<script>
'use strict';
test(function(t) {
assert_equals(document.timeline.getAnimations().length, 0,
'getAnimations returns an empty sequence for a document'
+ ' with no animations');
}, 'getAnimations for non-animated content');
test(function(t) {
var div = addDiv(t);
// Add a couple of transitions
div.style.left = '0px';
div.style.top = '0px';
getComputedStyle(div).transitionProperty;
div.style.transition = 'all 100s';
div.style.left = '100px';
div.style.top = '100px';
assert_equals(document.timeline.getAnimations().length, 2,
'getAnimations returns two running CSS Transitions');
// Remove both
div.style.transitionProperty = 'none';
assert_equals(document.timeline.getAnimations().length, 0,
'getAnimations returns no running CSS Transitions');
}, 'getAnimations for CSS Transitions');
async_test(function(t) {
var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
flushComputedStyle(div);
div.style.left = '100px';
var animations = div.getAnimations();
assert_equals(animations.length, 1, 'Got transition');
animations[0].finished.then(t.step_func(function() {
assert_equals(document.timeline.getAnimations().length, 0,
'No animations returned');
t.done();
}));
}, 'Transitions are not returned after they have finished');
done();
</script>
</body>

View File

@ -0,0 +1,15 @@
<!doctype html>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id="log"></div>
<script>
'use strict';
setup({explicit_done: true});
SpecialPowers.pushPrefEnv(
{ "set": [["dom.animations-api.core.enabled", true]]},
function() {
window.open("file_timeline-get-animations.html");
});
</script>
</html>

View File

@ -31,6 +31,8 @@ support-files = css-animations/file_effect-target.html
[css-animations/test_element-get-animations.html]
skip-if = buildapp == 'mulet'
support-files = css-animations/file_element-get-animations.html
[css-animations/test_timeline-get-animations.html]
support-files = css-animations/file_timeline-get-animations.html
[css-transitions/test_animation-cancel.html]
support-files = css-transitions/file_animation-cancel.html
[css-transitions/test_animation-currenttime.html]
@ -50,6 +52,8 @@ support-files = css-transitions/file_effect-target.html
[css-transitions/test_element-get-animations.html]
skip-if = buildapp == 'mulet'
support-files = css-transitions/file_element-get-animations.html
[css-transitions/test_timeline-get-animations.html]
support-files = css-transitions/file_timeline-get-animations.html
[document-timeline/test_document-timeline.html]
support-files = document-timeline/file_document-timeline.html
[document-timeline/test_request_animation_frame.html]

View File

@ -78,8 +78,9 @@ function flushComputedStyle(elem) {
}
for (var funcName of ["async_test", "assert_not_equals", "assert_equals",
"assert_approx_equals", "assert_less_than_equal",
"assert_between_inclusive", "assert_true", "assert_false",
"assert_approx_equals", "assert_less_than",
"assert_less_than_equal", "assert_between_inclusive",
"assert_true", "assert_false",
"assert_class_string", "assert_throws", "test"]) {
window[funcName] = opener[funcName].bind(opener);
}

View File

@ -131,8 +131,12 @@ this.ImportExport = {
}
let files = [];
debug("aApp=" + uneval(aApp));
if (aApp.origin.startsWith("app://")) {
files.push("update.webapp");
// Apps sideloaded from WebIDE don't have an update manifest.
if (!aApp.sideloaded) {
files.push("update.webapp");
}
files.push("application.zip");
} else {
files.push("manifest.webapp");
@ -215,18 +219,19 @@ this.ImportExport = {
_importPackagedApp: function(aZipReader, aManifestURL, aDir) {
debug("Importing packaged app " + aManifestURL);
if (!aZipReader.hasEntry("update.webapp")) {
throw "NoUpdateManifestFound";
}
if (!aZipReader.hasEntry("application.zip")) {
throw "NoPackageFound";
}
// The order matters, application.zip needs to be the last element.
let files = [];
aZipReader.hasEntry("update.webapp") && files.push("update.webapp");
files.push("application.zip");
// Extract application.zip and update.webapp
// We get manifest.webapp from application.zip itself.
let file;
["update.webapp", "application.zip"].forEach((aName) => {
files.forEach((aName) => {
file = aDir.clone();
file.append(aName);
aZipReader.extract(aName, file);

View File

@ -51,6 +51,7 @@
#include "nsDOMString.h"
#include "nsIScriptSecurityManager.h"
#include "nsIDOMMutationEvent.h"
#include "mozilla/AnimationComparator.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventDispatcher.h"
@ -3201,6 +3202,8 @@ Element::GetAnimations(nsTArray<nsRefPtr<Animation>>& aAnimations)
}
}
}
aAnimations.Sort(AnimationPtrComparator<nsRefPtr<Animation>>());
}
NS_IMETHODIMP

View File

@ -0,0 +1,14 @@
<!DOCTYPE html>
<body>
<script>
var obs = new MutationObserver(function() {
// Just need something here to assert exception is not pending. Any
// binding method will do.
console.log("hello");
});
obs.observe(document.body, { childList: true });
</script>
<script>
noSuchMethodYo();
</script>
</body>

View File

@ -203,3 +203,4 @@ load xhr_html_nullresponse.html
load structured_clone_container_throws.html
load 1154598.xhtml
load 1157995.html
load 1181619.html

View File

@ -82,6 +82,7 @@
#include "nsIRefreshURI.h"
#include "nsIWebNavigation.h"
#include "nsIScriptError.h"
#include "nsISimpleEnumerator.h"
#include "nsStyleSheetService.h"
#include "nsNetUtil.h" // for NS_NewURI
@ -11067,7 +11068,8 @@ public:
NS_IMETHOD Run()
{
if (mDoc->GetWindow()) {
mDoc->GetWindow()->SetFullScreenInternal(mValue, false, mHMD);
mDoc->GetWindow()->SetFullscreenInternal(
nsPIDOMWindow::eForFullscreenAPI, mValue, mHMD);
}
return NS_OK;
}

View File

@ -6033,7 +6033,7 @@ nsGlobalWindow::SetFullScreen(bool aFullScreen, mozilla::ErrorResult& aError)
{
FORWARD_TO_OUTER_OR_THROW(SetFullScreen, (aFullScreen, aError), aError, /* void */);
aError = SetFullScreenInternal(aFullScreen, true);
aError = SetFullscreenInternal(eForFullscreenMode, aFullScreen);
}
NS_IMETHODIMP
@ -6041,7 +6041,7 @@ nsGlobalWindow::SetFullScreen(bool aFullScreen)
{
FORWARD_TO_OUTER(SetFullScreen, (aFullScreen), NS_ERROR_NOT_INITIALIZED);
return SetFullScreenInternal(aFullScreen, true);
return SetFullscreenInternal(eForFullscreenMode, aFullScreen);
}
void
@ -6060,7 +6060,8 @@ FinishDOMFullscreenChange(nsIDocument* aDoc, bool aInDOMFullscreen)
}
nsresult
nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aFullscreenMode,
nsGlobalWindow::SetFullscreenInternal(FullscreenReason aReason,
bool aFullScreen,
gfx::VRHMDInfo* aHMD)
{
MOZ_ASSERT(IsOuterWindow());
@ -6070,7 +6071,7 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aFullscreenMode,
// Only chrome can change our fullscreen mode. Otherwise, the state
// can only be changed for DOM fullscreen.
if (aFullScreen == FullScreen() ||
(aFullscreenMode && !nsContentUtils::IsCallerChrome())) {
(aReason == eForFullscreenMode && !nsContentUtils::IsCallerChrome())) {
return NS_OK;
}
@ -6083,7 +6084,7 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aFullscreenMode,
if (!window)
return NS_ERROR_FAILURE;
if (rootItem != mDocShell)
return window->SetFullScreenInternal(aFullScreen, aFullscreenMode, aHMD);
return window->SetFullscreenInternal(aReason, aFullScreen, aHMD);
// make sure we don't try to set full screen on a non-chrome window,
// which might happen in embedding world
@ -6097,7 +6098,7 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aFullscreenMode,
// Note that although entering DOM fullscreen could also cause
// consequential calls to this method, those calls will be skipped
// at the condition above.
if (aFullscreenMode) {
if (aReason == eForFullscreenMode) {
mFullscreenMode = aFullScreen;
} else {
// If we are exiting from DOM fullscreen while we initially make
@ -6132,7 +6133,7 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aFullscreenMode,
if (aHMD) {
screen = aHMD->GetScreen();
}
if (!aFullscreenMode) {
if (aReason == eForFullscreenAPI) {
widget->PrepareForDOMFullscreenTransition();
}
widget->MakeFullScreen(aFullScreen, screen);
@ -12013,6 +12014,7 @@ nsGlobalWindow::RunTimeoutHandler(nsTimeout* aTimeout,
// New script entry point required, due to the "Create a script" sub-step of
// http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
nsAutoMicroTask mt;
AutoEntryScript entryScript(this, reason, true, aScx->GetNativeContext());
entryScript.TakeOwnershipOfErrorReporting();
JS::CompileOptions options(entryScript.cx());

View File

@ -485,8 +485,9 @@ public:
virtual void RefreshCompartmentPrincipal() override;
// Outer windows only.
virtual nsresult SetFullScreenInternal(bool aIsFullscreen, bool aFullscreenMode,
mozilla::gfx::VRHMDInfo *aHMD = nullptr) override final;
virtual nsresult SetFullscreenInternal(
FullscreenReason aReason, bool aIsFullscreen,
mozilla::gfx::VRHMDInfo *aHMD = nullptr) override final;
virtual void FinishFullscreenChange(bool aIsFullscreen) override final;
bool FullScreen() const;

View File

@ -60,8 +60,10 @@
#ifdef MOZ_NFC
#include "mozilla/dom/MozNDEFRecord.h"
#endif // MOZ_NFC
#ifdef MOZ_WEBRTC
#include "mozilla/dom/RTCCertificate.h"
#include "mozilla/dom/RTCCertificateBinding.h"
#endif
#include "mozilla/dom/StructuredClone.h"
#include "mozilla/dom/SubtleCryptoBinding.h"
#include "mozilla/ipc/BackgroundUtils.h"
@ -2549,6 +2551,7 @@ NS_DOMReadStructuredClone(JSContext* cx,
}
if (tag == SCTAG_DOM_RTC_CERTIFICATE) {
#ifdef MOZ_WEBRTC
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
if (!global) {
return nullptr;
@ -2565,6 +2568,9 @@ NS_DOMReadStructuredClone(JSContext* cx,
}
}
return result;
#else
return nullptr;
#endif
}
// Don't know what this is. Bail.
@ -2591,12 +2597,14 @@ NS_DOMWriteStructuredClone(JSContext* cx,
key->WriteStructuredClone(writer);
}
#ifdef MOZ_WEBRTC
// Handle WebRTC Certificate cloning
RTCCertificate* cert;
if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, obj, cert))) {
return JS_WriteUint32Pair(writer, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
cert->WriteStructuredClone(writer);
}
#endif
if (xpc::IsReflector(obj)) {
nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);

View File

@ -174,6 +174,8 @@ nsJSUtils::EvaluateString(JSContext* aCx,
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
aEvaluationGlobal);
MOZ_ASSERT_IF(aOffThreadToken, aCompileOptions.noScriptRval);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(nsContentUtils::IsInMicroTask());
// Unfortunately, the JS engine actually compiles scripts with a return value
// in a different, less efficient way. Furthermore, it can't JIT them in many
@ -183,7 +185,6 @@ nsJSUtils::EvaluateString(JSContext* aCx,
// aCompileOptions.noScriptRval set to true.
aRetValue.setUndefined();
nsAutoMicroTask mt;
nsresult rv = NS_OK;
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();

View File

@ -62,8 +62,8 @@ enum UIStateChangeType
};
#define NS_PIDOMWINDOW_IID \
{ 0x0df7578f, 0x31d4, 0x4391, \
{ 0x98, 0xd4, 0xf3, 0x7d, 0x51, 0x8e, 0xa4, 0x37 } }
{ 0x2aebbbd7, 0x154b, 0x4341, \
{ 0x8d, 0x02, 0x7f, 0x70, 0xf8, 0x3e, 0xf7, 0xa1 } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -461,24 +461,31 @@ public:
}
}
enum FullscreenReason
{
// Toggling the fullscreen mode requires trusted context.
eForFullscreenMode,
// Fullscreen API is the API provided to untrusted content.
eForFullscreenAPI
};
/**
* Moves the top-level window into fullscreen mode if aIsFullScreen is true,
* otherwise exits fullscreen. If aFullscreenMode is true, this method is
* called for fullscreen mode instead of DOM fullscreen, which means it can
* only change window state in a context trusted for write.
* otherwise exits fullscreen.
*
* If aHMD is not null, the window is made full screen on the given VR HMD
* device instead of its currrent display.
*
* Outer windows only.
*/
virtual nsresult SetFullScreenInternal(bool aIsFullscreen, bool aFullscreenMode,
mozilla::gfx::VRHMDInfo *aHMD = nullptr) = 0;
virtual nsresult SetFullscreenInternal(
FullscreenReason aReason, bool aIsFullscreen,
mozilla::gfx::VRHMDInfo *aHMD = nullptr) = 0;
/**
* This function should be called when the fullscreen state is flipped.
* If no widget is involved the fullscreen change, this method is called
* by SetFullScreenInternal, otherwise, it is called when the widget
* by SetFullscreenInternal, otherwise, it is called when the widget
* finishes its change to or from fullscreen.
*
* @param aIsFullscreen indicates whether the widget is in fullscreen.

View File

@ -1113,6 +1113,7 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
// New script entry point required, due to the "Create a script" sub-step of
// http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
nsAutoMicroTask mt;
AutoEntryScript entryScript(globalObject, "<script> element", true,
context->GetNativeContext());
entryScript.TakeOwnershipOfErrorReporting();

View File

@ -184,7 +184,7 @@ GetCurrentJSStack()
cx = workers::GetCurrentThreadJSContext();
}
if (!cx) {
if (!cx || !js::GetContextCompartment(cx)) {
return nullptr;
}

View File

@ -971,10 +971,9 @@ BrowserElementChild.prototype = {
},
_mozScrollAreaChanged: function(e) {
let dimensions = this._getContentDimensions();
sendAsyncMsg('scrollareachanged', {
width: dimensions.width,
height: dimensions.height
width: e.width,
height: e.height
});
},

View File

@ -31,11 +31,11 @@ namespace dom {
namespace cache {
namespace db {
const int32_t kMaxWipeSchemaVersion = 14;
const int32_t kMaxWipeSchemaVersion = 15;
namespace {
const int32_t kLatestSchemaVersion = 14;
const int32_t kLatestSchemaVersion = 15;
const int32_t kMaxEntriesPerStatement = 255;
const uint32_t kPageSize = 4 * 1024;
@ -1833,7 +1833,6 @@ ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
if (!serializedInfo.IsEmpty()) {
nsAutoCString originNoSuffix;
OriginAttributes attrs;
fprintf(stderr, "\n%s\n", serializedInfo.get());
if (!attrs.PopulateFromOrigin(serializedInfo, originNoSuffix)) {
NS_WARNING("Something went wrong parsing a serialized principal!");
return NS_ERROR_FAILURE;

View File

@ -57,13 +57,12 @@ void
WebGLContextLossHandler::TimerCallback()
{
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
MOZ_ASSERT(mIsTimerRunning);
mIsTimerRunning = false;
if (mIsDisabled)
return;
MOZ_ASSERT(mIsTimerRunning);
mIsTimerRunning = false;
// If we need to run the timer again, restart it immediately.
// Otherwise, the code we call into below might *also* try to
// restart it.
@ -100,7 +99,7 @@ WebGLContextLossHandler::RunTimer()
void
WebGLContextLossHandler::DisableTimer()
{
if (!mIsDisabled)
if (mIsDisabled)
return;
mIsDisabled = true;

View File

@ -2095,6 +2095,7 @@ HTMLMediaElement::HTMLMediaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNo
mAudioChannelFaded(false),
mPlayingThroughTheAudioChannel(false),
mDisableVideo(false),
mPlayBlockedBecauseHidden(false),
mElementInTreeState(ELEMENT_NOT_INTREE)
{
if (!gMediaElementLog) {
@ -2224,6 +2225,15 @@ HTMLMediaElement::Play(ErrorResult& aRv)
if (mSuspendedForPreloadNone) {
ResumeLoad(PRELOAD_ENOUGH);
}
if (Preferences::GetBool("media.block-play-until-visible", false) &&
!nsContentUtils::IsCallerChrome() &&
OwnerDoc()->Hidden()) {
LOG(LogLevel::Debug, ("%p Blocked playback because owner hidden.", this));
mPlayBlockedBecauseHidden = true;
return;
}
// Even if we just did Load() or ResumeLoad(), we could already have a decoder
// here if we managed to clone an existing decoder.
if (mDecoder) {
@ -3752,23 +3762,33 @@ bool HTMLMediaElement::CanActivateAutoplay()
void HTMLMediaElement::CheckAutoplayDataReady()
{
if (CanActivateAutoplay()) {
mPaused = false;
// We changed mPaused which can affect AddRemoveSelfReference
AddRemoveSelfReference();
if (mDecoder) {
SetPlayedOrSeeked(true);
if (mCurrentPlayRangeStart == -1.0) {
mCurrentPlayRangeStart = CurrentTime();
}
mDecoder->Play();
} else if (mSrcStream) {
SetPlayedOrSeeked(true);
GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
}
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
if (!CanActivateAutoplay()) {
return;
}
if (Preferences::GetBool("media.block-play-until-visible", false) &&
OwnerDoc()->Hidden()) {
LOG(LogLevel::Debug, ("%p Blocked autoplay because owner hidden.", this));
mPlayBlockedBecauseHidden = true;
return;
}
mPaused = false;
// We changed mPaused which can affect AddRemoveSelfReference
AddRemoveSelfReference();
if (mDecoder) {
SetPlayedOrSeeked(true);
if (mCurrentPlayRangeStart == -1.0) {
mCurrentPlayRangeStart = CurrentTime();
}
mDecoder->Play();
} else if (mSrcStream) {
SetPlayedOrSeeked(true);
GetSrcMediaStream()->ChangeExplicitBlockerCount(-1);
}
DispatchAsyncEvent(NS_LITERAL_STRING("play"));
}
bool HTMLMediaElement::IsActive()
@ -3950,6 +3970,9 @@ void HTMLMediaElement::UpdateInitialMediaSize(const nsIntSize& aSize)
void HTMLMediaElement::SuspendOrResumeElement(bool aPauseElement, bool aSuspendEvents)
{
LOG(LogLevel::Debug, ("%p SuspendOrResumeElement(pause=%d, suspendEvents=%d) hidden=%d",
this, aPauseElement, aSuspendEvents, OwnerDoc()->Hidden()));
if (aPauseElement != mPausedForInactiveDocumentOrChannel) {
mPausedForInactiveDocumentOrChannel = aPauseElement;
if (aPauseElement) {
@ -4031,6 +4054,14 @@ void HTMLMediaElement::NotifyOwnerDocumentActivityChanged()
SuspendOrResumeElement(pauseElement, !IsActive());
if (!mPausedForInactiveDocumentOrChannel &&
mPlayBlockedBecauseHidden &&
!OwnerDoc()->Hidden()) {
LOG(LogLevel::Debug, ("%p Resuming playback now that owner doc is visble.", this));
mPlayBlockedBecauseHidden = false;
Play();
}
AddRemoveSelfReference();
}

View File

@ -1374,6 +1374,11 @@ protected:
// playback.
bool mDisableVideo;
// True if we blocked either a play() call or autoplay because the
// media's owner doc was not visible. Only enforced when the pref
// media.block-play-until-visible=true.
bool mPlayBlockedBecauseHidden;
// An agent used to join audio channel service.
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;

View File

@ -45,32 +45,13 @@ var gTestWindows = [
var testWindow = null;
var gTestIndex = 0;
// TODO: if ever we remove these checks for XP and Lion, we should do the same
// in dom/tests/mochitest/pointerlock/test_pointerlock-api.html, which uses the same pattern.
const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
const isOSXYosemite = navigator.userAgent.indexOf("Mac OS X 10.10") != -1;
function finish() {
SimpleTest.finish();
}
function nextTest() {
if (isWinXP) {
todo(false, "Can't reliably run full-screen tests on Windows XP due to bug 704010");
SimpleTest.finish();
return;
}
if (testWindow) {
testWindow.close();
if (isOSXLion || isOSXMtnLion || isOSXYosemite) {
// On OS X Lion, tests cause problems. Timeouts are a bad way to get around
// the problem and may lead to future [orange], but they are the only option
// at this point.
SimpleTest.waitForFocus(function() { setTimeout(runNextTest, 3000); });
return;
}
}
runNextTest();
}
@ -95,24 +76,20 @@ function runNextTest() {
}
}
if (isOSXLion || isOSXMtnLion || isOSXYosemite) {
todo(false, "Can't reliably run full-screen tests on OS X (bug 900453 comment 18 & bug 802504)");
} else {
try {
window.fullScreen = true;
} catch (e) {
}
is(window.fullScreen, false, "Shouldn't be able to set window fullscreen from content");
// Ensure the full-screen api is enabled, and will be disabled on test exit.
// Disable the requirement for trusted contexts only, so the tests are easier
// to write
addLoadEvent(function() {
SpecialPowers.pushPrefEnv({
"set":[["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false]]}, nextTest);
});
SimpleTest.waitForExplicitFinish();
try {
window.fullScreen = true;
} catch (e) {
}
is(window.fullScreen, false, "Shouldn't be able to set window fullscreen from content");
// Ensure the full-screen api is enabled, and will be disabled on test exit.
// Disable the requirement for trusted contexts only, so the tests are easier
// to write
addLoadEvent(function() {
SpecialPowers.pushPrefEnv({
"set":[["full-screen-api.enabled", true],
["full-screen-api.allow-trusted-requests-only", false]]}, nextTest);
});
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>

View File

@ -250,6 +250,7 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
// New script entry point required, due to the "Create a script" step of
// http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
nsAutoMicroTask mt;
AutoEntryScript entryScript(innerGlobal, "javascript: URI", true,
scriptContext->GetNativeContext());
// We want to make sure we report any exceptions that happen before we

View File

@ -68,9 +68,8 @@ AudioSink::AudioSink(MediaDecoderStateMachine* aStateMachine,
, mSetPlaybackRate(false)
, mSetPreservesPitch(false)
, mPlaying(true)
, mOnAudioEndTimeUpdateTask(new OnAudioEndTimeUpdateTask(aStateMachine))
{
NS_ASSERTION(mStartTime != -1, "Should have audio start time by now");
mOnAudioEndTimeUpdateTask = new OnAudioEndTimeUpdateTask(aStateMachine);
}
nsresult
@ -107,7 +106,7 @@ AudioSink::GetPosition()
mLastGoodPosition = pos;
}
return mLastGoodPosition;
return mStartTime + mLastGoodPosition;
}
bool

View File

@ -113,7 +113,7 @@ private:
// microseconds. We can add this to the audio stream position to determine
// the current audio time. Accessed on audio and state machine thread.
// Synchronized by decoder monitor.
int64_t mStartTime;
const int64_t mStartTime;
// PCM frames written to the stream so far.
int64_t mWritten;

View File

@ -212,7 +212,6 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mReader(aReader),
mCurrentPosition(mTaskQueue, 0, "MediaDecoderStateMachine::mCurrentPosition (Canonical)"),
mStreamStartTime(0),
mAudioStartTime(0),
mAudioEndTime(-1),
mDecodedAudioEndTime(-1),
mVideoFrameEndTime(-1),
@ -1745,11 +1744,11 @@ MediaDecoderStateMachine::StartAudioThread()
}
if (HasAudio() && !mAudioSink) {
auto audioStartTime = GetMediaTime();
// The audio end time should always be at least the audio start time.
mAudioEndTime = mAudioStartTime;
MOZ_ASSERT(mAudioStartTime == GetMediaTime());
mAudioEndTime = audioStartTime;
mAudioCompleted = false;
mAudioSink = new AudioSink(this, mAudioStartTime,
mAudioSink = new AudioSink(this, audioStartTime,
mInfo.mAudio, mDecoder->GetAudioChannel());
// OnAudioSinkError() will be called before Init() returns if an error
// occurs during initialization.
@ -2129,7 +2128,7 @@ MediaDecoderStateMachine::SeekCompleted()
// Setup timestamp state.
nsRefPtr<VideoData> video = VideoQueue().PeekFront();
if (seekTime == Duration().ToMicroseconds()) {
newCurrentTime = mAudioStartTime = seekTime;
newCurrentTime = seekTime;
} else if (HasAudio()) {
AudioData* audio = AudioQueue().PeekFront();
// Though we adjust the newCurrentTime in audio-based, and supplemented
@ -2140,7 +2139,7 @@ MediaDecoderStateMachine::SeekCompleted()
// seekTime is bounded in suitable duration. See Bug 1112438.
int64_t videoStart = video ? video->mTime : seekTime;
int64_t audioStart = audio ? audio->mTime : seekTime;
newCurrentTime = mAudioStartTime = std::min(audioStart, videoStart);
newCurrentTime = std::min(audioStart, videoStart);
} else {
newCurrentTime = video ? video->mTime : seekTime;
}
@ -2464,7 +2463,6 @@ MediaDecoderStateMachine::Reset()
mVideoFrameEndTime = -1;
mDecodedVideoEndTime = -1;
mStreamStartTime = 0;
mAudioStartTime = 0;
mAudioEndTime = -1;
mDecodedAudioEndTime = -1;
mAudioCompleted = false;
@ -2589,9 +2587,11 @@ MediaDecoderStateMachine::GetAudioClock() const
// audio sink to ensure that it doesn't get destroyed on the audio sink
// while we're using it.
AssertCurrentThreadInMonitor();
MOZ_ASSERT(HasAudio() && !mAudioCompleted);
return mAudioStartTime +
(mAudioSink ? mAudioSink->GetPosition() : 0);
MOZ_ASSERT(HasAudio() && !mAudioCompleted && IsPlaying());
// Since this function is called while we are playing and AudioSink is
// created once playback starts, mAudioSink is guaranteed to be non-null.
MOZ_ASSERT(mAudioSink);
return mAudioSink->GetPosition();
}
int64_t MediaDecoderStateMachine::GetStreamClock() const
@ -2653,8 +2653,6 @@ void MediaDecoderStateMachine::UpdateRenderedVideoFrames()
{
MOZ_ASSERT(OnTaskQueue());
AssertCurrentThreadInMonitor();
NS_ASSERTION(!HasAudio() || mAudioStartTime != -1,
"Should know audio start time if we have audio.");
if (!IsPlaying() || mLogicallySeeking) {
return;

View File

@ -1070,12 +1070,6 @@ protected:
// media stream.
int64_t mStreamStartTime;
// The presentation time of the first audio frame that was played in
// microseconds. We can add this to the audio stream position to determine
// the current audio time. Accessed on audio and state machine thread.
// Synchronized by decoder monitor.
int64_t mAudioStartTime;
// The end time of the last audio frame that's been pushed onto the audio
// hardware in microseconds. This will approximately be the end time of the
// audio stream, unless another frame is pushed to the hardware.

View File

@ -412,9 +412,7 @@ MediaFormatReader::EnsureDecodersSetup()
}
MOZ_ASSERT(proxy);
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy,
HasAudio(),
HasVideo());
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy);
NS_ENSURE_TRUE(mPlatform, false);
#else
// EME not supported.
@ -1438,7 +1436,8 @@ void MediaFormatReader::ReleaseMediaResources()
{
// Before freeing a video codec, all video buffers needed to be released
// even from graphics pipeline.
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
VideoFrameContainer* container =
mDecoder ? mDecoder->GetVideoFrameContainer() : nullptr;
if (container) {
container->ClearCurrentFrame();
}

View File

@ -26,6 +26,7 @@
#include "nsAppDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
namespace mozilla {
namespace dom {
@ -100,6 +101,11 @@ static bool
AdobePluginFileExists(const nsACString& aVersionStr,
const nsAString& aFilename)
{
if (XRE_GetProcessType() != GeckoProcessType_Default) {
NS_WARNING("AdobePluginFileExists() lying because it doesn't work with e10s");
return true;
}
nsCOMPtr<nsIFile> path;
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(path));
if (NS_WARN_IF(NS_FAILED(rv))) {

View File

@ -325,6 +325,7 @@ MP4TrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
int64_t
MP4TrackDemuxer::GetEvictionOffset(media::TimeUnit aTime)
{
EnsureUpToDateIndex();
MonitorAutoLock mon(mMonitor);
uint64_t offset = mIndex->GetEvictionOffset(aTime.ToMicroseconds());
return int64_t(offset == std::numeric_limits<uint64_t>::max() ? 0 : offset);

View File

@ -466,9 +466,7 @@ MP4Reader::EnsureDecodersSetup()
}
MOZ_ASSERT(proxy);
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy,
HasAudio(),
HasVideo());
mPlatform = PlatformDecoderModule::CreateCDMWrapper(proxy);
NS_ENSURE_TRUE(mPlatform, false);
#else
// EME not supported.

View File

@ -205,6 +205,8 @@ TrackBuffer::BufferAppend()
decoders.AppendElement(mCurrentDecoder);
}
mLastAppendRange = Interval<int64_t>();
if (gotMedia) {
if (mParser->IsMediaSegmentPresent(mInputBuffer) && mLastEndTimestamp &&
(!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
@ -225,6 +227,7 @@ TrackBuffer::BufferAppend()
}
MSE_DEBUG("Decoder marked as initialized.");
AppendDataToCurrentResource(oldInit, 0);
mLastAppendRange = Interval<int64_t>(0, int64_t(oldInit->Length()));
}
mLastStartTimestamp = start;
} else {
@ -251,8 +254,10 @@ TrackBuffer::BufferAppend()
return p;
}
mLastAppendRange =
Interval<int64_t>(offset, offset + int64_t(mInputBuffer->Length()));
mLastAppendRange = mLastAppendRange.IsEmpty()
? Interval<int64_t>(offset, offset + int64_t(mInputBuffer->Length()))
: mLastAppendRange.Span(
Interval<int64_t>(offset, offset + int64_t(mInputBuffer->Length())));
if (decoders.Length()) {
// We're going to have to wait for the decoder to initialize, the promise

View File

@ -182,7 +182,6 @@ EXPORTS.mozilla.dom += [
'MediaDevices.h',
'MediaStreamError.h',
'MediaStreamTrack.h',
'RTCIdentityProviderRegistrar.h',
'TextTrack.h',
'TextTrackCue.h',
'TextTrackCueList.h',
@ -235,7 +234,6 @@ UNIFIED_SOURCES += [
'MP3Decoder.cpp',
'MP3Demuxer.cpp',
'MP3FrameParser.cpp',
'RTCIdentityProviderRegistrar.cpp',
'RtspMediaResource.cpp',
'SharedThreadPool.cpp',
'StreamBuffer.cpp',

View File

@ -89,9 +89,7 @@ PlatformDecoderModule::Init()
#ifdef MOZ_EME
/* static */
already_AddRefed<PlatformDecoderModule>
PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
bool aHasAudio,
bool aHasVideo)
PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy)
{
bool cdmDecodesAudio;
bool cdmDecodesVideo;
@ -101,14 +99,11 @@ PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
cdmDecodesVideo = caps.CanDecryptAndDecodeVideo();
}
nsRefPtr<PlatformDecoderModule> pdm;
if ((!cdmDecodesAudio && aHasAudio) || (!cdmDecodesVideo && aHasVideo)) {
// The CDM itself can't decode. We need to wrap a PDM to decode the
// decrypted output of the CDM.
pdm = Create();
if (!pdm) {
return nullptr;
}
// We always create a default PDM in order to decode
// non-encrypted tracks.
nsRefPtr<PlatformDecoderModule> pdm = Create();
if (!pdm) {
return nullptr;
}
nsRefPtr<PlatformDecoderModule> emepdm(

View File

@ -75,9 +75,7 @@ public:
// that we use on on aTaskQueue to decode the decrypted stream.
// This is called on the decode task queue.
static already_AddRefed<PlatformDecoderModule>
CreateCDMWrapper(CDMProxy* aProxy,
bool aHasAudio,
bool aHasVideo);
CreateCDMWrapper(CDMProxy* aProxy);
#endif
// Creates a decoder.

View File

@ -242,6 +242,7 @@ EMEDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
return wrapper.forget();
}
MOZ_ASSERT(mPDM);
nsRefPtr<MediaDataDecoder> decoder(
mPDM->CreateDecoder(aConfig,
aVideoTaskQueue,
@ -277,6 +278,7 @@ EMEDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
return wrapper.forget();
}
MOZ_ASSERT(mPDM);
nsRefPtr<MediaDataDecoder> decoder(
mPDM->CreateDecoder(aConfig, aAudioTaskQueue, aCallback));
if (!decoder) {

View File

@ -67,9 +67,15 @@ var startTest = function(media, token) {
v2.onplay = checkDrawImageEventHandler;
v3.onplay = checkDrawImageEventHandler;
v1.onplaying = checkDrawImageEventHandler;
v2.onplaying = checkDrawImageEventHandler;
v3.onplaying = checkDrawImageEventHandler;
function onplaying(ev) {
if (!ev.target.gotPlaying) {
ev.target.gotPlaying = true;
checkDrawImageEventHandler(ev);
}
}
v1.onplaying = onplaying;
v2.onplaying = onplaying;
v3.onplaying = onplaying;
var onloadeddata = function(ev) {
ev.target.gotLoadeddata = true;

View File

@ -22,12 +22,15 @@ EXPORTS += [
if CONFIG['MOZ_WEBRTC']:
EXPORTS += ['AudioOutputObserver.h',
'MediaEngineWebRTC.h']
EXPORTS.mozilla.dom += [ 'RTCIdentityProviderRegistrar.h' ]
UNIFIED_SOURCES += [
'MediaEngineCameraVideoSource.cpp',
'MediaEngineTabVideoSource.cpp',
'MediaEngineWebRTCAudio.cpp',
'MediaEngineWebRTCVideo.cpp',
'MediaTrackConstraints.cpp',
'RTCCertificate.cpp',
'RTCIdentityProviderRegistrar.cpp',
]
# MediaEngineWebRTC.cpp needs to be built separately.
SOURCES += [
@ -59,7 +62,6 @@ XPIDL_SOURCES += [
UNIFIED_SOURCES += [
'MediaEngineDefault.cpp',
'PeerIdentity.cpp',
'RTCCertificate.cpp',
]
EXPORTS.mozilla += [

View File

@ -900,7 +900,9 @@ MessagePort::RemoveDocFromBFCache()
}
nsPIDOMWindow* window = GetOwner();
MOZ_ASSERT(window);
if (!window) {
return;
}
nsIDocument* doc = window->GetExtantDoc();
if (!doc) {

View File

@ -1072,8 +1072,9 @@ CancellableTransaction.prototype = {
break;
}
case kSmsDeletedObserverTopic: {
if (subject && subject.deletedMessageIds &&
subject.deletedMessageIds.indexOf(this.cancellableId) >= 0) {
let deletedInfo = subject.QueryInterface(Ci.nsIDeletedMessageInfo);
if (deletedInfo && deletedInfo.deletedMessageIds &&
deletedInfo.deletedMessageIds.indexOf(this.cancellableId) >= 0) {
this.cancelRunning(_MMS_ERROR_MESSAGE_DELETED);
}
break;

View File

@ -1307,19 +1307,19 @@ SmsSendingScheduler.prototype = {
observe: function(aSubject, aTopic, aData) {
switch (aTopic) {
case kSmsDeletedObserverTopic:
let deletedInfo = aSubject.QueryInterface(Ci.nsIDeletedMessageInfo);
if (DEBUG) {
debug("Observe " + kSmsDeletedObserverTopic + ": " +
JSON.stringify(aSubject));
JSON.stringify(deletedInfo));
}
if (aSubject && aSubject.deletedMessageIds) {
for (let id of aSubject.deletedMessageIds) {
for (let i = 0; i < this._queue.length; i++) {
if (this._queue[i].messageId === id) {
if (DEBUG) debug("Deleting message with id=" + id);
this._queue.splice(i, 1)[0].onCancel(
Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
}
if (deletedInfo && deletedInfo.deletedMessageIds) {
for (let i = 0; i < this._queue.length; i++) {
let id = this._queue[i].messageId;
if (deletedInfo.deletedMessageIds.includes(id)) {
if (DEBUG) debug("Deleting message with id=" + id);
this._queue.splice(i, 1)[0].onCancel(
Ci.nsIMobileMessageCallback.NOT_FOUND_ERROR);
}
}
}

View File

@ -49,7 +49,22 @@ DeserializeArrayBuffer(JS::Handle<JSObject*> aObj,
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION(TCPSocketChildBase, mSocket)
NS_IMPL_CYCLE_COLLECTION_CLASS(TCPSocketChildBase)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TCPSocketChildBase)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSocket)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TCPSocketChildBase)
tmp->mWindowObj = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSocket)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TCPSocketChildBase)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mWindowObj)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketChildBase)
NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketChildBase)
@ -61,10 +76,12 @@ NS_INTERFACE_MAP_END
TCPSocketChildBase::TCPSocketChildBase()
: mIPCOpen(false)
{
mozilla::HoldJSObjects(this);
}
TCPSocketChildBase::~TCPSocketChildBase()
{
mozilla::DropJSObjects(this);
}
NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketChild::Release(void)
@ -78,8 +95,7 @@ NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketChild::Release(void)
}
TCPSocketChild::TCPSocketChild()
: mWindowObj(nullptr)
, mHost()
: mHost()
, mPort(0)
{
}

View File

@ -23,7 +23,7 @@ namespace dom {
class TCPSocketChildBase : public nsITCPSocketChild {
public:
NS_DECL_CYCLE_COLLECTION_CLASS(TCPSocketChildBase)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TCPSocketChildBase)
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
void AddIPDLReference();
@ -34,6 +34,7 @@ protected:
virtual ~TCPSocketChildBase();
nsCOMPtr<nsITCPSocketInternal> mSocket;
JS::Heap<JSObject*> mWindowObj;
bool mIPCOpen;
};
@ -56,7 +57,6 @@ public:
virtual bool RecvUpdateBufferedAmount(const uint32_t& aBufferred,
const uint32_t& aTrackingNumber) override;
private:
JSObject* mWindowObj;
nsString mHost;
uint16_t mPort;
};

View File

@ -1479,6 +1479,7 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
return false;
}
nsAutoMicroTask mt;
dom::AutoEntryScript aes(win, "NPAPI NPN_evaluate");
aes.TakeOwnershipOfErrorReporting();
JSContext* cx = aes.cx();

View File

@ -464,8 +464,7 @@ function startTest()
}
}
is(t19.selectionStart, 0, "input focused from mouse selectionStart");
is(t19.selectionEnd, 0, "input focused from mouse selectionEnd");
ok(t19.selectionStart == t19.selectionEnd, "input focused from mouse selection");
// mouse clicking on elements that are not tabbable
for (idx = 1; idx <= kFocusSteps; idx++) {

View File

@ -71,13 +71,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
var gTestWindow = null;
var gTestIndex = 0;
// TODO: if ever we remove these checks for XP and Lion, we should do the same
// in dom/html/test/test_fullscreen-api.html, which uses the same pattern.
const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
const isOSXLion = navigator.userAgent.indexOf("Mac OS X 10.7") != -1;
const isOSXMtnLion = navigator.userAgent.indexOf("Mac OS X 10.8") != -1;
const isOSXYosemite = navigator.userAgent.indexOf("Mac OS X 10.10") != -1;
function finish() {
SpecialPowers.clearUserPref("full-screen-api.enabled");
SpecialPowers.clearUserPref("full-screen-api.allow-trusted-requests-only");
@ -86,16 +79,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633602
}
function nextTest() {
if (isWinXP) {
todo(false, "Can't reliably run full-screen tests on Windows XP due to bug 704010");
finish();
return;
}
if (isOSXLion || isOSXMtnLion || isOSXYosemite) {
todo(false, "Can't reliably run full-screen tests on OS X Lion or Mountain Lion or Yosemite, see bug 744125");
finish();
return;
}
if (gTestWindow) {
gTestWindow.close();
}

View File

@ -17,7 +17,7 @@ interface Animation {
// Bug 1049975: Make 'effect' writeable
[Pure]
readonly attribute AnimationEffectReadOnly? effect;
readonly attribute AnimationTimeline timeline;
readonly attribute AnimationTimeline? timeline;
[BinaryName="startTimeAsDouble"]
attribute double? startTime;
[SetterThrows, BinaryName="currentTimeAsDouble"]

View File

@ -14,7 +14,5 @@
interface AnimationTimeline {
[BinaryName="currentTimeAsDouble"]
readonly attribute double? currentTime;
// Not yet implemented:
// Animation play (optional TimedItem? source = null);
// sequence<Animation> getAnimations ();
sequence<Animation> getAnimations ();
};

View File

@ -29,7 +29,7 @@ interface HTMLImageElement : HTMLElement {
attribute DOMString? crossOrigin;
[SetterThrows]
attribute DOMString useMap;
[SetterThrows]
[SetterThrows, Pref="network.http.enablePerElementReferrer"]
attribute DOMString referrer;
[SetterThrows]
attribute boolean isMap;

View File

@ -374,16 +374,6 @@ WEBIDL_FILES = [
'ResourceStatsManager.webidl',
'Response.webidl',
'RGBColor.webidl',
'RTCCertificate.webidl',
'RTCConfiguration.webidl',
'RTCIceCandidate.webidl',
'RTCIdentityAssertion.webidl',
'RTCIdentityProvider.webidl',
'RTCPeerConnection.webidl',
'RTCPeerConnectionStatic.webidl',
'RTCRtpReceiver.webidl',
'RTCRtpSender.webidl',
'RTCSessionDescription.webidl',
'RTCStatsReport.webidl',
'Screen.webidl',
'ScriptProcessorNode.webidl',
@ -602,6 +592,16 @@ if CONFIG['MOZ_WEBRTC']:
'PeerConnectionImplEnums.webidl',
'PeerConnectionObserver.webidl',
'PeerConnectionObserverEnums.webidl',
'RTCCertificate.webidl',
'RTCConfiguration.webidl',
'RTCIceCandidate.webidl',
'RTCIdentityAssertion.webidl',
'RTCIdentityProvider.webidl',
'RTCPeerConnection.webidl',
'RTCPeerConnectionStatic.webidl',
'RTCRtpReceiver.webidl',
'RTCRtpSender.webidl',
'RTCSessionDescription.webidl',
'WebrtcGlobalInformation.webidl',
]
@ -754,7 +754,6 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'IccChangeEvent.webidl',
'ImageCaptureErrorEvent.webidl',
'MediaStreamEvent.webidl',
'MediaStreamTrackEvent.webidl',
'MozApplicationEvent.webidl',
'MozCellBroadcastEvent.webidl',
'MozClirModeEvent.webidl',
@ -775,8 +774,6 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'PopupBlockedEvent.webidl',
'ProgressEvent.webidl',
'RecordErrorEvent.webidl',
'RTCDataChannelEvent.webidl',
'RTCPeerConnectionIceEvent.webidl',
'ScrollViewChangeEvent.webidl',
'SelectionStateChangedEvent.webidl',
'StyleRuleChangeEvent.webidl',
@ -792,6 +789,13 @@ GENERATED_EVENTS_WEBIDL_FILES = [
'USSDReceivedEvent.webidl',
]
if CONFIG['MOZ_WEBRTC']:
GENERATED_EVENTS_WEBIDL_FILES += [
'MediaStreamTrackEvent.webidl',
'RTCDataChannelEvent.webidl',
'RTCPeerConnectionIceEvent.webidl',
]
if CONFIG['MOZ_WEBSPEECH']:
GENERATED_EVENTS_WEBIDL_FILES += [
'SpeechRecognitionEvent.webidl',

View File

@ -184,7 +184,8 @@ AppendEllipseToPath(PathBuilder* aPathBuilder,
bool
SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
const DrawTarget& aDrawTarget)
const DrawTarget& aDrawTarget,
Float aLineWidth)
{
Matrix mat = aDrawTarget.GetTransform();
if (mat.HasNonTranslation()) {
@ -199,14 +200,21 @@ SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
p2.Round();
p1 -= mat.GetTranslation(); // back into user space
p2 -= mat.GetTranslation();
if (aP1.x == aP2.x) {
// snap vertical line, adding 0.5 to align it to be mid-pixel:
aP1 = p1 + Point(0.5, 0);
aP2 = p2 + Point(0.5, 0);
} else {
// snap horizontal line, adding 0.5 to align it to be mid-pixel:
aP1 = p1 + Point(0, 0.5);
aP2 = p2 + Point(0, 0.5);
aP1 = p1;
aP2 = p2;
bool lineWidthIsOdd = (int(aLineWidth) % 2) == 1;
if (lineWidthIsOdd) {
if (aP1.x == aP2.x) {
// snap vertical line, adding 0.5 to align it to be mid-pixel:
aP1 += Point(0.5, 0);
aP2 += Point(0.5, 0);
} else {
// snap horizontal line, adding 0.5 to align it to be mid-pixel:
aP1 += Point(0, 0.5);
aP2 += Point(0, 0.5);
}
}
return true;
}
@ -222,22 +230,26 @@ StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget,
Point p1 = aRect.TopLeft();
Point p2 = aRect.BottomLeft();
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
aStrokeOptions.mLineWidth);
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
p1 = aRect.BottomLeft();
p2 = aRect.BottomRight();
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
aStrokeOptions.mLineWidth);
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
p1 = aRect.TopLeft();
p2 = aRect.TopRight();
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
aStrokeOptions.mLineWidth);
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
p1 = aRect.TopRight();
p2 = aRect.BottomRight();
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
aStrokeOptions.mLineWidth);
aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions);
}

View File

@ -276,7 +276,8 @@ inline already_AddRefed<Path> MakePathForEllipse(const DrawTarget& aDrawTarget,
* false.
*/
GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
const DrawTarget& aDrawTarget);
const DrawTarget& aDrawTarget,
Float aLineWidth);
/**
* This function paints each edge of aRect separately, snapping the edges using

View File

@ -439,12 +439,6 @@ CreateBoxShadow(DrawTarget& aDT, SourceSurface* aBlurMask, const gfxRGBA& aShado
return nullptr;
}
if (boxShadowDT->GetType() != aDT.GetType()) {
printf_stderr("Box shadow type: %d, dest draw target type: %d\n",
(int) boxShadowDT->GetType(), (int) aDT.GetType());
MOZ_ASSERT(false, "Box shadows are incorrect type\n");
}
ColorPattern shadowColor(ToDeviceColor(aShadowColor));
boxShadowDT->MaskSurface(shadowColor, aBlurMask, Point(0, 0));
return boxShadowDT->Snapshot();

View File

@ -259,7 +259,7 @@ private:
DECL_GFX_PREF(Once, "image.cache.size", ImageCacheSize, int32_t, 5*1024*1024);
DECL_GFX_PREF(Once, "image.cache.timeweight", ImageCacheTimeWeight, int32_t, 500);
DECL_GFX_PREF(Live, "image.decode-only-on-draw.enabled", ImageDecodeOnlyOnDrawEnabled, bool, true);
DECL_GFX_PREF(Live, "image.decode-only-on-draw.enabled", ImageDecodeOnlyOnDrawEnabled, bool, false);
DECL_GFX_PREF(Live, "image.decode-immediately.enabled", ImageDecodeImmediatelyEnabled, bool, false);
DECL_GFX_PREF(Once, "image.decode.retry-on-alloc-failure", ImageDecodeRetryOnAllocFailure, bool, false);
DECL_GFX_PREF(Live, "image.downscale-during-decode.enabled", ImageDownscaleDuringDecodeEnabled, bool, true);

View File

@ -129,6 +129,7 @@ public:
///////////////////////////////////////////////////////////////////////////////
/* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
/* static */ uint32_t DecodePool::sNumCores = 0;
NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
@ -301,6 +302,7 @@ private:
DecodePool::Initialize()
{
MOZ_ASSERT(NS_IsMainThread());
sNumCores = PR_GetNumberOfProcessors();
DecodePool::Singleton();
}
@ -316,6 +318,12 @@ DecodePool::Singleton()
return sSingleton;
}
/* static */ uint32_t
DecodePool::NumberOfCores()
{
return sNumCores;
}
DecodePool::DecodePool()
: mImpl(new DecodePoolImpl)
, mMutex("image::DecodePool")
@ -324,7 +332,7 @@ DecodePool::DecodePool()
int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
uint32_t limit;
if (prefLimit <= 0) {
int32_t numCores = PR_GetNumberOfProcessors();
int32_t numCores = NumberOfCores();
if (numCores <= 1) {
limit = 1;
} else if (numCores == 2) {

View File

@ -49,6 +49,10 @@ public:
/// Returns the singleton instance.
static DecodePool* Singleton();
/// @return the number of processor cores we have available. This is not the
/// same as the number of decoding threads we're actually using.
static uint32_t NumberOfCores();
/// Ask the DecodePool to run @aDecoder asynchronously and return immediately.
void AsyncDecode(Decoder* aDecoder);
@ -86,6 +90,7 @@ private:
void NotifyProgress(Decoder* aDecoder);
static StaticRefPtr<DecodePool> sSingleton;
static uint32_t sNumCores;
nsRefPtr<DecodePoolImpl> mImpl;

View File

@ -51,9 +51,8 @@ ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
bool doDownscaleDuringDecode = gfxPrefs::ImageDownscaleDuringDecodeEnabled();
// We use the platform APZ value here since we don't have a widget to test.
// It's safe since this is an optimization, and the only platform
// ImageDecodeOnlyOnDraw is disabled on is B2G (where APZ is enabled in all
// widgets anyway).
// It's safe since this is an optimization, and
// ImageDecodeOnlyOnDraw is disabled everywhere and will be removed soon.
bool doDecodeOnlyOnDraw = gfxPrefs::ImageDecodeOnlyOnDrawEnabled() &&
gfxPlatform::AsyncPanZoomEnabled();

View File

@ -1196,7 +1196,13 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus,
// Let decoders know that there won't be any more data coming.
mSourceBuffer->Complete(aStatus);
if (mSyncLoad && !mHasSize) {
// Allow a synchronous size decode if mSyncLoad was set, or if we're running
// on a single thread (in which case waiting for the async size decoder could
// delay this image's load event quite a bit), or if this image is transient.
bool canSyncSizeDecode = mSyncLoad || mTransient ||
DecodePool::NumberOfCores() < 2;
if (canSyncSizeDecode && !mHasSize) {
// We're loading this image synchronously, so it needs to be usable after
// this call returns. Since we haven't gotten our size yet, we need to do a
// synchronous size decode here.
@ -1219,7 +1225,7 @@ RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus,
if (!mHasSize && !mError) {
// We don't have our size yet, so we'll fire the load event in SetSize().
MOZ_ASSERT(!mSyncLoad, "Firing load asynchronously but mSyncLoad is set?");
MOZ_ASSERT(!canSyncSizeDecode, "Firing load async but canSyncSizeDecode?");
NotifyProgress(FLAG_ONLOAD_BLOCKED);
mLoadProgress = Some(loadProgress);
return finalStatus;

View File

@ -1085,6 +1085,9 @@ class GCRuntime
/* The invocation kind of the current GC, taken from the first slice. */
JSGCInvocationKind invocationKind;
/* The initial GC reason, taken from the first slice. */
JS::gcreason::Reason initialReason;
/*
* If this is 0, all cross-compartment proxies must be registered in the
* wrapper map. This checking must be disabled temporarily while creating

View File

@ -30,9 +30,14 @@
#include "jit/arm64/vixl/Simulator-vixl.h"
#include "mozilla/FloatingPoint.h"
#include <math.h>
#include <string.h>
using mozilla::IsInfinite;
using mozilla::IsNaN;
namespace vixl {
const Instruction* Simulator::kEndOfSimAddress = NULL;
@ -364,7 +369,7 @@ void Simulator::FPCompare(double val0, double val1) {
// TODO: This assumes that the C++ implementation handles comparisons in the
// way that we expect (as per AssertSupportedFPCR()).
if ((isnan(val0) != 0) || (isnan(val1) != 0)) {
if (IsNaN(val0) || IsNaN(val1)) {
nzcv().SetRawValue(FPUnorderedFlag);
} else if (val0 < val1) {
nzcv().SetRawValue(FPLessThanFlag);
@ -1767,7 +1772,7 @@ int32_t Simulator::FPToInt32(double value, FPRounding rmode) {
} else if (value < kWMinInt) {
return kWMinInt;
}
return isnan(value) ? 0 : static_cast<int32_t>(value);
return IsNaN(value) ? 0 : static_cast<int32_t>(value);
}
@ -1778,7 +1783,7 @@ int64_t Simulator::FPToInt64(double value, FPRounding rmode) {
} else if (value < kXMinInt) {
return kXMinInt;
}
return isnan(value) ? 0 : static_cast<int64_t>(value);
return IsNaN(value) ? 0 : static_cast<int64_t>(value);
}
@ -1789,7 +1794,7 @@ uint32_t Simulator::FPToUInt32(double value, FPRounding rmode) {
} else if (value < 0.0) {
return 0;
}
return isnan(value) ? 0 : static_cast<uint32_t>(value);
return IsNaN(value) ? 0 : static_cast<uint32_t>(value);
}
@ -1800,7 +1805,7 @@ uint64_t Simulator::FPToUInt64(double value, FPRounding rmode) {
} else if (value < 0.0) {
return 0;
}
return isnan(value) ? 0 : static_cast<uint64_t>(value);
return IsNaN(value) ? 0 : static_cast<uint64_t>(value);
}
@ -1894,14 +1899,14 @@ void Simulator::VisitFPDataProcessing1Source(const Instruction* instr) {
float input = sreg(fn);
float rounded = FPRoundInt(input, fpcr_rounding);
set_sreg(fd, rounded);
if (!isnan(input) && (input != rounded)) FPProcessException();
if (!IsNaN(input) && (input != rounded)) FPProcessException();
break;
}
case FRINTX_d: {
double input = dreg(fn);
double rounded = FPRoundInt(input, fpcr_rounding);
set_dreg(fd, rounded);
if (!isnan(input) && (input != rounded)) FPProcessException();
if (!IsNaN(input) && (input != rounded)) FPProcessException();
break;
}
case FRINTZ_s: set_sreg(fd, FPRoundInt(sreg(fn), FPZero)); break;
@ -2160,7 +2165,7 @@ double Simulator::FPRoundInt(double value, FPRounding round_mode) {
if ((value == 0.0) || (value == kFP64PositiveInfinity) ||
(value == kFP64NegativeInfinity)) {
return value;
} else if (isnan(value)) {
} else if (IsNaN(value)) {
return FPProcessNaN(value);
}
@ -2392,9 +2397,9 @@ void Simulator::VisitFPDataProcessing3Source(const Instruction* instr) {
template <typename T>
T Simulator::FPAdd(T op1, T op2) {
// NaNs should be handled elsewhere.
VIXL_ASSERT(!isnan(op1) && !isnan(op2));
VIXL_ASSERT(!IsNaN(op1) && !IsNaN(op2));
if (isinf(op1) && isinf(op2) && (op1 != op2)) {
if (IsInfinite(op1) && IsInfinite(op2) && (op1 != op2)) {
// inf + -inf returns the default NaN.
FPProcessException();
return FPDefaultNaN<T>();
@ -2408,9 +2413,9 @@ T Simulator::FPAdd(T op1, T op2) {
template <typename T>
T Simulator::FPDiv(T op1, T op2) {
// NaNs should be handled elsewhere.
VIXL_ASSERT(!isnan(op1) && !isnan(op2));
VIXL_ASSERT(!IsNaN(op1) && !IsNaN(op2));
if ((isinf(op1) && isinf(op2)) || ((op1 == 0.0) && (op2 == 0.0))) {
if ((IsInfinite(op1) && IsInfinite(op2)) || ((op1 == 0.0) && (op2 == 0.0))) {
// inf / inf and 0.0 / 0.0 return the default NaN.
FPProcessException();
return FPDefaultNaN<T>();
@ -2426,7 +2431,7 @@ T Simulator::FPDiv(T op1, T op2) {
template <typename T>
T Simulator::FPMax(T a, T b) {
// NaNs should be handled elsewhere.
VIXL_ASSERT(!isnan(a) && !isnan(b));
VIXL_ASSERT(!IsNaN(a) && !IsNaN(b));
if ((a == 0.0) && (b == 0.0) &&
(copysign(1.0, a) != copysign(1.0, b))) {
@ -2447,14 +2452,14 @@ T Simulator::FPMaxNM(T a, T b) {
}
T result = FPProcessNaNs(a, b);
return isnan(result) ? result : FPMax(a, b);
return IsNaN(result) ? result : FPMax(a, b);
}
template <typename T>
T Simulator::FPMin(T a, T b) {
// NaNs should be handled elsewhere.
VIXL_ASSERT(!isnan(a) && !isnan(b));
VIXL_ASSERT(!IsNaN(a) && !IsNaN(b));
if ((a == 0.0) && (b == 0.0) &&
(copysign(1.0, a) != copysign(1.0, b))) {
@ -2475,16 +2480,16 @@ T Simulator::FPMinNM(T a, T b) {
}
T result = FPProcessNaNs(a, b);
return isnan(result) ? result : FPMin(a, b);
return IsNaN(result) ? result : FPMin(a, b);
}
template <typename T>
T Simulator::FPMul(T op1, T op2) {
// NaNs should be handled elsewhere.
VIXL_ASSERT(!isnan(op1) && !isnan(op2));
VIXL_ASSERT(!IsNaN(op1) && !IsNaN(op2));
if ((isinf(op1) && (op2 == 0.0)) || (isinf(op2) && (op1 == 0.0))) {
if ((IsInfinite(op1) && (op2 == 0.0)) || (IsInfinite(op2) && (op1 == 0.0))) {
// inf * 0.0 returns the default NaN.
FPProcessException();
return FPDefaultNaN<T>();
@ -2501,13 +2506,13 @@ T Simulator::FPMulAdd(T a, T op1, T op2) {
T sign_a = copysign(1.0, a);
T sign_prod = copysign(1.0, op1) * copysign(1.0, op2);
bool isinf_prod = isinf(op1) || isinf(op2);
bool isinf_prod = IsInfinite(op1) || IsInfinite(op2);
bool operation_generates_nan =
(isinf(op1) && (op2 == 0.0)) || // inf * 0.0
(isinf(op2) && (op1 == 0.0)) || // 0.0 * inf
(isinf(a) && isinf_prod && (sign_a != sign_prod)); // inf - inf
(IsInfinite(op1) && (op2 == 0.0)) || // inf * 0.0
(IsInfinite(op2) && (op1 == 0.0)) || // 0.0 * inf
(IsInfinite(a) && isinf_prod && (sign_a != sign_prod)); // inf - inf
if (isnan(result)) {
if (IsNaN(result)) {
// Generated NaNs override quiet NaNs propagated from a.
if (operation_generates_nan && IsQuietNaN(a)) {
FPProcessException();
@ -2530,7 +2535,7 @@ T Simulator::FPMulAdd(T a, T op1, T op2) {
}
result = FusedMultiplyAdd(op1, op2, a);
VIXL_ASSERT(!isnan(result));
VIXL_ASSERT(!IsNaN(result));
// Work around broken fma implementations for rounded zero results: If a is
// 0.0, the sign of the result is the sign of op1 * op2 before rounding.
@ -2545,9 +2550,9 @@ T Simulator::FPMulAdd(T a, T op1, T op2) {
template <typename T>
T Simulator::FPSub(T op1, T op2) {
// NaNs should be handled elsewhere.
VIXL_ASSERT(!isnan(op1) && !isnan(op2));
VIXL_ASSERT(!IsNaN(op1) && !IsNaN(op2));
if (isinf(op1) && isinf(op2) && (op1 == op2)) {
if (IsInfinite(op1) && IsInfinite(op2) && (op1 == op2)) {
// inf - inf returns the default NaN.
FPProcessException();
return FPDefaultNaN<T>();
@ -2560,7 +2565,7 @@ T Simulator::FPSub(T op1, T op2) {
template <typename T>
T Simulator::FPSqrt(T op) {
if (isnan(op)) {
if (IsNaN(op)) {
return FPProcessNaN(op);
} else if (op < 0.0) {
FPProcessException();
@ -2573,7 +2578,7 @@ T Simulator::FPSqrt(T op) {
template <typename T>
T Simulator::FPProcessNaN(T op) {
VIXL_ASSERT(isnan(op));
VIXL_ASSERT(IsNaN(op));
if (IsSignallingNaN(op)) {
FPProcessException();
}
@ -2587,10 +2592,10 @@ T Simulator::FPProcessNaNs(T op1, T op2) {
return FPProcessNaN(op1);
} else if (IsSignallingNaN(op2)) {
return FPProcessNaN(op2);
} else if (isnan(op1)) {
} else if (IsNaN(op1)) {
VIXL_ASSERT(IsQuietNaN(op1));
return FPProcessNaN(op1);
} else if (isnan(op2)) {
} else if (IsNaN(op2)) {
VIXL_ASSERT(IsQuietNaN(op2));
return FPProcessNaN(op2);
} else {
@ -2607,13 +2612,13 @@ T Simulator::FPProcessNaNs3(T op1, T op2, T op3) {
return FPProcessNaN(op2);
} else if (IsSignallingNaN(op3)) {
return FPProcessNaN(op3);
} else if (isnan(op1)) {
} else if (IsNaN(op1)) {
VIXL_ASSERT(IsQuietNaN(op1));
return FPProcessNaN(op1);
} else if (isnan(op2)) {
} else if (IsNaN(op2)) {
VIXL_ASSERT(IsQuietNaN(op2));
return FPProcessNaN(op2);
} else if (isnan(op3)) {
} else if (IsNaN(op3)) {
VIXL_ASSERT(IsQuietNaN(op3));
return FPProcessNaN(op3);
} else {
@ -2630,13 +2635,13 @@ bool Simulator::FPProcessNaNs(const Instruction* instr) {
if (instr->Mask(FP64) == FP64) {
double result = FPProcessNaNs(dreg(fn), dreg(fm));
if (isnan(result)) {
if (IsNaN(result)) {
set_dreg(fd, result);
done = true;
}
} else {
float result = FPProcessNaNs(sreg(fn), sreg(fm));
if (isnan(result)) {
if (IsNaN(result)) {
set_sreg(fd, result);
done = true;
}

View File

@ -121,14 +121,14 @@ class Registers
};
static const char* GetName(Code code) {
MOZ_ASSERT(code < Total);
static const char * const Names[] = { "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
"t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
"t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra"};
return Names[code];
}
static const char* GetName(uint32_t i) {
MOZ_ASSERT(i < Total);
static const char* GetName(Encoding i) {
return GetName(Code(i));
}

View File

@ -696,7 +696,7 @@ JitRuntime::generateVMWrapper(JSContext* cx, const VMFunction& f)
MacroAssembler masm(cx);
AllocatableGeneralRegisterSet regs(GeneralRegisterSet(Register::Codes::WrapperMask));
AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
"Wrapper register set should be a superset of Volatile register set.");

View File

@ -171,6 +171,11 @@ LIRGeneratorX86Shared::lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs)
void
LIRGeneratorX86Shared::lowerDivI(MDiv* div)
{
if (div->isUnsigned()) {
lowerUDiv(div);
return;
}
// Division instructions are slow. Division by constant denominators can be
// rewritten to use other instructions.
if (div->rhs()->isConstant()) {
@ -205,11 +210,6 @@ LIRGeneratorX86Shared::lowerDivI(MDiv* div)
}
}
if (div->isUnsigned()) {
lowerUDiv(div);
return;
}
LDivI* lir = new(alloc()) LDivI(useRegister(div->lhs()), useRegister(div->rhs()),
tempFixed(edx));
if (div->fallible())
@ -220,6 +220,11 @@ LIRGeneratorX86Shared::lowerDivI(MDiv* div)
void
LIRGeneratorX86Shared::lowerModI(MMod* mod)
{
if (mod->isUnsigned()) {
lowerUMod(mod);
return;
}
if (mod->rhs()->isConstant()) {
int32_t rhs = mod->rhs()->toConstant()->value().toInt32();
int32_t shift = FloorLog2(Abs(rhs));
@ -240,11 +245,6 @@ LIRGeneratorX86Shared::lowerModI(MMod* mod)
}
}
if (mod->isUnsigned()) {
lowerUMod(mod);
return;
}
LModI* lir = new(alloc()) LModI(useRegister(mod->lhs()),
useRegister(mod->rhs()),
tempFixed(eax));

View File

@ -2108,12 +2108,18 @@ ArenaList::relocateArenas(ArenaHeader* toRelocate, ArenaHeader* relocated, Slice
// heap memory.
static const double MIN_ZONE_RECLAIM_PERCENT = 2.0;
static bool isOOMReason(JS::gcreason::Reason reason)
{
return reason == JS::gcreason::LAST_DITCH ||
reason == JS::gcreason::MEM_PRESSURE;
}
static bool ShouldRelocateZone(size_t arenaCount, size_t relocCount, JS::gcreason::Reason reason)
{
if (relocCount == 0)
return false;
if (reason == JS::gcreason::MEM_PRESSURE || reason == JS::gcreason::LAST_DITCH)
if (isOOMReason(reason))
return true;
return (relocCount * 100.0) / arenaCount >= MIN_ZONE_RECLAIM_PERCENT;
@ -5501,10 +5507,7 @@ GCRuntime::finishCollection(JS::gcreason::Reason reason)
// before returning to ensure that we free as much as possible. If this is
// a zeal-triggered GC, we want to ensure that the mutator can continue
// allocating on the same pages to reduce fragmentation.
if (reason == JS::gcreason::LAST_DITCH ||
reason == JS::gcreason::MEM_PRESSURE ||
reason == JS::gcreason::DEBUG_GC)
{
if (isOOMReason(reason) || reason == JS::gcreason::DEBUG_GC) {
gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
rt->gc.waitBackgroundSweepOrAllocEnd();
}
@ -5789,6 +5792,7 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
switch (incrementalState) {
case NO_INCREMENTAL:
initialReason = reason;
cleanUpEverything = ShouldCleanUpEverything(reason, invocationKind);
isCompacting = shouldCompact();
lastMarkSlice = false;
@ -6237,6 +6241,19 @@ void
GCRuntime::finishGC(JS::gcreason::Reason reason)
{
MOZ_ASSERT(isIncrementalGCInProgress());
// If we're not collecting because we're out of memory then skip the
// compacting phase if we need to finish an ongoing incremental GC
// non-incrementally to avoid janking the browser.
if (!isOOMReason(initialReason)) {
if (incrementalState == COMPACT) {
abortGC();
return;
}
isCompacting = false;
}
collect(true, SliceBudget(), reason);
}

View File

@ -37,7 +37,9 @@
#include "mozilla/dom/PromiseBinding.h"
#include "mozilla/dom/RequestBinding.h"
#include "mozilla/dom/ResponseBinding.h"
#ifdef MOZ_WEBRTC
#include "mozilla/dom/RTCIdentityProviderRegistrar.h"
#endif
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/TextDecoderBinding.h"
#include "mozilla/dom/TextEncoderBinding.h"
@ -226,6 +228,7 @@ SandboxCreateCrypto(JSContext* cx, JS::HandleObject obj)
return JS_DefineProperty(cx, obj, "crypto", wrapped, JSPROP_ENUMERATE);
}
#ifdef MOZ_WEBRTC
static bool
SandboxCreateRTCIdentityProvider(JSContext* cx, JS::HandleObject obj)
{
@ -239,6 +242,7 @@ SandboxCreateRTCIdentityProvider(JSContext* cx, JS::HandleObject obj)
JS::RootedObject wrapped(cx, registrar->WrapObject(cx, nullptr));
return JS_DefineProperty(cx, obj, "rtcIdentityProvider", wrapped, JSPROP_ENUMERATE);
}
#endif
static bool
SetFetchRequestFromValue(JSContext *cx, RequestOrUSVString& request,
@ -898,8 +902,10 @@ xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj)
File = true;
} else if (!strcmp(name.ptr(), "crypto")) {
crypto = true;
#ifdef MOZ_WEBRTC
} else if (!strcmp(name.ptr(), "rtcIdentityProvider")) {
rtcIdentityProvider = true;
#endif
} else if (!strcmp(name.ptr(), "fetch")) {
fetch = true;
} else {
@ -959,8 +965,10 @@ xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj)
if (crypto && !SandboxCreateCrypto(cx, obj))
return false;
#ifdef MOZ_WEBRTC
if (rtcIdentityProvider && !SandboxCreateRTCIdentityProvider(cx, obj))
return false;
#endif
if (fetch && !SandboxCreateFetch(cx, obj))
return false;

View File

@ -3686,6 +3686,45 @@ static void SetPoly(const Rect& aRect, Point* poly)
poly[3].y = aRect.y + aRect.height;
}
static void
DrawDashedSegment(nsRenderingContext& aContext,
nsRect aRect,
nscoord aDashLength,
nscolor aColor,
int32_t aAppUnitsPerDevPixel,
nscoord aTwipsPerPixel,
bool aHorizontal)
{
DrawTarget* drawTarget = aContext.GetDrawTarget();
ColorPattern color(ToDeviceColor(aColor));
DrawOptions drawOptions(1.f, CompositionOp::OP_OVER, AntialiasMode::NONE);
StrokeOptions strokeOptions;
Float dash[2];
dash[0] = Float(aDashLength) / aAppUnitsPerDevPixel;
dash[1] = dash[0];
strokeOptions.mDashPattern = dash;
strokeOptions.mDashLength = MOZ_ARRAY_LENGTH(dash);
if (aHorizontal) {
nsPoint left = (aRect.TopLeft() + aRect.BottomLeft()) / 2;
nsPoint right = (aRect.TopRight() + aRect.BottomRight()) / 2;
strokeOptions.mLineWidth = Float(aRect.height) / aAppUnitsPerDevPixel;
StrokeLineWithSnapping(left, right,
aAppUnitsPerDevPixel, *drawTarget,
color, strokeOptions, drawOptions);
} else {
nsPoint top = (aRect.TopLeft() + aRect.TopRight()) / 2;
nsPoint bottom = (aRect.BottomLeft() + aRect.BottomRight()) / 2;
strokeOptions.mLineWidth = Float(aRect.width) / aAppUnitsPerDevPixel;
StrokeLineWithSnapping(top, bottom,
aAppUnitsPerDevPixel, *drawTarget,
color, strokeOptions, drawOptions);
}
}
static void
DrawSolidBorderSegment(nsRenderingContext& aContext,
nsRect aRect,
@ -3832,24 +3871,40 @@ nsCSSRendering::DrawTableBorderSegment(nsRenderingContext& aContext,
nscoord startDashLength = minDashLength;
nscoord endDashLength = minDashLength;
if (horizontal) {
GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
GetDashInfo(aBorder.width, dashLength, twipsPerPixel, numDashSpaces,
startDashLength, endDashLength);
nsRect rect(aBorder.x, aBorder.y, startDashLength, aBorder.height);
DrawSolidBorderSegment(aContext, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel);
for (int32_t spaceX = 0; spaceX < numDashSpaces; spaceX++) {
rect.x += rect.width + dashLength;
rect.width = (spaceX == (numDashSpaces - 1)) ? endDashLength : dashLength;
DrawSolidBorderSegment(aContext, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel);
}
DrawSolidBorderSegment(aContext, rect, aBorderColor,
aAppUnitsPerDevPixel, twipsPerPixel);
rect.x += startDashLength + dashLength;
rect.width = aBorder.width
- (startDashLength + endDashLength + dashLength);
DrawDashedSegment(aContext, rect, dashLength, aBorderColor,
aAppUnitsPerDevPixel, twipsPerPixel, horizontal);
rect.x += rect.width;
rect.width = endDashLength;
DrawSolidBorderSegment(aContext, rect, aBorderColor,
aAppUnitsPerDevPixel, twipsPerPixel);
}
else {
GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces, startDashLength, endDashLength);
GetDashInfo(aBorder.height, dashLength, twipsPerPixel, numDashSpaces,
startDashLength, endDashLength);
nsRect rect(aBorder.x, aBorder.y, aBorder.width, startDashLength);
DrawSolidBorderSegment(aContext, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel);
for (int32_t spaceY = 0; spaceY < numDashSpaces; spaceY++) {
rect.y += rect.height + dashLength;
rect.height = (spaceY == (numDashSpaces - 1)) ? endDashLength : dashLength;
DrawSolidBorderSegment(aContext, rect, aBorderColor, aAppUnitsPerDevPixel, twipsPerPixel);
}
DrawSolidBorderSegment(aContext, rect, aBorderColor,
aAppUnitsPerDevPixel, twipsPerPixel);
rect.y += rect.height + dashLength;
rect.height = aBorder.height
- (startDashLength + endDashLength + dashLength);
DrawDashedSegment(aContext, rect, dashLength, aBorderColor,
aAppUnitsPerDevPixel, twipsPerPixel, horizontal);
rect.y += rect.height;
rect.height = endDashLength;
DrawSolidBorderSegment(aContext, rect, aBorderColor,
aAppUnitsPerDevPixel, twipsPerPixel);
}
}
break;

View File

@ -364,6 +364,11 @@ AddAnimationForProperty(nsIFrame* aFrame, const AnimationProperty& aProperty,
MOZ_ASSERT(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
MOZ_ASSERT(aAnimation->GetEffect(),
"Should not be adding an animation without an effect");
MOZ_ASSERT(!aAnimation->GetCurrentOrPendingStartTime().IsNull() ||
(aAnimation->GetTimeline() &&
aAnimation->GetTimeline()->TracksWallclockTime()),
"Animation should either have a resolved start time or "
"a timeline that tracks wallclock time");
nsStyleContext* styleContext = aFrame->StyleContext();
nsPresContext* presContext = aFrame->PresContext();
TransformReferenceBox refBox(aFrame);
@ -377,7 +382,7 @@ AddAnimationForProperty(nsIFrame* aFrame, const AnimationProperty& aProperty,
Nullable<TimeDuration> startTime = aAnimation->GetCurrentOrPendingStartTime();
animation->startTime() = startTime.IsNull()
? TimeStamp()
: aAnimation->Timeline()->ToTimeStamp(
: aAnimation->GetTimeline()->ToTimeStamp(
startTime.Value() + timing.mDelay);
animation->initialCurrentTime() = aAnimation->GetCurrentTime().Value()
- timing.mDelay;
@ -451,20 +456,20 @@ AddAnimationsForProperty(nsIFrame* aFrame, nsCSSProperty aProperty,
MOZ_ASSERT(property->mWinsInCascade,
"GetAnimationOfProperty already tested mWinsInCascade");
// Don't add animations that are pending when their corresponding
// refresh driver is under test control. This is because any pending
// animations on layers will have their start time updated with the
// current timestamp but when the refresh driver is under test control
// its refresh times are unrelated to timestamp values.
// Don't add animations that are pending if their timeline does not
// track wallclock time. This is because any pending animations on layers
// will have their start time updated with the current wallclock time.
// If we can't convert that wallclock time back to an equivalent timeline
// time, we won't be able to update the content animation and it will end
// up being out of sync with the layer animation.
//
// Instead we leave the animation running on the main thread and the
// next time the refresh driver is advanced it will trigger any pending
// animations.
if (anim->PlayState() == AnimationPlayState::Pending) {
nsRefreshDriver* driver = anim->Timeline()->GetRefreshDriver();
if (driver && driver->IsTestControllingRefreshesEnabled()) {
continue;
}
// Currently this only happens when the timeline is driven by a refresh
// driver under test control. In this case, the next time the refresh
// driver is advanced it will trigger any pending animations.
if (anim->PlayState() == AnimationPlayState::Pending &&
(!anim->GetTimeline() ||
!anim->GetTimeline()->TracksWallclockTime())) {
continue;
}
AddAnimationForProperty(aFrame, *property, anim, aLayer, aData, aPending);

View File

@ -8049,7 +8049,8 @@ void StrokeLineWithSnapping(const nsPoint& aP1, const nsPoint& aP2,
{
Point p1 = NSPointToPoint(aP1, aAppUnitsPerDevPixel);
Point p2 = NSPointToPoint(aP2, aAppUnitsPerDevPixel);
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget);
SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget,
aStrokeOptions.mLineWidth);
aDrawTarget.StrokeLine(p1, p2, aPattern, aStrokeOptions, aDrawOptions);
}

Some files were not shown because too many files have changed in this diff Show More