mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to fx-team. a=merge
This commit is contained in:
commit
85394e2690
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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"/>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 |
@ -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();
|
||||
|
@ -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).
|
||||
|
32
dom/animation/AnimationComparator.h
Normal file
32
dom/animation/AnimationComparator.h
Normal 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
|
@ -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, ¶ms);
|
||||
|
||||
// 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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ EXPORTS.mozilla.dom += [
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'AnimationComparator.h',
|
||||
'AnimationUtils.h',
|
||||
'PendingAnimationTracker.h',
|
||||
]
|
||||
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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]
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
14
dom/base/crashtests/1181619.html
Normal file
14
dom/base/crashtests/1181619.html
Normal 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>
|
@ -203,3 +203,4 @@ load xhr_html_nullresponse.html
|
||||
load structured_clone_container_throws.html
|
||||
load 1154598.xhtml
|
||||
load 1157995.html
|
||||
load 1181619.html
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
|
@ -184,7 +184,7 @@ GetCurrentJSStack()
|
||||
cx = workers::GetCurrentThreadJSContext();
|
||||
}
|
||||
|
||||
if (!cx) {
|
||||
if (!cx || !js::GetContextCompartment(cx)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
});
|
||||
},
|
||||
|
||||
|
5
dom/cache/DBSchema.cpp
vendored
5
dom/cache/DBSchema.cpp
vendored
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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))) {
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
|
@ -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(
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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 += [
|
||||
|
@ -900,7 +900,9 @@ MessagePort::RemoveDocFromBFCache()
|
||||
}
|
||||
|
||||
nsPIDOMWindow* window = GetOwner();
|
||||
MOZ_ASSERT(window);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (!doc) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
@ -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++) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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"]
|
||||
|
@ -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 ();
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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.");
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user