mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
4755deb0c5
@ -51,3 +51,5 @@ if CONFIG['MOZ_ENABLE_DBUS']:
|
||||
CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -29,3 +29,5 @@ if CONFIG['ACCESSIBILITY']:
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -22,3 +22,5 @@ LOCAL_INCLUDES += [
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -50,3 +50,5 @@ FINAL_LIBRARY = 'xul'
|
||||
# macros which conflicts with std::min/max. Suppress the macros:
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DEFINES['NOMINMAX'] = True
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -59,3 +59,5 @@ LOCAL_INCLUDES += [
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -24,3 +24,5 @@ LOCAL_INCLUDES += [
|
||||
DEFINES['NOMINMAX'] = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -23,3 +23,5 @@ FINAL_LIBRARY = 'xul'
|
||||
# macros which conflicts with std::min/max. Suppress the macros:
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DEFINES['NOMINMAX'] = True
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -47,3 +47,6 @@ else:
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -1073,3 +1073,8 @@ pref("dom.mozSettings.SettingsDB.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsManager.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsRequestManager.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsService.verbose.enabled", false);
|
||||
|
||||
// Controlling whether we want to allow forcing some Settings
|
||||
// IndexedDB transactions to be opened as readonly or keep everything as
|
||||
// readwrite.
|
||||
pref("dom.mozSettings.allowForceReadOnly", false);
|
||||
|
@ -74,3 +74,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
OS_LIBS += [
|
||||
'version',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -18,3 +18,5 @@ else:
|
||||
]
|
||||
DEFINES['B2G_NAME'] = '"%s-bin%s"' % (PROGRAM, CONFIG['BIN_SUFFIX'])
|
||||
DEFINES['GAIA_PATH'] = '"gaia/profile"'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -72,3 +72,5 @@ if CONFIG['HAVE_CLOCK_MONOTONIC']:
|
||||
OS_LIBS += CONFIG['REALTIME_LIBS']
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -17,3 +17,5 @@ FINAL_LIBRARY = 'browsercomps'
|
||||
LOCAL_INCLUDES += [
|
||||
'../build',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -34,3 +34,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
# GTK2: Need to link with glib for GNOME shell service
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'gtk2', 'gtk3'):
|
||||
OS_LIBS += CONFIG['TK_LIBS']
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -21,3 +21,5 @@ FINAL_LIBRARY = 'browsercomps'
|
||||
LOCAL_INCLUDES += [
|
||||
'../build'
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -39,3 +39,5 @@ for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'):
|
||||
LOCAL_INCLUDES += [
|
||||
'../build',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -52,3 +52,5 @@ EXTRA_PP_JS_MODULES += [
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'browsercomps'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -49,3 +49,5 @@ for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION'):
|
||||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -24,3 +24,5 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
|
||||
LOCAL_INCLUDES += ['/uriloader/exthandler/mac']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -11,14 +11,19 @@
|
||||
#include "nsIDocument.h" // For nsIDocument
|
||||
#include "nsIPresShell.h" // For nsIPresShell
|
||||
#include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
|
||||
#include "PendingPlayerTracker.h" // For PendingPlayerTracker
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline, mSource)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationPlayer, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationPlayer, Release)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline,
|
||||
mSource, mReady)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationPlayer)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationPlayer)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationPlayer)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
AnimationPlayer::WrapObject(JSContext* aCx)
|
||||
@ -47,6 +52,10 @@ AnimationPlayer::GetCurrentTime() const
|
||||
AnimationPlayState
|
||||
AnimationPlayer::PlayState() const
|
||||
{
|
||||
if (mIsPending) {
|
||||
return AnimationPlayState::Pending;
|
||||
}
|
||||
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
||||
if (currentTime.IsNull()) {
|
||||
return AnimationPlayState::Idle;
|
||||
@ -63,6 +72,26 @@ AnimationPlayer::PlayState() const
|
||||
return AnimationPlayState::Running;
|
||||
}
|
||||
|
||||
Promise*
|
||||
AnimationPlayer::GetReady(ErrorResult& aRv)
|
||||
{
|
||||
// Lazily create the ready promise if it doesn't exist
|
||||
if (!mReady) {
|
||||
nsIGlobalObject* global = mTimeline->GetParentObject();
|
||||
if (global) {
|
||||
mReady = Promise::Create(global, aRv);
|
||||
if (mReady && PlayState() != AnimationPlayState::Pending) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mReady) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
return mReady;
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Play()
|
||||
{
|
||||
@ -110,7 +139,7 @@ AnimationPlayer::Tick()
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::ResolveStartTime()
|
||||
AnimationPlayer::StartNow()
|
||||
{
|
||||
// Currently we only expect this method to be called when we are in the
|
||||
// middle of initiating/resuming playback so we should have an unresolved
|
||||
@ -125,6 +154,24 @@ AnimationPlayer::ResolveStartTime()
|
||||
MOZ_ASSERT(!readyTime.IsNull(), "Missing or inactive timeline");
|
||||
mStartTime.SetValue(readyTime.Value() - mHoldTime.Value());
|
||||
mHoldTime.SetNull();
|
||||
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Cancel()
|
||||
{
|
||||
if (mIsPending) {
|
||||
CancelPendingPlay();
|
||||
if (mReady) {
|
||||
mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
mHoldTime.SetNull();
|
||||
mStartTime.SetNull();
|
||||
}
|
||||
|
||||
bool
|
||||
@ -173,7 +220,8 @@ AnimationPlayer::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
|
||||
}
|
||||
|
||||
AnimationPlayState playState = PlayState();
|
||||
if (playState == AnimationPlayState::Running) {
|
||||
if (playState == AnimationPlayState::Running ||
|
||||
playState == AnimationPlayState::Pending) {
|
||||
aNeedsRefreshes = true;
|
||||
}
|
||||
|
||||
@ -197,21 +245,32 @@ AnimationPlayer::DoPlay()
|
||||
return;
|
||||
}
|
||||
|
||||
ResolveStartTime();
|
||||
// Clear ready promise. We'll create a new one lazily.
|
||||
mReady = nullptr;
|
||||
|
||||
StartNow();
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::DoPause()
|
||||
{
|
||||
if (IsPaused()) {
|
||||
return;
|
||||
if (mIsPending) {
|
||||
CancelPendingPlay();
|
||||
// Resolve the ready promise since we currently only use it for
|
||||
// players that are waiting to play. Later (in bug 1109390), we will
|
||||
// use this for players waiting to pause as well and then we won't
|
||||
// want to resolve it just yet.
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark this as no longer running on the compositor so that next time
|
||||
// we update animations we won't throttle them and will have a chance
|
||||
// to remove the animation from any layer it might be on.
|
||||
mIsRunningOnCompositor = false;
|
||||
|
||||
// Bug 927349 - check for null result here and go to pending state
|
||||
// Bug 1109390 - check for null result here and go to pending state
|
||||
mHoldTime = GetCurrentTime();
|
||||
mStartTime.SetNull();
|
||||
}
|
||||
@ -234,6 +293,24 @@ AnimationPlayer::PostUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::CancelPendingPlay()
|
||||
{
|
||||
if (!mIsPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* doc = GetRenderedDocument();
|
||||
if (doc) {
|
||||
PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
|
||||
if (tracker) {
|
||||
tracker->RemovePlayPending(*this);
|
||||
}
|
||||
}
|
||||
|
||||
mIsPending = false;
|
||||
}
|
||||
|
||||
StickyTimeDuration
|
||||
AnimationPlayer::SourceContentEnd() const
|
||||
{
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/dom/Animation.h" // for Animation
|
||||
#include "mozilla/dom/AnimationPlayerBinding.h" // for AnimationPlayState
|
||||
#include "mozilla/dom/AnimationTimeline.h" // for AnimationTimeline
|
||||
#include "mozilla/dom/Promise.h" // for Promise
|
||||
#include "nsCSSProperty.h" // for nsCSSProperty
|
||||
|
||||
// X11 has a #define for CurrentTime.
|
||||
@ -37,7 +38,8 @@ class CSSTransitionPlayer;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AnimationPlayer : public nsWrapperCache
|
||||
class AnimationPlayer : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
protected:
|
||||
virtual ~AnimationPlayer() { }
|
||||
@ -45,13 +47,14 @@ protected:
|
||||
public:
|
||||
explicit AnimationPlayer(AnimationTimeline* aTimeline)
|
||||
: mTimeline(aTimeline)
|
||||
, mIsPending(false)
|
||||
, mIsRunningOnCompositor(false)
|
||||
, mIsPreviousStateFinished(false)
|
||||
{
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationPlayer)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationPlayer)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationPlayer)
|
||||
|
||||
AnimationTimeline* GetParentObject() const { return mTimeline; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
@ -65,6 +68,7 @@ public:
|
||||
Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
|
||||
Nullable<TimeDuration> GetCurrentTime() const;
|
||||
AnimationPlayState PlayState() const;
|
||||
virtual Promise* GetReady(ErrorResult& aRv);
|
||||
virtual void Play();
|
||||
virtual void Pause();
|
||||
bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
|
||||
@ -84,10 +88,11 @@ public:
|
||||
|
||||
void SetSource(Animation* aSource);
|
||||
void Tick();
|
||||
// Sets the start time of the player to the current time of its timeline.
|
||||
// This should only be called on a player that is currently waiting to play
|
||||
// (and therefore has a null start time but a fixed hold time).
|
||||
void ResolveStartTime();
|
||||
|
||||
// Sets the start time of a player that is waiting to play to the current
|
||||
// time of its timeline.
|
||||
void StartNow();
|
||||
void Cancel();
|
||||
|
||||
const nsString& Name() const {
|
||||
return mSource ? mSource->Name() : EmptyString();
|
||||
@ -128,6 +133,10 @@ protected:
|
||||
|
||||
void FlushStyle() const;
|
||||
void PostUpdate();
|
||||
// Remove this player from the pending player tracker and resets mIsPending
|
||||
// as necessary. The caller is responsible for resolving or aborting the
|
||||
// mReady promise as necessary.
|
||||
void CancelPendingPlay();
|
||||
StickyTimeDuration SourceContentEnd() const;
|
||||
|
||||
nsIDocument* GetRenderedDocument() const;
|
||||
@ -140,6 +149,18 @@ protected:
|
||||
// The beginning of the delay period.
|
||||
Nullable<TimeDuration> mStartTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mHoldTime; // Player timescale
|
||||
|
||||
// A Promise that is replaced on each call to Play() (and in future Pause())
|
||||
// and fulfilled when Play() is successfully completed.
|
||||
// This object is lazily created by GetReady.
|
||||
nsRefPtr<Promise> mReady;
|
||||
|
||||
// Indicates if the player is in the pending state. We use this rather
|
||||
// than checking if this player is tracked by a PendingPlayerTracker.
|
||||
// This is because the PendingPlayerTracker is associated with the source
|
||||
// content's document but we need to know if we're pending even if the
|
||||
// source content loses association with its document.
|
||||
bool mIsPending;
|
||||
bool mIsRunningOnCompositor;
|
||||
// Indicates whether we were in the finished state during our
|
||||
// most recent unthrottled sample (our last ComposeStyle call).
|
||||
|
@ -55,17 +55,12 @@ AnimationTimeline::GetCurrentTimeStamp() const
|
||||
result = timing->GetNavigationStartTimeStamp();
|
||||
}
|
||||
|
||||
nsIPresShell* presShell = mDocument->GetShell();
|
||||
if (MOZ_UNLIKELY(!presShell)) {
|
||||
nsRefreshDriver* refreshDriver = GetRefreshDriver();
|
||||
if (!refreshDriver) {
|
||||
return result;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
if (MOZ_UNLIKELY(!presContext)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = presContext->RefreshDriver()->MostRecentRefresh();
|
||||
result = refreshDriver->MostRecentRefresh();
|
||||
// FIXME: We would like to assert that:
|
||||
// mLastCurrentTime.IsNull() || result >= mLastCurrentTime
|
||||
// but due to bug 1043078 this will not be the case when the refresh driver
|
||||
@ -104,5 +99,21 @@ AnimationTimeline::ToTimeStamp(const TimeDuration& aTimeDuration) const
|
||||
return result;
|
||||
}
|
||||
|
||||
nsRefreshDriver*
|
||||
AnimationTimeline::GetRefreshDriver() const
|
||||
{
|
||||
nsIPresShell* presShell = mDocument->GetShell();
|
||||
if (MOZ_UNLIKELY(!presShell)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
if (MOZ_UNLIKELY(!presContext)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return presContext->RefreshDriver();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "nsIDocument.h"
|
||||
|
||||
struct JSContext;
|
||||
class nsRefreshDriver;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -26,10 +27,17 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~AnimationTimeline() { }
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationTimeline)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationTimeline)
|
||||
|
||||
nsISupports* GetParentObject() const { return mDocument; }
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
{
|
||||
return mDocument->GetParentObject();
|
||||
}
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
// AnimationTimeline methods
|
||||
@ -44,8 +52,7 @@ public:
|
||||
|
||||
protected:
|
||||
TimeStamp GetCurrentTimeStamp() const;
|
||||
|
||||
virtual ~AnimationTimeline() { }
|
||||
nsRefreshDriver* GetRefreshDriver() const;
|
||||
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
|
||||
|
35
dom/animation/PendingPlayerTracker.cpp
Normal file
35
dom/animation/PendingPlayerTracker.cpp
Normal file
@ -0,0 +1,35 @@
|
||||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "PendingPlayerTracker.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(PendingPlayerTracker, mPlayPendingSet)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingPlayerTracker, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingPlayerTracker, Release)
|
||||
|
||||
void
|
||||
PendingPlayerTracker::AddPlayPending(dom::AnimationPlayer& aPlayer)
|
||||
{
|
||||
mPlayPendingSet.PutEntry(&aPlayer);
|
||||
}
|
||||
|
||||
void
|
||||
PendingPlayerTracker::RemovePlayPending(dom::AnimationPlayer& aPlayer)
|
||||
{
|
||||
mPlayPendingSet.RemoveEntry(&aPlayer);
|
||||
}
|
||||
|
||||
bool
|
||||
PendingPlayerTracker::IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const
|
||||
{
|
||||
return mPlayPendingSet.Contains(const_cast<dom::AnimationPlayer*>(&aPlayer));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
36
dom/animation/PendingPlayerTracker.h
Normal file
36
dom/animation/PendingPlayerTracker.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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_dom_PendingPlayerTracker_h
|
||||
#define mozilla_dom_PendingPlayerTracker_h
|
||||
|
||||
#include "mozilla/dom/AnimationPlayer.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PendingPlayerTracker MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingPlayerTracker)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingPlayerTracker)
|
||||
|
||||
void AddPlayPending(dom::AnimationPlayer& aPlayer);
|
||||
void RemovePlayPending(dom::AnimationPlayer& aPlayer);
|
||||
bool IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const;
|
||||
|
||||
private:
|
||||
~PendingPlayerTracker() { }
|
||||
|
||||
typedef nsTHashtable<nsRefPtrHashKey<dom::AnimationPlayer>>
|
||||
AnimationPlayerSet;
|
||||
|
||||
AnimationPlayerSet mPlayPendingSet;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PendingPlayerTracker_h
|
@ -14,11 +14,16 @@ EXPORTS.mozilla.dom += [
|
||||
'AnimationTimeline.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'PendingPlayerTracker.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Animation.cpp',
|
||||
'AnimationEffect.cpp',
|
||||
'AnimationPlayer.cpp',
|
||||
'AnimationTimeline.cpp',
|
||||
'PendingPlayerTracker.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -2,6 +2,7 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes xyz {
|
||||
@ -11,35 +12,26 @@
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'xyz 100s';
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
|
||||
'Animation effect name matches keyframes rule name');
|
||||
div.remove();
|
||||
}, 'Effect name makes keyframe rule');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\yz 100s';
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
|
||||
'Escaped animation effect name matches keyframes rule name');
|
||||
div.remove();
|
||||
}, 'Escaped animation name');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\79 z 100s';
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
|
||||
'Hex-escaped animation effect name matches keyframes rule'
|
||||
+ ' name');
|
||||
div.remove();
|
||||
}, 'Animation name with hex-escape');
|
||||
|
||||
</script>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim {
|
||||
@ -12,24 +13,12 @@
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
function waitForFrame() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.requestAnimationFrame(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
@ -39,27 +28,22 @@ async_test(function(t) {
|
||||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
player.pause();
|
||||
});
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
});
|
||||
div.remove();
|
||||
player.pause();
|
||||
return player.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() a running animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
@ -69,22 +53,18 @@ async_test(function(t) {
|
||||
|
||||
player.pause();
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Paused value of margin-left is zero');
|
||||
});
|
||||
div.remove();
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Paused value of margin-left is zero');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
@ -95,18 +75,15 @@ async_test(function(t) {
|
||||
|
||||
player.play();
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > 0,
|
||||
'Playing value of margin-left is greater than zero');
|
||||
});
|
||||
div.remove();
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > 0,
|
||||
'Playing value of margin-left is greater than zero');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'play() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
@ -115,35 +92,32 @@ async_test(function(t) {
|
||||
'Initial value of margin-left is zero');
|
||||
|
||||
player.play();
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
|
||||
var previousAnimVal;
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
div.style.animationPlayState = 'paused';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
});
|
||||
player.ready.then(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Animated value of margin-left does not change when'
|
||||
+ ' paused by style');
|
||||
});
|
||||
div.remove();
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
div.style.animationPlayState = 'paused';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Animated value of margin-left does not change when'
|
||||
+ ' paused by style');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'play() is overridden by later setting "animation-play-state: paused"');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
@ -158,20 +132,17 @@ async_test(function(t) {
|
||||
player.play();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'Playing value of margin-left is increasing');
|
||||
});
|
||||
div.remove();
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'Playing value of margin-left is increasing');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'play() flushes pending changes to animation-play-state first');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
@ -193,16 +164,13 @@ async_test(function(t) {
|
||||
player.pause();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Paused value of margin-left does not change');
|
||||
});
|
||||
div.remove();
|
||||
waitForFrame().then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Paused value of margin-left does not change');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() applies pending changes to animation-play-state first');
|
||||
// (Note that we can't actually test for this; see comment above, in test-body.)
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
@ -9,23 +10,23 @@
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player.playState, 'running');
|
||||
// Bug 927349: Check for pending state here
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
assert_equals(player.playState, 'running');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Player returns correct playState when running');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
@ -33,8 +34,8 @@ test(function() {
|
||||
assert_equals(player.playState, 'paused');
|
||||
}, 'Player returns correct playState when paused');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
@ -43,14 +44,15 @@ test(function() {
|
||||
assert_equals(player.playState, 'paused');
|
||||
}, 'Player.playState updates when paused by script');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
div.style.animationPlayState = 'running';
|
||||
// This test also checks that calling playState flushes style
|
||||
// Bug 927349: Make this check for 'pending'
|
||||
assert_equals(player.playState, 'running');
|
||||
}, 'Player.playState updates when resumed by setting style');
|
||||
|
||||
|
@ -0,0 +1,78 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes abc {
|
||||
to { transform: translate(10px) }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
var originalReadyPromise = player.ready;
|
||||
player.ready.then(function() {
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
player.pause();
|
||||
// TODO: When we implement deferred pausing (bug 1109390), change this to
|
||||
// assert_not_equals and wait on the new promise before continuing.
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise does not change when pausing (for now)');
|
||||
player.play();
|
||||
assert_not_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise object identity differs after calling'
|
||||
+ ' play()');
|
||||
t.done();
|
||||
});
|
||||
}, 'A new ready promise is created each time play() is called'
|
||||
+ ' the animation property');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s paused';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
var originalReadyPromise = player.ready;
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_not_equals(player.ready, originalReadyPromise,
|
||||
'After updating animation-play-state a new ready promise'
|
||||
+ ' object is created');
|
||||
}, 'A new ready promise is created when setting animation-play-state: running');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.ready.then(function() {
|
||||
var promiseBeforeCallingPlay = player.ready;
|
||||
player.play();
|
||||
assert_equals(player.ready, promiseBeforeCallingPlay,
|
||||
'Ready promise has same object identity after redundant call'
|
||||
+ ' to play()');
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant calls to play() do not generate new ready promise objects');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.ready.then(function(resolvedPlayer) {
|
||||
assert_equals(resolvedPlayer, player,
|
||||
'Object identity of player passed to Promise callback'
|
||||
+ ' matches the player object owning the Promise');
|
||||
t.done();
|
||||
});
|
||||
}, 'The ready promise is fulfilled with its AnimationPlayer');
|
||||
|
||||
</script>
|
@ -2,6 +2,7 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
@ -9,19 +10,12 @@
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players[0].source.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
div.remove();
|
||||
}, 'Returned CSS animations have the correct Animation.target');
|
||||
|
||||
</script>
|
||||
|
@ -2,6 +2,7 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
@ -12,23 +13,24 @@
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
|
||||
var originalPlayer = div.getAnimationPlayers()[0];
|
||||
var originalStartTime = originalPlayer.startTime;
|
||||
var originalCurrentTime = originalPlayer.currentTime;
|
||||
var originalStartTime;
|
||||
var originalCurrentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and doesn't
|
||||
// simply reflect the current time).
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
originalPlayer.ready.then(function() {
|
||||
originalStartTime = originalPlayer.startTime;
|
||||
originalCurrentTime = originalPlayer.currentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and
|
||||
// doesn't simply reflect the current time).
|
||||
return waitForFrame();
|
||||
}).then(t.step_func(function() {
|
||||
div.style.animationDuration = '200s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player, originalPlayer,
|
||||
@ -41,13 +43,12 @@ async_test(function(t) {
|
||||
assert_not_equals(player.currentTime, originalCurrentTime,
|
||||
'AnimationPlayer.currentTime has updated in next'
|
||||
+ ' requestAnimationFrame callback');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'AnimationPlayers preserve their startTime when changed');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
@ -65,7 +66,7 @@ test(function() {
|
||||
}, 'Updated AnimationPlayers maintain their order in the list');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 200s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
@ -73,8 +74,10 @@ async_test(function(t) {
|
||||
var player1 = players[0];
|
||||
var player2 = players[1];
|
||||
|
||||
// Wait before continuing so we can compare start times
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
// Wait before continuing so we can compare start times (otherwise the
|
||||
// new player objects and existing player objects will all have the same
|
||||
// start time).
|
||||
waitForAllPlayers(players).then(waitForFrame).then(t.step_func(function() {
|
||||
// Swap duration of first and second in list and prepend animation at the
|
||||
// same time
|
||||
div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
|
||||
@ -87,63 +90,69 @@ async_test(function(t) {
|
||||
'Second player is in third position after update');
|
||||
assert_equals(players[1].startTime, players[2].startTime,
|
||||
'Old players have the same start time');
|
||||
// TODO: Check that players[0].startTime === null
|
||||
return players[0].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime > players[1].startTime,
|
||||
'New player has later start time');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'Only the startTimes of existing animations are preserved');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
var secondPlayer = div.getAnimationPlayers()[1];
|
||||
|
||||
// Wait before continuing so we can compare start times
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
secondPlayer.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
// Trim list of animations
|
||||
div.style.animationName = 'anim1';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1, 'List of players was trimmed');
|
||||
assert_equals(players[0], secondPlayer,
|
||||
'Remaining player is the second one in the list');
|
||||
assert_equals(typeof(players[0].startTime), 'number',
|
||||
'Remaining player has resolved startTime');
|
||||
assert_true(players[0].startTime < players[0].timeline.currentTime,
|
||||
'Remaining player preserves startTime');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animations are removed from the start of the list while preserving'
|
||||
+ ' the state of existing players');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var firstAddedPlayer = div.getAnimationPlayers()[0];
|
||||
var firstAddedPlayer = div.getAnimationPlayers()[0],
|
||||
secondAddedPlayer,
|
||||
players;
|
||||
|
||||
// Wait and add second player
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
firstAddedPlayer.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
var secondAddedPlayer = div.getAnimationPlayers()[0];
|
||||
secondAddedPlayer = div.getAnimationPlayers()[0];
|
||||
|
||||
// Wait again and add another player
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_not_equals(firstAddedPlayer, secondAddedPlayer,
|
||||
'New players are added to start of the list');
|
||||
assert_equals(players[0], secondAddedPlayer,
|
||||
'Second player remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_equals(players[2], firstAddedPlayer,
|
||||
'First player remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_true(players[1].startTime > players[0].startTime,
|
||||
'Interleaved player starts later than existing players');
|
||||
assert_true(players[0].startTime > players[2].startTime,
|
||||
'Original players retain their start time');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
return secondAddedPlayer.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
|
||||
players = div.getAnimationPlayers();
|
||||
assert_not_equals(firstAddedPlayer, secondAddedPlayer,
|
||||
'New players are added to start of the list');
|
||||
assert_equals(players[0], secondAddedPlayer,
|
||||
'Second player remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_equals(players[2], firstAddedPlayer,
|
||||
'First player remains in same position after'
|
||||
+ ' interleaving');
|
||||
return players[1].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[1].startTime > players[0].startTime,
|
||||
'Interleaved player starts later than existing players');
|
||||
assert_true(players[0].startTime > players[2].startTime,
|
||||
'Original players retain their start time');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Player state is preserved when interleaving animations in list');
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
@ -18,95 +19,94 @@
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers returns an empty sequence for an element'
|
||||
+ ' with no animations');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for non-animated content');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.animation = 'anim1 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns a player running CSS Animations');
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS animation has a sensible start time');
|
||||
players[0].ready.then(t.step_func(function() {
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS animation has a sensible start time');
|
||||
|
||||
// Wait a moment then add a second animation.
|
||||
//
|
||||
// We wait for the next frame so that we can test that the start times of
|
||||
// the animations differ.
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
// Wait a moment then add a second animation.
|
||||
//
|
||||
// We wait for the next frame so that we can test that the start times of
|
||||
// the animations differ.
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s';
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers returns one player for each value of'
|
||||
+ ' animation-name');
|
||||
// Wait until both players are ready
|
||||
// (We don't make any assumptions about the order of the players since
|
||||
// that is the purpose of the following test.)
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime < players[1].startTime,
|
||||
'Additional players for CSS animations start after the original'
|
||||
+ ' animation and appear later in the list');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS Animations');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation that targets multiple properties
|
||||
div.style.animation = 'multiPropAnim 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns only one player for a CSS Animation'
|
||||
+ ' that targets multiple properties');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for multi-property animations');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.backgroundColor = 'red';
|
||||
div.style.animation = 'anim1 100s';
|
||||
window.getComputedStyle(div).backgroundColor;
|
||||
|
||||
// Wait a moment then add a transition
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
// Wait until a frame after the animation starts, then add a transition
|
||||
var players = div.getAnimationPlayers();
|
||||
players[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.backgroundColor = 'green';
|
||||
|
||||
var players = div.getAnimationPlayers();
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers returns players for both animations and '
|
||||
'getAnimationPlayers returns players for both animations and'
|
||||
+ ' transitions that run simultaneously');
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime > players[1].startTime,
|
||||
'players for transitions appear before animations even if they '
|
||||
'players for transitions appear before animations even if they'
|
||||
+ ' start later');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for both CSS Animations and Transitions at once');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers does not return players for finished '
|
||||
+ ' (and non-forwards-filling) CSS Animations');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
|
||||
@ -115,14 +115,13 @@ async_test(function(t) {
|
||||
}, 'getAnimationPlayers for CSS Animations that have finished');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns players for CSS Animations that have'
|
||||
+ ' finished but are filling forwards');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
|
||||
@ -131,8 +130,8 @@ async_test(function(t) {
|
||||
}, 'getAnimationPlayers for CSS Animations that have finished but are'
|
||||
+ ' forwards filling');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'none 100s';
|
||||
|
||||
var players = div.getAnimationPlayers();
|
||||
@ -145,12 +144,10 @@ test(function() {
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns players only for those CSS Animations whose'
|
||||
+ ' animation-name is not none');
|
||||
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with animation-name: none');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'missing 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 0,
|
||||
@ -162,67 +159,66 @@ test(function() {
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns players only for those CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with animation-name: missing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, notyet 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers initally only returns players for CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
players[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
var keyframes = '@keyframes notyet { to { left: 100px; } }';
|
||||
document.styleSheets[0].insertRule(keyframes, 0);
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers includes player when @keyframes rule is added'
|
||||
+ ' later');
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime < players[1].startTime,
|
||||
'Newly added player has a later start time');
|
||||
document.styleSheets[0].deleteRule(0);
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS Animations where the @keyframes rule is added'
|
||||
+ ' later');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 2,
|
||||
'getAnimationPlayers returns one player for each CSS animation-name'
|
||||
+ ' even if the names are duplicated');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with duplicated animation-name');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'empty 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns players for CSS animations with an'
|
||||
+ ' empty keyframes rule');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with empty keyframes rule');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns animations for CSS animations whose'
|
||||
+ ' delay makes them start later');
|
||||
assert_true(players[0].startTime <= document.timeline.currentTime,
|
||||
'For CSS Animations in delay phase, the start time of the player is'
|
||||
+ ' not in the future');
|
||||
div.remove();
|
||||
players[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime <= document.timeline.currentTime,
|
||||
'For CSS Animations in delay phase, the start time of the player is'
|
||||
+ ' not in the future');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS animations in delay phase');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 0s 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns animations for CSS animations whose'
|
||||
@ -230,8 +226,8 @@ test(function() {
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for zero-duration CSS Animations');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var originalPlayer = div.getAnimationPlayers()[0];
|
||||
|
||||
@ -252,8 +248,6 @@ test(function() {
|
||||
assert_equals(originalPlayer, extendedPlayer,
|
||||
'getAnimationPlayers returns the same objects even when their'
|
||||
+ ' duration changes');
|
||||
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers returns objects with the same identity');
|
||||
|
||||
</script>
|
||||
|
@ -2,13 +2,13 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a transition
|
||||
div.style.left = '0px';
|
||||
@ -18,7 +18,6 @@ test(function() {
|
||||
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, '',
|
||||
'Animation effects for transitions have an empty name');
|
||||
div.remove();
|
||||
}, 'Effect name for transitions');
|
||||
|
||||
</script>
|
||||
|
@ -2,28 +2,17 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
function waitForFrame() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.requestAnimationFrame(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
|
||||
div.style.marginLeft = '0px';
|
||||
@ -37,30 +26,23 @@ async_test(function(t) {
|
||||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.pause();
|
||||
});
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.pause();
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.play();
|
||||
});
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left increases after calling play()');
|
||||
});
|
||||
div.remove();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.play();
|
||||
return player.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left increases after calling play()');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() and play() a transition');
|
||||
|
||||
</script>
|
||||
|
@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.transform = 'translate(0px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
div.style.transition = 'transform 100s';
|
||||
div.style.transform = 'translate(10px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
var originalReadyPromise = player.ready;
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
player.pause();
|
||||
// TODO: When we implement deferred pausing, change this to
|
||||
// assert_not_equals and wait on the new promise before continuing.
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise does not change when pausing (for now)');
|
||||
player.play();
|
||||
assert_not_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise object identity differs after calling'
|
||||
+ ' play()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created each time play() is called'
|
||||
+ ' the animation property');
|
||||
|
||||
</script>
|
@ -2,13 +2,13 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
@ -18,7 +18,6 @@ test(function() {
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players[0].source.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
div.remove();
|
||||
}, 'Returned CSS transitions have the correct Animation.target');
|
||||
|
||||
</script>
|
||||
|
@ -2,18 +2,13 @@
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a couple of transitions
|
||||
div.style.left = '0px';
|
||||
@ -27,34 +22,35 @@ async_test(function(t) {
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers() returns one player per transitioning property');
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS transitions have sensible start times');
|
||||
assert_equals(players[0].startTime, players[1].startTime,
|
||||
'CSS transitions started together have the same start time');
|
||||
|
||||
// Wait a moment then add a third transition
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
waitForAllPlayers(players).then(t.step_func(function() {
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS transitions have sensible start times');
|
||||
assert_equals(players[0].startTime, players[1].startTime,
|
||||
'CSS transitions started together have the same start time');
|
||||
// Wait a moment then add a third transition
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.backgroundColor = 'green';
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 3,
|
||||
'getAnimationPlayers returns players for all running CSS Transitions');
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[1].startTime < players[2].startTime,
|
||||
'Player for additional CSS transition starts after the original'
|
||||
+ ' transitions and appears later in the list');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS Transitions');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('transitionend', t.step_func(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers does not return finished CSS Transitions');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
|
||||
@ -67,8 +63,8 @@ async_test(function(t) {
|
||||
window.getComputedStyle(div).left;
|
||||
}, 'getAnimationPlayers for CSS Transitions that have finished');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Try to transition non-animatable property animation-duration
|
||||
div.style.animationDuration = '10s';
|
||||
@ -80,11 +76,10 @@ test(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers returns an empty sequence for a transition'
|
||||
+ ' of a non-animatable property');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for transition on non-animatable property');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.setProperty('-vendor-unsupported', '0px', '');
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
@ -94,7 +89,6 @@ test(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers returns an empty sequence for a transition'
|
||||
+ ' of an unsupported property');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for transition on unsupported property');
|
||||
|
||||
</script>
|
||||
|
@ -1,14 +1,20 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
testcommon.js
|
||||
|
||||
[animation-timeline/test_animation-timeline.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[css-animations/test_animations-dynamic-changes.html]
|
||||
[css-animations/test_animation-effect-name.html]
|
||||
[css-animations/test_animation-pausing.html]
|
||||
[css-animations/test_animation-player-playstate.html]
|
||||
[css-animations/test_animation-player-ready.html]
|
||||
[css-animations/test_animation-target.html]
|
||||
[css-animations/test_element-get-animation-players.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[css-transitions/test_animation-effect-name.html]
|
||||
[css-transitions/test_animation-pausing.html]
|
||||
[css-transitions/test_animation-player-ready.html]
|
||||
[css-transitions/test_animation-target.html]
|
||||
[css-transitions/test_element-get-animation-players.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
|
36
dom/animation/test/testcommon.js
Normal file
36
dom/animation/test/testcommon.js
Normal file
@ -0,0 +1,36 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Appends a div to the document body.
|
||||
*
|
||||
* @param t The testharness.js Test object. If provided, this will be used
|
||||
* to register a cleanup callback to remove the div when the test
|
||||
* finishes.
|
||||
*/
|
||||
function addDiv(t) {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
if (t && typeof t.add_cleanup === 'function') {
|
||||
t.add_cleanup(function() { div.remove(); });
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise wrapper for requestAnimationFrame.
|
||||
*/
|
||||
function waitForFrame() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.requestAnimationFrame(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that takes a sequence of N players and returns:
|
||||
*
|
||||
* Promise.all([players[0].ready, players[1].ready, ... players[N-1].ready]);
|
||||
*/
|
||||
function waitForAllPlayers(players) {
|
||||
return Promise.all(players.map(function(player) { return player.ready; }));
|
||||
}
|
@ -505,7 +505,16 @@ this.DOMApplicationRegistry = {
|
||||
// Installs a 3rd party app.
|
||||
installPreinstalledApp: function installPreinstalledApp(aId) {
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
let app = this.webapps[aId];
|
||||
// In some cases, the app might be already installed under a different ID but
|
||||
// with the same manifestURL. In that case, the only content of the webapp will
|
||||
// be the id of the old version, which is the one we'll keep.
|
||||
let destId = this.webapps[aId].oldId || aId;
|
||||
// We don't need the oldId anymore
|
||||
if (destId !== aId) {
|
||||
delete this.webapps[aId];
|
||||
}
|
||||
|
||||
let app = this.webapps[destId];
|
||||
let baseDir, isPreinstalled = false;
|
||||
try {
|
||||
baseDir = FileUtils.getDir("coreAppsDir", ["webapps", aId], false);
|
||||
@ -545,10 +554,10 @@ this.DOMApplicationRegistry = {
|
||||
}
|
||||
|
||||
debug("Installing 3rd party app : " + aId +
|
||||
" from " + baseDir.path);
|
||||
" from " + baseDir.path + " to " + destId);
|
||||
|
||||
// We copy this app to DIRECTORY_NAME/$aId, and set the base path as needed.
|
||||
let destDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true);
|
||||
// We copy this app to DIRECTORY_NAME/$destId, and set the base path as needed.
|
||||
let destDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", destId], true, true);
|
||||
|
||||
filesToMove.forEach(function(aFile) {
|
||||
let file = baseDir.clone();
|
||||
@ -568,7 +577,7 @@ this.DOMApplicationRegistry = {
|
||||
return isPreinstalled;
|
||||
}
|
||||
|
||||
app.origin = "app://" + aId;
|
||||
app.origin = "app://" + destId;
|
||||
|
||||
// Do this for all preinstalled apps... we can't know at this
|
||||
// point if the updates will be signed or not and it doesn't
|
||||
@ -593,7 +602,7 @@ this.DOMApplicationRegistry = {
|
||||
// If we are unable to extract the manifest, cleanup and remove this app.
|
||||
debug("Cleaning up: " + e);
|
||||
destDir.remove(true);
|
||||
delete this.webapps[aId];
|
||||
delete this.webapps[destId];
|
||||
} finally {
|
||||
zipReader.close();
|
||||
}
|
||||
@ -667,7 +676,13 @@ this.DOMApplicationRegistry = {
|
||||
for (let id in data) {
|
||||
// Core apps have ids matching their domain name (eg: dialer.gaiamobile.org)
|
||||
// Use that property to check if they are new or not.
|
||||
if (!(id in this.webapps)) {
|
||||
// Note that in some cases, the id might change, but the
|
||||
// manifest URL wont. So consider that the app is old if
|
||||
// the id does not exist already and if there's no other id
|
||||
// for the manifestURL.
|
||||
var oldId = (id in this.webapps) ? id :
|
||||
this._appIdForManifestURL(data[id].manifestURL);
|
||||
if (!oldId) {
|
||||
this.webapps[id] = data[id];
|
||||
this.webapps[id].basePath = appDir.path;
|
||||
|
||||
@ -688,11 +703,17 @@ this.DOMApplicationRegistry = {
|
||||
// we fall into this case if the app is present in /system/b2g/webapps/webapps.json
|
||||
// and in /data/local/webapps/webapps.json: this happens when updating gaia apps
|
||||
// Confere bug 989876
|
||||
// We also should fall in this case when the app is a preinstalled third party app.
|
||||
for (let field in data[id]) {
|
||||
if (fieldsBlacklist.indexOf(field) === -1) {
|
||||
this.webapps[id][field] = data[id][field];
|
||||
this.webapps[oldId][field] = data[id][field];
|
||||
}
|
||||
}
|
||||
// If the id for the app has changed on the update, keep a pointer to the old one
|
||||
// since we'll need this to update the app files.
|
||||
if (id !== oldId) {
|
||||
this.webapps[id] = {oldId: oldId};
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this)).then(null, Cu.reportError);
|
||||
|
@ -569,8 +569,6 @@ AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
|
||||
AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread)
|
||||
: ScriptSettingsStackEntry()
|
||||
{
|
||||
MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContextForThread(),
|
||||
!JS_IsExceptionPending(nsContentUtils::GetCurrentJSContextForThread()));
|
||||
if (aIsMainThread) {
|
||||
mCxPusher.emplace(static_cast<JSContext*>(nullptr),
|
||||
/* aAllowNull = */ true);
|
||||
|
@ -375,7 +375,7 @@ nsDOMWindowUtils::SetDisplayPortForElement(float aXPx, float aYPx,
|
||||
new DisplayPortPropertyData(displayport, aPriority),
|
||||
nsINode::DeleteProperty<DisplayPortPropertyData>);
|
||||
|
||||
if (nsLayoutUtils::UsesAsyncScrolling()) {
|
||||
if (nsLayoutUtils::UsesAsyncScrolling() && gfxPrefs::LayoutUseContainersForRootFrames()) {
|
||||
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
|
||||
if (rootScrollFrame && content == rootScrollFrame->GetContent()) {
|
||||
// We are setting a root displayport for a document.
|
||||
|
@ -1995,6 +1995,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationTimeline)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayerTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
|
||||
@ -2078,6 +2079,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationTimeline)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayerTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
|
||||
@ -5266,7 +5268,7 @@ nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsIPresShell* shell = GetShell();
|
||||
if (!shell) {
|
||||
if (!shell || !shell->GetCanvasFrame()) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
@ -7391,6 +7393,16 @@ nsDocument::GetAnimationController()
|
||||
return mAnimationController;
|
||||
}
|
||||
|
||||
PendingPlayerTracker*
|
||||
nsDocument::GetOrCreatePendingPlayerTracker()
|
||||
{
|
||||
if (!mPendingPlayerTracker) {
|
||||
mPendingPlayerTracker = new PendingPlayerTracker();
|
||||
}
|
||||
|
||||
return mPendingPlayerTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the "direction" property of the document.
|
||||
*
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PendingPlayerTracker.h"
|
||||
#include "mozilla/dom/DOMImplementation.h"
|
||||
#include "mozilla/dom/StyleSheetList.h"
|
||||
#include "nsDataHashtable.h"
|
||||
@ -1047,6 +1048,15 @@ public:
|
||||
// If HasAnimationController is true, this is guaranteed to return non-null.
|
||||
nsSMILAnimationController* GetAnimationController() MOZ_OVERRIDE;
|
||||
|
||||
virtual mozilla::PendingPlayerTracker*
|
||||
GetPendingPlayerTracker() MOZ_FINAL
|
||||
{
|
||||
return mPendingPlayerTracker;
|
||||
}
|
||||
|
||||
virtual mozilla::PendingPlayerTracker*
|
||||
GetOrCreatePendingPlayerTracker() MOZ_OVERRIDE;
|
||||
|
||||
void SetImagesNeedAnimating(bool aAnimating) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SuppressEventHandling(SuppressionType aWhat,
|
||||
@ -1511,6 +1521,10 @@ protected:
|
||||
// Array of observers
|
||||
nsTObserverArray<nsIDocumentObserver*> mObservers;
|
||||
|
||||
// Tracker for animation players that are waiting to start.
|
||||
// nullptr until GetOrCreatePendingPlayerTracker is called.
|
||||
nsRefPtr<mozilla::PendingPlayerTracker> mPendingPlayerTracker;
|
||||
|
||||
// Weak reference to the scope object (aka the script global object)
|
||||
// that, unlike mScriptGlobalObject, is never unset once set. This
|
||||
// is a weak reference to avoid leaks due to circular references.
|
||||
|
@ -86,6 +86,7 @@ namespace mozilla {
|
||||
class CSSStyleSheet;
|
||||
class ErrorResult;
|
||||
class EventStates;
|
||||
class PendingPlayerTracker;
|
||||
class SVGAttrAnimationRuleProcessor;
|
||||
|
||||
namespace css {
|
||||
@ -1822,6 +1823,17 @@ public:
|
||||
// mAnimationController isn't yet initialized.
|
||||
virtual nsSMILAnimationController* GetAnimationController() = 0;
|
||||
|
||||
// Gets the tracker for animation players that are waiting to start.
|
||||
// Returns nullptr if there is no pending player tracker for this document
|
||||
// which will be the case if there have never been any CSS animations or
|
||||
// transitions on elements in the document.
|
||||
virtual mozilla::PendingPlayerTracker* GetPendingPlayerTracker() = 0;
|
||||
|
||||
// Gets the tracker for animation players that are waiting to start and
|
||||
// creates it if it doesn't already exist. As a result, the return value
|
||||
// will never be nullptr.
|
||||
virtual mozilla::PendingPlayerTracker* GetOrCreatePendingPlayerTracker() = 0;
|
||||
|
||||
// Makes the images on this document capable of having their animation
|
||||
// active or suspended. An Image will animate as long as at least one of its
|
||||
// owning Documents needs it to animate; otherwise it can suspend.
|
||||
|
@ -39,3 +39,5 @@ LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
||||
if not CONFIG['CLANG_CXX']:
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -2991,6 +2991,14 @@ void HTMLMediaElement::Error(uint16_t aErrorCode)
|
||||
aErrorCode == nsIDOMMediaError::MEDIA_ERR_NETWORK ||
|
||||
aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
|
||||
"Only use nsIDOMMediaError codes!");
|
||||
|
||||
// Since we have multiple paths calling into DecodeError, e.g.
|
||||
// MediaKeys::Terminated and EMEH264Decoder::Error. We should take the 1st
|
||||
// one only in order not to fire multiple 'error' events.
|
||||
if (mError) {
|
||||
return;
|
||||
}
|
||||
|
||||
mError = new MediaError(this, aErrorCode);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("error"));
|
||||
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
||||
|
@ -104,6 +104,16 @@ size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
|
||||
return functor.mSize;
|
||||
}
|
||||
|
||||
size_t MediaDecoderReader::SizeOfVideoQueueInFrames()
|
||||
{
|
||||
return mVideoQueue.GetSize();
|
||||
}
|
||||
|
||||
size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
|
||||
{
|
||||
return mAudioQueue.GetSize();
|
||||
}
|
||||
|
||||
nsresult MediaDecoderReader::ResetDecode()
|
||||
{
|
||||
nsresult res = NS_OK;
|
||||
|
@ -188,6 +188,9 @@ public:
|
||||
// the audio queue.
|
||||
size_t SizeOfAudioQueueInBytes() const;
|
||||
|
||||
virtual size_t SizeOfVideoQueueInFrames();
|
||||
virtual size_t SizeOfAudioQueueInFrames();
|
||||
|
||||
// Only used by WebMReader and MediaOmxReader for now, so stub here rather
|
||||
// than in every reader than inherits from MediaDecoderReader.
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
|
||||
|
@ -623,6 +623,9 @@ MediaDecoderStateMachine::DecodeVideo()
|
||||
mVideoDecodeStartTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
SAMPLE_LOG("DecodeVideo() queued=%i, decoder-queued=%o, skip=%i, time=%lld",
|
||||
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame, currentTime);
|
||||
|
||||
mReader->RequestVideoData(skipToNextKeyFrame, currentTime)
|
||||
->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
@ -669,6 +672,10 @@ MediaDecoderStateMachine::DecodeAudio()
|
||||
mIsAudioPrerolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
SAMPLE_LOG("DecodeAudio() queued=%i, decoder-queued=%o",
|
||||
AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
|
||||
|
||||
mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded);
|
||||
@ -2760,7 +2767,9 @@ void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
|
||||
return;
|
||||
}
|
||||
|
||||
VERBOSE_LOG("playing video frame %lld", aData->mTime);
|
||||
VERBOSE_LOG("playing video frame %lld (queued=%i, state-machine=%i, decoder-queued=%i)",
|
||||
aData->mTime, VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(),
|
||||
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames());
|
||||
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
if (container) {
|
||||
|
@ -26,3 +26,5 @@ LOCAL_INCLUDES += [
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -329,8 +329,8 @@ void
|
||||
CDMCallbackProxy::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
mProxy->gmp_Terminated();
|
||||
nsRefPtr<nsIRunnable> task = NS_NewRunnableMethod(mProxy, &CDMProxy::Terminated);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -569,11 +569,13 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Terminated()
|
||||
CDMProxy::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("CDM terminated");
|
||||
gmp_Shutdown();
|
||||
if (!mKeys.IsNull()) {
|
||||
mKeys->Terminated();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -104,6 +104,9 @@ public:
|
||||
// Main thread only.
|
||||
void Shutdown();
|
||||
|
||||
// Main thread only.
|
||||
void Terminated();
|
||||
|
||||
// Threadsafe.
|
||||
const nsCString& GetNodeId() const;
|
||||
|
||||
@ -157,9 +160,6 @@ public:
|
||||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Terminated();
|
||||
|
||||
CDMCaps& Capabilites();
|
||||
|
||||
// Main thread only.
|
||||
|
@ -66,6 +66,43 @@ MediaKeys::~MediaKeys()
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
CopySessions(const nsAString& aKey,
|
||||
nsRefPtr<MediaKeySession>& aSession,
|
||||
void* aClosure)
|
||||
{
|
||||
KeySessionHashMap* p = static_cast<KeySessionHashMap*>(aClosure);
|
||||
p->Put(aSession->GetSessionId(), aSession);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
CloseSessions(const nsAString& aKey,
|
||||
nsRefPtr<MediaKeySession>& aSession,
|
||||
void* aClosure)
|
||||
{
|
||||
aSession->OnClosed();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeys::Terminated()
|
||||
{
|
||||
KeySessionHashMap keySessions;
|
||||
// Remove entries during iteration will screw it. Make a copy first.
|
||||
mKeySessions.Enumerate(&CopySessions, &keySessions);
|
||||
keySessions.Enumerate(&CloseSessions, nullptr);
|
||||
keySessions.Clear();
|
||||
MOZ_ASSERT(mKeySessions.Count() == 0);
|
||||
|
||||
// Notify the element about that CDM has terminated.
|
||||
if (mElement) {
|
||||
mElement->DecodeError();
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeys::Shutdown()
|
||||
{
|
||||
|
@ -104,6 +104,10 @@ public:
|
||||
|
||||
void Shutdown();
|
||||
|
||||
// Called by CDMProxy when CDM crashes or shuts down. It is different from
|
||||
// Shutdown which is called from the script/dom side.
|
||||
void Terminated();
|
||||
|
||||
// Returns true if this MediaKeys has been bound to a media element.
|
||||
bool IsBoundToMediaElement() const;
|
||||
|
||||
|
@ -647,6 +647,26 @@ MP4Reader::PopSample(TrackType aTrack)
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
MP4Reader::SizeOfVideoQueueInFrames()
|
||||
{
|
||||
return SizeOfQueue(kVideo);
|
||||
}
|
||||
|
||||
size_t
|
||||
MP4Reader::SizeOfAudioQueueInFrames()
|
||||
{
|
||||
return SizeOfQueue(kAudio);
|
||||
}
|
||||
|
||||
size_t
|
||||
MP4Reader::SizeOfQueue(TrackType aTrack)
|
||||
{
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
MonitorAutoLock lock(decoder.mMonitor);
|
||||
return decoder.mOutput.Length() + (decoder.mNumSamplesInput - decoder.mNumSamplesOutput);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MP4Reader::ResetDecode()
|
||||
{
|
||||
|
@ -37,6 +37,9 @@ public:
|
||||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
|
||||
|
||||
virtual size_t SizeOfVideoQueueInFrames() MOZ_OVERRIDE;
|
||||
virtual size_t SizeOfAudioQueueInFrames() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
@ -117,6 +120,8 @@ private:
|
||||
bool IsWaitingOnCodecResource();
|
||||
virtual bool IsWaitingOnCDMResource() MOZ_OVERRIDE;
|
||||
|
||||
size_t SizeOfQueue(TrackType aTrack);
|
||||
|
||||
nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
|
||||
nsAutoPtr<PlatformDecoderModule> mPlatform;
|
||||
|
||||
|
@ -93,6 +93,26 @@ MediaSourceReader::IsWaitingMediaResources()
|
||||
return !mHasEssentialTrackBuffers;
|
||||
}
|
||||
|
||||
size_t
|
||||
MediaSourceReader::SizeOfVideoQueueInFrames()
|
||||
{
|
||||
if (!mVideoReader) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::SizeOfVideoQueue called with no video reader", this);
|
||||
return 0;
|
||||
}
|
||||
return mVideoReader->SizeOfVideoQueueInFrames();
|
||||
}
|
||||
|
||||
size_t
|
||||
MediaSourceReader::SizeOfAudioQueueInFrames()
|
||||
{
|
||||
if (!mAudioReader) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::SizeOfAudioQueue called with no audio reader", this);
|
||||
return 0;
|
||||
}
|
||||
return mAudioReader->SizeOfAudioQueueInFrames();
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
||||
MediaSourceReader::RequestAudioData()
|
||||
{
|
||||
|
@ -50,6 +50,9 @@ public:
|
||||
nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
virtual size_t SizeOfVideoQueueInFrames() MOZ_OVERRIDE;
|
||||
virtual size_t SizeOfAudioQueueInFrames() MOZ_OVERRIDE;
|
||||
|
||||
void OnAudioDecoded(AudioData* aSample);
|
||||
void OnAudioNotDecoded(NotDecodedReason aReason);
|
||||
void OnVideoDecoded(VideoData* aSample);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "SourceBufferResource.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -43,6 +44,7 @@ TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& a
|
||||
mParser = ContainerParser::CreateForMIMEType(aType);
|
||||
mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
|
||||
aParentDecoder->AddTrackBuffer(this);
|
||||
mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
|
||||
}
|
||||
|
||||
TrackBuffer::~TrackBuffer()
|
||||
@ -150,7 +152,8 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
|
||||
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
|
||||
if (mParser->IsMediaSegmentPresent(aData, aLength) &&
|
||||
mLastEndTimestamp &&
|
||||
!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value())) {
|
||||
(!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
|
||||
mDecoderPerSegment)) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
|
||||
this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
|
||||
|
||||
|
@ -162,6 +162,7 @@ private:
|
||||
|
||||
void ContinueShutdown();
|
||||
MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
|
||||
bool mDecoderPerSegment;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -36,3 +36,5 @@ CXXFLAGS += [
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -105,3 +105,5 @@ CXXFLAGS += [
|
||||
|
||||
if CONFIG['ANDROID_VERSION'] > '15':
|
||||
DEFINES['MOZ_OMX_WEBM_DECODER'] = True
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -171,7 +171,7 @@ BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList)
|
||||
IPCByteRanges ranges;
|
||||
for (; aRangeList; aRangeList = aRangeList->next) {
|
||||
IPCByteRange br = {aRangeList->offset, aRangeList->length};
|
||||
ranges.push_back(br);
|
||||
ranges.AppendElement(br);
|
||||
}
|
||||
|
||||
NPError result;
|
||||
|
@ -58,16 +58,16 @@ BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges,
|
||||
if (!mStream)
|
||||
return false;
|
||||
|
||||
if (ranges.size() > INT32_MAX)
|
||||
if (ranges.Length() > INT32_MAX)
|
||||
return false;
|
||||
|
||||
nsAutoArrayPtr<NPByteRange> rp(new NPByteRange[ranges.size()]);
|
||||
for (uint32_t i = 0; i < ranges.size(); ++i) {
|
||||
nsAutoArrayPtr<NPByteRange> rp(new NPByteRange[ranges.Length()]);
|
||||
for (uint32_t i = 0; i < ranges.Length(); ++i) {
|
||||
rp[i].offset = ranges[i].offset;
|
||||
rp[i].length = ranges[i].length;
|
||||
rp[i].next = &rp[i + 1];
|
||||
}
|
||||
rp[ranges.size() - 1].next = nullptr;
|
||||
rp[ranges.Length() - 1].next = nullptr;
|
||||
|
||||
*result = mNPP->mNPNIface->requestread(mStream, rp);
|
||||
return true;
|
||||
|
@ -75,7 +75,7 @@ struct IPCByteRange
|
||||
uint32_t length;
|
||||
};
|
||||
|
||||
typedef std::vector<IPCByteRange> IPCByteRanges;
|
||||
typedef nsTArray<IPCByteRange> IPCByteRanges;
|
||||
|
||||
typedef nsCString Buffer;
|
||||
|
||||
|
@ -271,26 +271,30 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
|
||||
{
|
||||
nsAsyncRedirectAutoCallback autoCallback(callback);
|
||||
|
||||
nsCOMPtr<nsIURI> newUri;
|
||||
nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// No need to continue processing if CSP is disabled or if the protocol
|
||||
// is *not* subject to CSP.
|
||||
// Please note, the correct way to opt-out of CSP using a custom
|
||||
// protocolHandler is to set one of the nsIProtocolHandler flags
|
||||
// that are whitelistet in subjectToCSP()
|
||||
if (!sCSPEnabled || !subjectToCSP(newUri)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
nsresult rv = oldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
rv = oldChannel->GetLoadInfo(getter_AddRefs(loadInfo));
|
||||
|
||||
// if no loadInfo on the channel, nothing for us to do
|
||||
if (!loadInfo) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// The loadInfo must not necessarily contain a Node, hence we try to query
|
||||
// the CSP in the following order:
|
||||
// a) Get the Node, the Principal of that Node, and the CSP of that Principal
|
||||
// b) Get the Principal and the CSP of that Principal
|
||||
|
||||
nsCOMPtr<nsINode> loadingNode = loadInfo->LoadingNode();
|
||||
nsCOMPtr<nsIPrincipal> principal = loadingNode ?
|
||||
loadingNode->NodePrincipal() :
|
||||
loadInfo->LoadingPrincipal();
|
||||
NS_ASSERTION(principal, "Can not evaluate CSP without a principal");
|
||||
// Get the LoadingPrincipal and CSP from the loadInfo
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = principal->GetCsp(getter_AddRefs(csp));
|
||||
rv = loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// if there is no CSP, nothing for us to do
|
||||
@ -305,10 +309,6 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
|
||||
* We check if the CSP permits this host for this type of load, if not,
|
||||
* we cancel the load now.
|
||||
*/
|
||||
|
||||
nsCOMPtr<nsIURI> newUri;
|
||||
rv = newChannel->GetURI(getter_AddRefs(newUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIURI> originalUri;
|
||||
rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -331,12 +331,14 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
|
||||
("CSPService::AsyncOnChannelRedirect called for %s",
|
||||
newUriSpec.get()));
|
||||
}
|
||||
if (aDecision == 1)
|
||||
if (aDecision == 1) {
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSPService::AsyncOnChannelRedirect ALLOWING request."));
|
||||
else
|
||||
}
|
||||
else {
|
||||
PR_LOG(gCspPRLog, PR_LOG_DEBUG,
|
||||
("CSPService::AsyncOnChannelRedirect CANCELLING request."));
|
||||
}
|
||||
#endif
|
||||
|
||||
// if ShouldLoad doesn't accept the load, cancel the request
|
||||
|
@ -149,10 +149,6 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType)
|
||||
case nsIContentPolicy::TYPE_MEDIA:
|
||||
return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE;
|
||||
|
||||
// TYPE_DOCUMENT shouldn't be used since it's specifically whitelisted by
|
||||
// the CSPService, but in case we do want to know which directive to check,
|
||||
// FRAME_SRC is the best fit.
|
||||
case nsIContentPolicy::TYPE_DOCUMENT:
|
||||
case nsIContentPolicy::TYPE_SUBDOCUMENT:
|
||||
return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
|
||||
|
||||
@ -172,8 +168,13 @@ CSP_ContentTypeToDirective(nsContentPolicyType aType)
|
||||
case nsIContentPolicy::TYPE_OTHER:
|
||||
return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
|
||||
|
||||
// CSP can not block csp reports, fall through to error
|
||||
// csp shold not block top level loads, e.g. in case
|
||||
// of a redirect.
|
||||
case nsIContentPolicy::TYPE_DOCUMENT:
|
||||
// CSP can not block csp reports
|
||||
case nsIContentPolicy::TYPE_CSP_REPORT:
|
||||
return nsIContentSecurityPolicy::NO_DIRECTIVE;
|
||||
|
||||
// Fall through to error for all other directives
|
||||
default:
|
||||
MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective");
|
||||
|
@ -25,6 +25,11 @@ try {
|
||||
Services.prefs.getBoolPref("dom.mozSettings.SettingsRequestManager.verbose.enabled");
|
||||
} catch (ex) { }
|
||||
|
||||
let allowForceReadOnly = false;
|
||||
try {
|
||||
allowForceReadOnly = Services.prefs.getBoolPref("dom.mozSettings.allowForceReadOnly");
|
||||
} catch (ex) { }
|
||||
|
||||
function debug(s) {
|
||||
dump("-*- SettingsRequestManager: " + s + "\n");
|
||||
}
|
||||
@ -120,6 +125,12 @@ function SettingsLockInfo(aDB, aMsgMgr, aPrincipal, aLockID, aIsServiceLock, aWi
|
||||
canClear: true,
|
||||
// Lets us know if this lock has been used to clear at any point.
|
||||
hasCleared: false,
|
||||
// forceReadOnly sets whether we want to do a read only transaction. Define
|
||||
// true by default, and let queueTask() set this to false if we queue any
|
||||
// "set" task. Since users of settings locks will queue all tasks before
|
||||
// any idb transaction is created, we know we will have all needed
|
||||
// information to set this before creating a transaction.
|
||||
forceReadOnly: true,
|
||||
// Principal the lock was created under. We assume that the lock
|
||||
// will continue to exist under this principal for the duration of
|
||||
// its lifetime.
|
||||
@ -146,7 +157,8 @@ function SettingsLockInfo(aDB, aMsgMgr, aPrincipal, aLockID, aIsServiceLock, aWi
|
||||
// slightly slower on apps with full settings permissions, but
|
||||
// it means we don't have to do our own transaction order
|
||||
// bookkeeping.
|
||||
if (!SettingsPermissions.hasSomeWritePermission(this.principal)) {
|
||||
let canReadOnly = allowForceReadOnly && this.forceReadOnly;
|
||||
if (canReadOnly || !SettingsPermissions.hasSomeWritePermission(this.principal)) {
|
||||
if (VERBOSE) debug("Making READONLY transaction for " + this.lockID);
|
||||
this._transaction = aDB._db.transaction(SETTINGSSTORE_NAME, "readonly");
|
||||
} else {
|
||||
@ -254,7 +266,11 @@ let SettingsRequestManager = {
|
||||
aData.settings = this._serializePreservingBinaries(aData.settings);
|
||||
}
|
||||
|
||||
this.lockInfo[aData.lockID].tasks.push({
|
||||
if (aOperation === "set" || aOperation === "clear") {
|
||||
lock.forceReadOnly = false;
|
||||
}
|
||||
|
||||
lock.tasks.push({
|
||||
operation: aOperation,
|
||||
data: aData,
|
||||
defer: defer
|
||||
|
@ -28,9 +28,10 @@ interface AnimationPlayer {
|
||||
attribute double playbackRate; */
|
||||
[BinaryName="playStateFromJS"]
|
||||
readonly attribute AnimationPlayState playState;
|
||||
[Throws]
|
||||
readonly attribute Promise<AnimationPlayer> ready;
|
||||
/*
|
||||
readonly attribute Promise ready;
|
||||
readonly attribute Promise finished;
|
||||
readonly attribute Promise<AnimationPlayer> finished;
|
||||
void cancel ();
|
||||
void finish ();
|
||||
*/
|
||||
|
@ -330,6 +330,9 @@ private:
|
||||
|
||||
DECL_GFX_PREF(Once, "dom.vr.enabled", VREnabled, bool, false);
|
||||
DECL_GFX_PREF(Once, "dom.vr.add-test-devices", VRAddTestDevices, int32_t, 1);
|
||||
|
||||
// This and code dependent on it should be removed once containerless scrolling looks stable.
|
||||
DECL_GFX_PREF(Once, "layout.scroll.root-frame-containers", LayoutUseContainersForRootFrames, bool, true);
|
||||
public:
|
||||
// Manage the singleton:
|
||||
static gfxPrefs& GetSingleton()
|
||||
|
@ -264,7 +264,6 @@ CreateSimdClass(JSContext *cx,
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(type)));
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
|
||||
typeDescr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(nullptr));
|
||||
|
||||
if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr))
|
||||
return nullptr;
|
||||
|
@ -1435,7 +1435,7 @@ TypedObject::GetBuffer(JSContext *cx, unsigned argc, Value *vp)
|
||||
else
|
||||
buffer = obj.as<InlineTransparentTypedObject>().getOrCreateBuffer(cx);
|
||||
if (!buffer)
|
||||
MOZ_CRASH();
|
||||
return false;
|
||||
args.rval().setObject(*buffer);
|
||||
return true;
|
||||
}
|
||||
@ -1595,6 +1595,8 @@ TypedObject::createZeroed(JSContext *cx, HandleTypeDescr descr, int32_t length,
|
||||
// If possible, create an object with inline data.
|
||||
if ((size_t) descr->size() <= InlineTypedObject::MaximumSize) {
|
||||
InlineTypedObject *obj = InlineTypedObject::create(cx, descr, heap);
|
||||
if (!obj)
|
||||
return nullptr;
|
||||
descr->initInstances(cx->runtime(), obj->inlineTypedMem(), 1);
|
||||
return obj;
|
||||
}
|
||||
@ -3182,10 +3184,8 @@ CreateTraceList(JSContext *cx, HandleTypeDescr descr)
|
||||
// for larger objects, both to limit the size of the trace lists and
|
||||
// because tracing outline typed objects is considerably more complicated
|
||||
// than inline ones.
|
||||
if ((size_t) descr->size() > InlineTypedObject::MaximumSize || descr->transparent()) {
|
||||
descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(nullptr));
|
||||
if ((size_t) descr->size() > InlineTypedObject::MaximumSize || descr->transparent())
|
||||
return true;
|
||||
}
|
||||
|
||||
TraceListVisitor visitor;
|
||||
visitReferences(*descr, nullptr, visitor);
|
||||
@ -3196,10 +3196,8 @@ CreateTraceList(JSContext *cx, HandleTypeDescr descr)
|
||||
|
||||
// Trace lists aren't necessary for descriptors with no references.
|
||||
MOZ_ASSERT(entries.length() >= 3);
|
||||
if (entries.length() == 3) {
|
||||
descr->initReservedSlot(JS_DESCR_SLOT_TRACE_LIST, PrivateValue(nullptr));
|
||||
if (entries.length() == 3)
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t *list = cx->pod_malloc<int32_t>(entries.length());
|
||||
if (!list)
|
||||
@ -3214,5 +3212,6 @@ CreateTraceList(JSContext *cx, HandleTypeDescr descr)
|
||||
/* static */ void
|
||||
TypeDescr::finalize(FreeOp *fop, JSObject *obj)
|
||||
{
|
||||
js_free(const_cast<int32_t *>(obj->as<TypeDescr>().traceList()));
|
||||
if (obj->as<TypeDescr>().hasTraceList())
|
||||
js_free(const_cast<int32_t *>(obj->as<TypeDescr>().traceList()));
|
||||
}
|
||||
|
@ -178,13 +178,17 @@ class TypeDescr : public NativeObject
|
||||
// typed objects, rather than the slower trace hook. This list is only
|
||||
// specified when (a) the descriptor is short enough that it can fit in an
|
||||
// InlineTypedObject, and (b) the descriptor contains at least one
|
||||
// reference. Otherwise it is null.
|
||||
// reference. Otherwise its value is undefined.
|
||||
//
|
||||
// The list is three consecutive arrays of int32_t offsets, with each array
|
||||
// terminated by -1. The arrays store offsets of string, object, and value
|
||||
// references in the descriptor, in that order.
|
||||
bool hasTraceList() const {
|
||||
return !getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).isUndefined();
|
||||
}
|
||||
const int32_t *traceList() const {
|
||||
return reinterpret_cast<int32_t *>(getReservedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
|
||||
MOZ_ASSERT(hasTraceList());
|
||||
return reinterpret_cast<int32_t *>(getFixedSlot(JS_DESCR_SLOT_TRACE_LIST).toPrivate());
|
||||
}
|
||||
|
||||
void initInstances(const JSRuntime *rt, uint8_t *mem, size_t length);
|
||||
|
@ -1786,9 +1786,10 @@ GCMarker::processMarkStackTop(SliceBudget &budget)
|
||||
|
||||
scan_typed_obj:
|
||||
{
|
||||
const int32_t *list = obj->as<InlineOpaqueTypedObject>().typeDescr().traceList();
|
||||
if (!list)
|
||||
TypeDescr *descr = &obj->as<InlineOpaqueTypedObject>().typeDescr();
|
||||
if (!descr->hasTraceList())
|
||||
return;
|
||||
const int32_t *list = descr->traceList();
|
||||
uint8_t *memory = obj->as<InlineOpaqueTypedObject>().inlineTypedMem();
|
||||
while (*list != -1) {
|
||||
JSString *str = *reinterpret_cast<JSString **>(memory + *list);
|
||||
|
@ -1881,9 +1881,9 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
|
||||
if (isFloat) {
|
||||
VFPRegister vd(ToFloatRegister(ins->value()));
|
||||
if (size == 32)
|
||||
masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Always);
|
||||
masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, 0, Assembler::Always);
|
||||
else
|
||||
masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Always);
|
||||
masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Always);
|
||||
} else {
|
||||
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
|
||||
ToRegister(ins->value()), Offset, Assembler::Always);
|
||||
@ -1896,9 +1896,9 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins)
|
||||
if (isFloat) {
|
||||
VFPRegister vd(ToFloatRegister(ins->value()));
|
||||
if (size == 32)
|
||||
masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, Assembler::Below);
|
||||
masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, 0, Assembler::Below);
|
||||
else
|
||||
masm.ma_vstr(vd, HeapReg, ptrReg, 0, Assembler::Below);
|
||||
masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Below);
|
||||
} else {
|
||||
masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
|
||||
ToRegister(ins->value()), Offset, Assembler::Below);
|
||||
|
@ -1760,10 +1760,11 @@ MacroAssemblerARM::ma_vstr(VFPRegister src, const Operand &addr, Condition cc)
|
||||
return ma_vdtr(IsStore, addr, src, cc);
|
||||
}
|
||||
BufferOffset
|
||||
MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift, Condition cc)
|
||||
MacroAssemblerARM::ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
|
||||
int32_t offset, Condition cc)
|
||||
{
|
||||
as_add(ScratchRegister, base, lsl(index, shift), NoSetCond, cc);
|
||||
return ma_vstr(src, Operand(ScratchRegister, 0), cc);
|
||||
return ma_vstr(src, Operand(ScratchRegister, offset), cc);
|
||||
}
|
||||
|
||||
void
|
||||
@ -3631,7 +3632,6 @@ void
|
||||
MacroAssemblerARMCompat::storePayload(const Value &val, const BaseIndex &dest)
|
||||
{
|
||||
unsigned shift = ScaleToShift(dest.scale);
|
||||
MOZ_ASSERT(dest.offset == 0);
|
||||
|
||||
jsval_layout jv = JSVAL_TO_IMPL(val);
|
||||
if (val.isMarkable())
|
||||
@ -3644,8 +3644,17 @@ MacroAssemblerARMCompat::storePayload(const Value &val, const BaseIndex &dest)
|
||||
// be integrated into the as_dtr call.
|
||||
JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
|
||||
|
||||
// If an offset is used, modify the base so that a [base + index << shift]
|
||||
// instruction format can be used.
|
||||
if (dest.offset != 0)
|
||||
ma_add(dest.base, Imm32(dest.offset), dest.base);
|
||||
|
||||
as_dtr(IsStore, 32, Offset, ScratchRegister,
|
||||
DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
|
||||
|
||||
// Restore the original value of the base, if necessary.
|
||||
if (dest.offset != 0)
|
||||
ma_sub(dest.base, Imm32(dest.offset), dest.base);
|
||||
}
|
||||
|
||||
void
|
||||
@ -3653,16 +3662,22 @@ MacroAssemblerARMCompat::storePayload(Register src, const BaseIndex &dest)
|
||||
{
|
||||
unsigned shift = ScaleToShift(dest.scale);
|
||||
MOZ_ASSERT(shift < 32);
|
||||
MOZ_ASSERT(dest.offset == 0);
|
||||
|
||||
// If NUNBOX32_PAYLOAD_OFFSET is not zero, the memory operand [base + index
|
||||
// << shift + imm] cannot be encoded into a single instruction, and cannot
|
||||
// be integrated into the as_dtr call.
|
||||
JS_STATIC_ASSERT(NUNBOX32_PAYLOAD_OFFSET == 0);
|
||||
|
||||
// Save/restore the base if the BaseIndex has an offset, as above.
|
||||
if (dest.offset != 0)
|
||||
ma_add(dest.base, Imm32(dest.offset), dest.base);
|
||||
|
||||
// Technically, shift > -32 can be handle by changing LSL to ASR, but should
|
||||
// never come up, and this is one less code path to get wrong.
|
||||
as_dtr(IsStore, 32, Offset, src, DTRAddr(dest.base, DtrRegImmShift(dest.index, LSL, shift)));
|
||||
|
||||
if (dest.offset != 0)
|
||||
ma_sub(dest.base, Imm32(dest.offset), dest.base);
|
||||
}
|
||||
|
||||
void
|
||||
@ -3683,7 +3698,6 @@ MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex &dest)
|
||||
Register base = dest.base;
|
||||
Register index = dest.index;
|
||||
unsigned shift = ScaleToShift(dest.scale);
|
||||
MOZ_ASSERT(dest.offset == 0);
|
||||
MOZ_ASSERT(base != ScratchRegister);
|
||||
MOZ_ASSERT(index != ScratchRegister);
|
||||
|
||||
@ -3693,10 +3707,10 @@ MacroAssemblerARMCompat::storeTypeTag(ImmTag tag, const BaseIndex &dest)
|
||||
// immediate that is being stored into said memory location. Work around
|
||||
// this by modifying the base so the valid [base + index << shift] format
|
||||
// can be used, then restore it.
|
||||
ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
|
||||
ma_add(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
|
||||
ma_mov(tag, ScratchRegister);
|
||||
ma_str(ScratchRegister, DTRAddr(base, DtrRegImmShift(index, LSL, shift)));
|
||||
ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET), base);
|
||||
ma_sub(base, Imm32(NUNBOX32_TYPE_OFFSET + dest.offset), base);
|
||||
}
|
||||
|
||||
// ARM says that all reads of pc will return 8 higher than the address of the
|
||||
|
@ -398,7 +398,8 @@ class MacroAssemblerARM : public Assembler
|
||||
BufferOffset ma_vstr(VFPRegister src, VFPAddr addr, Condition cc = Always);
|
||||
BufferOffset ma_vstr(VFPRegister src, const Operand &addr, Condition cc = Always);
|
||||
|
||||
BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift = defaultShift, Condition cc = Always);
|
||||
BufferOffset ma_vstr(VFPRegister src, Register base, Register index, int32_t shift,
|
||||
int32_t offset, Condition cc = Always);
|
||||
// Calls an Ion function, assumes that the stack is untouched (8 byte
|
||||
// aligned).
|
||||
void ma_callJit(const Register reg);
|
||||
@ -1417,10 +1418,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
ma_vstr(src, Operand(addr));
|
||||
}
|
||||
void storeDouble(FloatRegister src, BaseIndex addr) {
|
||||
// Harder cases not handled yet.
|
||||
MOZ_ASSERT(addr.offset == 0);
|
||||
uint32_t scale = Imm32::ShiftOf(addr.scale).value;
|
||||
ma_vstr(src, addr.base, addr.index, scale);
|
||||
ma_vstr(src, addr.base, addr.index, scale, addr.offset);
|
||||
}
|
||||
void moveDouble(FloatRegister src, FloatRegister dest) {
|
||||
ma_vmov(src, dest);
|
||||
@ -1430,10 +1429,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
|
||||
ma_vstr(VFPRegister(src).singleOverlay(), Operand(addr));
|
||||
}
|
||||
void storeFloat32(FloatRegister src, BaseIndex addr) {
|
||||
// Harder cases not handled yet.
|
||||
MOZ_ASSERT(addr.offset == 0);
|
||||
uint32_t scale = Imm32::ShiftOf(addr.scale).value;
|
||||
ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale);
|
||||
ma_vstr(VFPRegister(src).singleOverlay(), addr.base, addr.index, scale, addr.offset);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -5265,7 +5265,12 @@ TypeObject::setAddendum(AddendumKind kind, void *addendum)
|
||||
MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
|
||||
MOZ_ASSERT(!(flags_ & OBJECT_FLAG_ADDENDUM_MASK));
|
||||
|
||||
writeBarrierPre(this);
|
||||
// Manually trigger barriers if we are clearing a TypeNewScript. Other
|
||||
// kinds of addendums are immutable.
|
||||
if (addendum_) {
|
||||
MOZ_ASSERT(kind == Addendum_NewScript);
|
||||
TypeNewScript::writeBarrierPre(newScript());
|
||||
}
|
||||
|
||||
flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
|
||||
addendum_ = addendum;
|
||||
|
@ -985,6 +985,8 @@ class TypeNewScript
|
||||
js_free(initializerList);
|
||||
}
|
||||
|
||||
static inline void writeBarrierPre(TypeNewScript *newScript);
|
||||
|
||||
bool analyzed() const {
|
||||
if (preliminaryObjects) {
|
||||
MOZ_ASSERT(!templateObject());
|
||||
|
@ -1300,6 +1300,17 @@ TypeObject::getProperty(unsigned i)
|
||||
return propertySet[i];
|
||||
}
|
||||
|
||||
inline void
|
||||
TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
|
||||
{
|
||||
if (!newScript->fun->runtimeFromAnyThread()->needsIncrementalBarrier())
|
||||
return;
|
||||
|
||||
JS::Zone *zone = newScript->fun->zoneFromAnyThread();
|
||||
if (zone->needsIncrementalBarrier())
|
||||
newScript->trace(zone->barrierTracer());
|
||||
}
|
||||
|
||||
} } /* namespace js::types */
|
||||
|
||||
inline js::types::TypeScript *
|
||||
|
@ -3034,9 +3034,11 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList)
|
||||
scrollItem->IsDisplayPortOpaque();
|
||||
newLayerEntry->mBaseFrameMetrics =
|
||||
scrollItem->ComputeFrameMetrics(ownLayer, mParameters);
|
||||
} else if (itemType == nsDisplayItem::TYPE_SUBDOCUMENT ||
|
||||
itemType == nsDisplayItem::TYPE_ZOOM ||
|
||||
itemType == nsDisplayItem::TYPE_RESOLUTION) {
|
||||
} else if ((itemType == nsDisplayItem::TYPE_SUBDOCUMENT ||
|
||||
itemType == nsDisplayItem::TYPE_ZOOM ||
|
||||
itemType == nsDisplayItem::TYPE_RESOLUTION) &&
|
||||
gfxPrefs::LayoutUseContainersForRootFrames())
|
||||
{
|
||||
newLayerEntry->mBaseFrameMetrics =
|
||||
static_cast<nsDisplaySubDocument*>(item)->ComputeFrameMetrics(ownLayer, mParameters);
|
||||
}
|
||||
|
@ -1503,23 +1503,25 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
|
||||
root->SetPostScale(1.0f/containerParameters.mXScale,
|
||||
1.0f/containerParameters.mYScale);
|
||||
|
||||
bool isRoot = presContext->IsRootContentDocument();
|
||||
if (gfxPrefs::LayoutUseContainersForRootFrames()) {
|
||||
bool isRoot = presContext->IsRootContentDocument();
|
||||
|
||||
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
|
||||
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
|
||||
|
||||
nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize());
|
||||
nsRect viewport(aBuilder->ToReferenceFrame(frame), frame->GetSize());
|
||||
|
||||
root->SetFrameMetrics(
|
||||
nsDisplayScrollLayer::ComputeFrameMetrics(frame, rootScrollFrame,
|
||||
aBuilder->FindReferenceFrameFor(frame),
|
||||
root, FrameMetrics::NULL_SCROLL_ID, viewport,
|
||||
!isRoot, isRoot, containerParameters));
|
||||
root->SetFrameMetrics(
|
||||
nsDisplayScrollLayer::ComputeFrameMetrics(frame, rootScrollFrame,
|
||||
aBuilder->FindReferenceFrameFor(frame),
|
||||
root, FrameMetrics::NULL_SCROLL_ID, viewport,
|
||||
!isRoot, isRoot, containerParameters));
|
||||
}
|
||||
|
||||
// NS_WARNING is debug-only, so don't even bother checking the conditions in
|
||||
// a release build.
|
||||
#ifdef DEBUG
|
||||
bool usingDisplayport = false;
|
||||
if (rootScrollFrame) {
|
||||
if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
|
||||
nsIContent* content = rootScrollFrame->GetContent();
|
||||
if (content) {
|
||||
usingDisplayport = nsLayoutUtils::GetDisplayPort(content, nullptr);
|
||||
|
@ -1014,7 +1014,7 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent,
|
||||
aMargins, aPriority),
|
||||
nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
|
||||
|
||||
if (nsLayoutUtils::UsesAsyncScrolling()) {
|
||||
if (nsLayoutUtils::UsesAsyncScrolling() && gfxPrefs::LayoutUseContainersForRootFrames()) {
|
||||
nsIFrame* rootScrollFrame = aPresShell->GetRootScrollFrame();
|
||||
if (rootScrollFrame && aContent == rootScrollFrame->GetContent()) {
|
||||
// We are setting a root displayport for a document.
|
||||
|
@ -1,5 +1,6 @@
|
||||
[DEFAULT]
|
||||
skip-if = toolkit == 'android' #SLOW_DIRECTORY
|
||||
# Android: SLOW_DIRECTORY; Mulet: bug 1048441, bug 1087611, bug 1112988, etc.
|
||||
skip-if = toolkit == 'android' || buildapp == 'mulet'
|
||||
support-files =
|
||||
border_radius_hit_testing_iframe.html
|
||||
preserve3d_sorting_hit_testing_iframe.html
|
||||
@ -186,7 +187,6 @@ support-files = file_bug607529.html
|
||||
[test_bug667512.html]
|
||||
[test_bug677878.html]
|
||||
[test_bug696020.html]
|
||||
skip-if = buildapp == 'mulet' #Bug 1048441, unexplained almost permanent orange on mulet
|
||||
[test_event_target_radius.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040987
|
||||
[test_event_target_iframe_oop.html]
|
||||
@ -199,7 +199,6 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e1
|
||||
support-files = bug583889_inner1.html bug583889_inner2.html
|
||||
[test_bug582771.html]
|
||||
[test_bug968148.html]
|
||||
skip-if = buildapp == 'mulet' # bug 1087611
|
||||
support-files = bug968148_inner.html
|
||||
[test_bug603550.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT # b2g(Components.classes[@mozilla.org/widget/dragservice;1] is undefined) b2g-debug(Components.classes[@mozilla.org/widget/dragservice;1] is undefined) b2g-desktop(Components.classes[@mozilla.org/widget/dragservice;1] is undefined)
|
||||
|
@ -2847,6 +2847,8 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
mOuter->PresContext()->IsRootContentDocument();
|
||||
|
||||
if (aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping()) {
|
||||
// Root scrollframes have FrameMetrics and clipping on their container
|
||||
// layers, so don't apply clipping again.
|
||||
mAddClipRectToLayer = false;
|
||||
|
||||
// If we are a root scroll frame that has a display port we want to add
|
||||
@ -2877,6 +2879,8 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
return;
|
||||
}
|
||||
|
||||
// Root scrollframes have FrameMetrics and clipping on their container
|
||||
// layers, so don't apply clipping again.
|
||||
mAddClipRectToLayer =
|
||||
!(mIsRoot && mOuter->PresContext()->PresShell()->GetIsViewportOverridden());
|
||||
|
||||
@ -2895,7 +2899,23 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
if (aBuilder->IsPaintingToWindow()) {
|
||||
bool wasUsingDisplayPort = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), nullptr);
|
||||
|
||||
if (!mIsRoot) {
|
||||
if (mIsRoot) {
|
||||
if (gfxPrefs::LayoutUseContainersForRootFrames()) {
|
||||
// For a root frame in a container, just get the value of the existing
|
||||
// display port if any.
|
||||
usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
|
||||
} else {
|
||||
// Override the value of the display port base rect, and possibly create a
|
||||
// display port if there isn't one already.
|
||||
nsRect displayportBase = dirtyRect;
|
||||
if (mIsRoot && mOuter->PresContext()->IsRootContentDocument()) {
|
||||
displayportBase =
|
||||
nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(mOuter));
|
||||
}
|
||||
usingDisplayport = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
|
||||
*aBuilder, mOuter, displayportBase, &displayPort);
|
||||
}
|
||||
} else {
|
||||
// For a non-root scroll frame, override the value of the display port
|
||||
// base rect, and possibly create a display port if there isn't one
|
||||
// already. For root scroll frame, nsLayoutUtils::PaintFrame or
|
||||
@ -2903,10 +2923,6 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
nsRect displayportBase = dirtyRect;
|
||||
usingDisplayport = nsLayoutUtils::GetOrMaybeCreateDisplayPort(
|
||||
*aBuilder, mOuter, displayportBase, &displayPort);
|
||||
} else {
|
||||
// For a root frame, just get the value of the existing display port, if
|
||||
// any.
|
||||
usingDisplayport = nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayPort);
|
||||
}
|
||||
|
||||
bool usingLowPrecision = gfxPrefs::UseLowPrecisionBuffer();
|
||||
@ -2968,10 +2984,12 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
shouldBuildLayer =
|
||||
nsLayoutUtils::WantSubAPZC() &&
|
||||
WantAsyncScroll() &&
|
||||
// If we are the root scroll frame for the display root then we don't need a scroll
|
||||
// info layer to make a ComputeFrameMetrics call for us as
|
||||
// nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us.
|
||||
(!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
|
||||
// If we are using containers for root frames, and we are the root
|
||||
// scroll frame for the display root, then we don't need a scroll
|
||||
// info layer. nsDisplayList::PaintForFrame already calls
|
||||
// ComputeFrameMetrics for us.
|
||||
(!(gfxPrefs::LayoutUseContainersForRootFrames() && mIsRoot) ||
|
||||
(aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext()));
|
||||
}
|
||||
|
||||
if (aBuilder->IsPaintingToWindow() &&
|
||||
@ -3119,8 +3137,6 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer,
|
||||
{
|
||||
nsRect scrollport = mScrollPort +
|
||||
mOuter->GetOffsetToCrossDoc(aContainerReferenceFrame);
|
||||
// Root scrollframes have FrameMetrics and clipping on their container layers,
|
||||
// so don't also apply clipping here.
|
||||
if (mAddClipRectToLayer) {
|
||||
*aClipRect = scrollport;
|
||||
}
|
||||
@ -3131,10 +3147,12 @@ ScrollFrameHelper::ComputeFrameMetrics(Layer* aLayer,
|
||||
|
||||
MOZ_ASSERT(mScrolledFrame->GetContent());
|
||||
|
||||
bool isRoot = mIsRoot && mOuter->PresContext()->IsRootContentDocument();
|
||||
|
||||
*aOutput->AppendElement() =
|
||||
nsDisplayScrollLayer::ComputeFrameMetrics(mScrolledFrame, mOuter,
|
||||
aContainerReferenceFrame, aLayer, mScrollParentID,
|
||||
scrollport, false, false, aParameters);
|
||||
scrollport, false, isRoot, aParameters);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -450,6 +450,7 @@ public:
|
||||
// If true, the layer should always be active because we always build a
|
||||
// scrollable layer. Used for asynchronous scrolling.
|
||||
bool mShouldBuildScrollableLayer:1;
|
||||
|
||||
// If true, add clipping in ScrollFrameHelper::ComputeFrameMetrics.
|
||||
bool mAddClipRectToLayer:1;
|
||||
|
||||
|
@ -413,16 +413,18 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
dirty = dirty.ConvertAppUnitsRoundOut(parentAPD, subdocAPD);
|
||||
|
||||
if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
|
||||
// for root content documents we want the base to be the composition bounds
|
||||
nsRect displayportBase = presContext->IsRootContentDocument() ?
|
||||
nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
|
||||
dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
|
||||
nsRect displayPort;
|
||||
if (aBuilder->IsPaintingToWindow() &&
|
||||
nsLayoutUtils::GetOrMaybeCreateDisplayPort(
|
||||
*aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
|
||||
haveDisplayPort = true;
|
||||
dirty = displayPort;
|
||||
if (gfxPrefs::LayoutUseContainersForRootFrames()) {
|
||||
// for root content documents we want the base to be the composition bounds
|
||||
nsRect displayportBase = presContext->IsRootContentDocument() ?
|
||||
nsRect(nsPoint(0,0), nsLayoutUtils::CalculateCompositionSizeForFrame(rootScrollFrame)) :
|
||||
dirty.Intersect(nsRect(nsPoint(0,0), subdocRootFrame->GetSize()));
|
||||
nsRect displayPort;
|
||||
if (aBuilder->IsPaintingToWindow() &&
|
||||
nsLayoutUtils::GetOrMaybeCreateDisplayPort(
|
||||
*aBuilder, rootScrollFrame, displayportBase, &displayPort)) {
|
||||
haveDisplayPort = true;
|
||||
dirty = displayPort;
|
||||
}
|
||||
}
|
||||
|
||||
ignoreViewportScrolling = presShell->IgnoringViewportScrolling();
|
||||
@ -453,9 +455,15 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
||||
bool constructResolutionItem = subdocRootFrame &&
|
||||
(presShell->GetXResolution() != 1.0 || presShell->GetYResolution() != 1.0);
|
||||
bool constructZoomItem = subdocRootFrame && parentAPD != subdocAPD;
|
||||
bool needsOwnLayer = constructResolutionItem || constructZoomItem ||
|
||||
haveDisplayPort ||
|
||||
presContext->IsRootContentDocument() || (sf && sf->IsScrollingActive(aBuilder));
|
||||
bool needsOwnLayer = false;
|
||||
if (constructResolutionItem ||
|
||||
constructZoomItem ||
|
||||
haveDisplayPort ||
|
||||
presContext->IsRootContentDocument() ||
|
||||
(sf && sf->IsScrollingActive(aBuilder)))
|
||||
{
|
||||
needsOwnLayer = true;
|
||||
}
|
||||
|
||||
nsDisplayList childItems;
|
||||
|
||||
|
@ -234,6 +234,9 @@ struct AnimationPlayerCollection : public PRCList
|
||||
|
||||
void Destroy()
|
||||
{
|
||||
for (size_t playerIdx = mPlayers.Length(); playerIdx-- != 0; ) {
|
||||
mPlayers[playerIdx]->Cancel();
|
||||
}
|
||||
// This will call our destructor.
|
||||
mElement->DeleteProperty(mElementProperty);
|
||||
}
|
||||
|
@ -26,6 +26,13 @@ using mozilla::dom::Animation;
|
||||
using mozilla::dom::AnimationPlayer;
|
||||
using mozilla::CSSAnimationPlayer;
|
||||
|
||||
mozilla::dom::Promise*
|
||||
CSSAnimationPlayer::GetReady(ErrorResult& aRv)
|
||||
{
|
||||
FlushStyle();
|
||||
return AnimationPlayer::GetReady(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
CSSAnimationPlayer::Play()
|
||||
{
|
||||
@ -324,6 +331,7 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
|
||||
// Although we're doing this while iterating this is safe because
|
||||
// we're not changing the length of newPlayers and we've finished
|
||||
// iterating over the list of old iterations.
|
||||
newPlayer->Cancel();
|
||||
newPlayer = nullptr;
|
||||
newPlayers.ReplaceElementAt(newIdx, oldPlayer);
|
||||
collection->mPlayers.RemoveElementAt(oldIdx);
|
||||
@ -337,6 +345,11 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
|
||||
collection->mNeedsRefreshes = true;
|
||||
collection->Tick();
|
||||
|
||||
// Cancel removed animations
|
||||
for (size_t newPlayerIdx = newPlayers.Length(); newPlayerIdx-- != 0; ) {
|
||||
newPlayers[newPlayerIdx]->Cancel();
|
||||
}
|
||||
|
||||
TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
|
||||
UpdateStyleAndEvents(collection, refreshTime,
|
||||
EnsureStyleRule_IsNotThrottled);
|
||||
|
@ -20,6 +20,9 @@ namespace mozilla {
|
||||
namespace css {
|
||||
class Declaration;
|
||||
} /* namespace css */
|
||||
namespace dom {
|
||||
class Promise;
|
||||
} /* namespace dom */
|
||||
|
||||
struct AnimationEventInfo {
|
||||
nsRefPtr<mozilla::dom::Element> mElement;
|
||||
@ -63,6 +66,7 @@ public:
|
||||
virtual CSSAnimationPlayer*
|
||||
AsCSSAnimationPlayer() MOZ_OVERRIDE { return this; }
|
||||
|
||||
virtual dom::Promise* GetReady(ErrorResult& aRv) MOZ_OVERRIDE;
|
||||
virtual void Play() MOZ_OVERRIDE;
|
||||
virtual void Pause() MOZ_OVERRIDE;
|
||||
|
||||
|
@ -300,6 +300,7 @@ nsTransitionManager::StyleContextChanged(dom::Element *aElement,
|
||||
currentValue) ||
|
||||
currentValue != segment.mToValue) {
|
||||
// stop the transition
|
||||
player->Cancel();
|
||||
players.RemoveElementAt(i);
|
||||
collection->UpdateAnimationGeneration(mPresContext);
|
||||
}
|
||||
|
@ -4416,3 +4416,20 @@ pref("intl.collation.mac.use_icu", true);
|
||||
|
||||
// Enable meta-viewport support in remote APZ-enabled frames.
|
||||
pref("dom.meta-viewport.enabled", false);
|
||||
|
||||
// MozSettings debugging prefs for each component
|
||||
pref("dom.mozSettings.SettingsDB.debug.enabled", false);
|
||||
pref("dom.mozSettings.SettingsManager.debug.enabled", false);
|
||||
pref("dom.mozSettings.SettingsRequestManager.debug.enabled", false);
|
||||
pref("dom.mozSettings.SettingsService.debug.enabled", false);
|
||||
|
||||
// MozSettings verbose mode to track everything
|
||||
pref("dom.mozSettings.SettingsDB.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsManager.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsRequestManager.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsService.verbose.enabled", false);
|
||||
|
||||
// Controlling whether we want to allow forcing some Settings
|
||||
// IndexedDB transactions to be opened as readonly or keep everything as
|
||||
// readwrite.
|
||||
pref("dom.mozSettings.allowForceReadOnly", false);
|
||||
|
@ -85,3 +85,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
|
||||
DEFINES['IMPL_MFBT'] = True
|
||||
|
||||
LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS']
|
||||
|
||||
if not CONFIG['_MSC_VER']:
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -33,3 +33,5 @@ TEST_DIRS += ['tests']
|
||||
HOST_OS_LIBS += [
|
||||
'z',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -5761,7 +5761,10 @@ nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
|
||||
}
|
||||
|
||||
if (progress > 0) {
|
||||
MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
|
||||
if (progress > progressMax) {
|
||||
NS_WARNING("unexpected progress values");
|
||||
}
|
||||
|
||||
// Try to get mProgressSink if it was nulled out during OnStatus.
|
||||
if (!mProgressSink) {
|
||||
GetCallback(mProgressSink);
|
||||
|
@ -36,3 +36,5 @@ SOURCES += [
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
@ -10,319 +10,4 @@ executable *driver* script (named whatever you want), and write mach
|
||||
commands. When the *driver* is executed, mach dispatches to the
|
||||
requested command handler automatically.
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
On a high level, mach is similar to using argparse with subparsers (for
|
||||
command handling). When you dig deeper, mach offers a number of
|
||||
additional features:
|
||||
|
||||
Distributed command definitions
|
||||
With optparse/argparse, you have to define your commands on a central
|
||||
parser instance. With mach, you annotate your command methods with
|
||||
decorators and mach finds and dispatches to them automatically.
|
||||
|
||||
Command categories
|
||||
Mach commands can be grouped into categories when displayed in help.
|
||||
This is currently not possible with argparse.
|
||||
|
||||
Logging management
|
||||
Mach provides a facility for logging (both classical text and
|
||||
structured) that is available to any command handler.
|
||||
|
||||
Settings files
|
||||
Mach provides a facility for reading settings from an ini-like file
|
||||
format.
|
||||
|
||||
Components
|
||||
==========
|
||||
|
||||
Mach is conceptually composed of the following components:
|
||||
|
||||
core
|
||||
The mach core is the core code powering mach. This is a Python package
|
||||
that contains all the business logic that makes mach work. The mach
|
||||
core is common to all mach deployments.
|
||||
|
||||
commands
|
||||
These are what mach dispatches to. Commands are simply Python methods
|
||||
registered as command names. The set of commands is unique to the
|
||||
environment mach is deployed in.
|
||||
|
||||
driver
|
||||
The *driver* is the entry-point to mach. It is simply an executable
|
||||
script that loads the mach core, tells it where commands can be found,
|
||||
then asks the mach core to handle the current request. The driver is
|
||||
unique to the deployed environment. But, it's usually based on an
|
||||
example from this source tree.
|
||||
|
||||
Project State
|
||||
=============
|
||||
|
||||
mach was originally written as a command dispatching framework to aid
|
||||
Firefox development. While the code is mostly generic, there are still
|
||||
some pieces that closely tie it to Mozilla/Firefox. The goal is for
|
||||
these to eventually be removed and replaced with generic features so
|
||||
mach is suitable for anybody to use. Until then, mach may not be the
|
||||
best fit for you.
|
||||
|
||||
Implementing Commands
|
||||
---------------------
|
||||
|
||||
Mach commands are defined via Python decorators.
|
||||
|
||||
All the relevant decorators are defined in the *mach.decorators* module.
|
||||
The important decorators are as follows:
|
||||
|
||||
CommandProvider
|
||||
A class decorator that denotes that a class contains mach
|
||||
commands. The decorator takes no arguments.
|
||||
|
||||
Command
|
||||
A method decorator that denotes that the method should be called when
|
||||
the specified command is requested. The decorator takes a command name
|
||||
as its first argument and a number of additional arguments to
|
||||
configure the behavior of the command.
|
||||
|
||||
CommandArgument
|
||||
A method decorator that defines an argument to the command. Its
|
||||
arguments are essentially proxied to ArgumentParser.add_argument()
|
||||
|
||||
Classes with the *@CommandProvider* decorator *must* have an *__init__*
|
||||
method that accepts 1 or 2 arguments. If it accepts 2 arguments, the
|
||||
2nd argument will be a *MachCommandContext* instance. This is just a named
|
||||
tuple containing references to objects provided by the mach driver.
|
||||
|
||||
Here is a complete example::
|
||||
|
||||
from mach.decorators import (
|
||||
CommandArgument,
|
||||
CommandProvider,
|
||||
Command,
|
||||
)
|
||||
|
||||
@CommandProvider
|
||||
class MyClass(object):
|
||||
@Command('doit', help='Do ALL OF THE THINGS.')
|
||||
@CommandArgument('--force', '-f', action='store_true',
|
||||
help='Force doing it.')
|
||||
def doit(self, force=False):
|
||||
# Do stuff here.
|
||||
|
||||
When the module is loaded, the decorators tell mach about all handlers.
|
||||
When mach runs, it takes the assembled metadata from these handlers and
|
||||
hooks it up to the command line driver. Under the hood, arguments passed
|
||||
to the decorators are being used to help mach parse command arguments,
|
||||
formulate arguments to the methods, etc. See the documentation in the
|
||||
*mach.base* module for more.
|
||||
|
||||
The Python modules defining mach commands do not need to live inside the
|
||||
main mach source tree.
|
||||
|
||||
Conditionally Filtering Commands
|
||||
--------------------------------
|
||||
|
||||
Sometimes it might only make sense to run a command given a certain
|
||||
context. For example, running tests only makes sense if the product
|
||||
they are testing has been built, and said build is available. To make
|
||||
sure a command is only runnable from within a correct context, you can
|
||||
define a series of conditions on the *Command* decorator.
|
||||
|
||||
A condition is simply a function that takes an instance of the
|
||||
*CommandProvider* class as an argument, and returns True or False. If
|
||||
any of the conditions defined on a command return False, the command
|
||||
will not be runnable. The doc string of a condition function is used in
|
||||
error messages, to explain why the command cannot currently be run.
|
||||
|
||||
Here is an example:
|
||||
|
||||
from mach.decorators import (
|
||||
CommandProvider,
|
||||
Command,
|
||||
)
|
||||
|
||||
def build_available(cls):
|
||||
"""The build needs to be available."""
|
||||
return cls.build_path is not None
|
||||
|
||||
@CommandProvider
|
||||
class MyClass(MachCommandBase):
|
||||
def __init__(self, build_path=None):
|
||||
self.build_path = build_path
|
||||
|
||||
@Command('run_tests', conditions=[build_available])
|
||||
def run_tests(self):
|
||||
# Do stuff here.
|
||||
|
||||
It is important to make sure that any state needed by the condition is
|
||||
available to instances of the command provider.
|
||||
|
||||
By default all commands without any conditions applied will be runnable,
|
||||
but it is possible to change this behaviour by setting *require_conditions*
|
||||
to True:
|
||||
|
||||
m = mach.main.Mach()
|
||||
m.require_conditions = True
|
||||
|
||||
Minimizing Code in Commands
|
||||
---------------------------
|
||||
|
||||
Mach command modules, classes, and methods work best when they are
|
||||
minimal dispatchers. The reason is import bloat. Currently, the mach
|
||||
core needs to import every Python file potentially containing mach
|
||||
commands for every command invocation. If you have dozens of commands or
|
||||
commands in modules that import a lot of Python code, these imports
|
||||
could slow mach down and waste memory.
|
||||
|
||||
It is thus recommended that mach modules, classes, and methods do as
|
||||
little work as possible. Ideally the module should only import from
|
||||
the *mach* package. If you need external modules, you should import them
|
||||
from within the command method.
|
||||
|
||||
To keep code size small, the body of a command method should be limited
|
||||
to:
|
||||
|
||||
1. Obtaining user input (parsing arguments, prompting, etc)
|
||||
2. Calling into some other Python package
|
||||
3. Formatting output
|
||||
|
||||
Of course, these recommendations can be ignored if you want to risk
|
||||
slower performance.
|
||||
|
||||
In the future, the mach driver may cache the dispatching information or
|
||||
have it intelligently loaded to facilitate lazy loading.
|
||||
|
||||
Logging
|
||||
=======
|
||||
|
||||
Mach configures a built-in logging facility so commands can easily log
|
||||
data.
|
||||
|
||||
What sets the logging facility apart from most loggers you've seen is
|
||||
that it encourages structured logging. Instead of conventional logging
|
||||
where simple strings are logged, the internal logging mechanism logs all
|
||||
events with the following pieces of information:
|
||||
|
||||
* A string *action*
|
||||
* A dict of log message fields
|
||||
* A formatting string
|
||||
|
||||
Essentially, instead of assembling a human-readable string at
|
||||
logging-time, you create an object holding all the pieces of data that
|
||||
will constitute your logged event. For each unique type of logged event,
|
||||
you assign an *action* name.
|
||||
|
||||
Depending on how logging is configured, your logged event could get
|
||||
written a couple of different ways.
|
||||
|
||||
JSON Logging
|
||||
------------
|
||||
|
||||
Where machines are the intended target of the logging data, a JSON
|
||||
logger is configured. The JSON logger assembles an array consisting of
|
||||
the following elements:
|
||||
|
||||
* Decimal wall clock time in seconds since UNIX epoch
|
||||
* String *action* of message
|
||||
* Object with structured message data
|
||||
|
||||
The JSON-serialized array is written to a configured file handle.
|
||||
Consumers of this logging stream can just perform a readline() then feed
|
||||
that into a JSON deserializer to reconstruct the original logged
|
||||
message. They can key off the *action* element to determine how to
|
||||
process individual events. There is no need to invent a parser.
|
||||
Convenient, isn't it?
|
||||
|
||||
Logging for Humans
|
||||
------------------
|
||||
|
||||
Where humans are the intended consumer of a log message, the structured
|
||||
log message are converted to more human-friendly form. This is done by
|
||||
utilizing the *formatting* string provided at log time. The logger
|
||||
simply calls the *format* method of the formatting string, passing the
|
||||
dict containing the message's fields.
|
||||
|
||||
When *mach* is used in a terminal that supports it, the logging facility
|
||||
also supports terminal features such as colorization. This is done
|
||||
automatically in the logging layer - there is no need to control this at
|
||||
logging time.
|
||||
|
||||
In addition, messages intended for humans typically prepends every line
|
||||
with the time passed since the application started.
|
||||
|
||||
Logging HOWTO
|
||||
-------------
|
||||
|
||||
Structured logging piggybacks on top of Python's built-in logging
|
||||
infrastructure provided by the *logging* package. We accomplish this by
|
||||
taking advantage of *logging.Logger.log()*'s *extra* argument. To this
|
||||
argument, we pass a dict with the fields *action* and *params*. These
|
||||
are the string *action* and dict of message fields, respectively. The
|
||||
formatting string is passed as the *msg* argument, like normal.
|
||||
|
||||
If you were logging to a logger directly, you would do something like:
|
||||
|
||||
logger.log(logging.INFO, 'My name is {name}',
|
||||
extra={'action': 'my_name', 'params': {'name': 'Gregory'}})
|
||||
|
||||
The JSON logging would produce something like:
|
||||
|
||||
[1339985554.306338, "my_name", {"name": "Gregory"}]
|
||||
|
||||
Human logging would produce something like:
|
||||
|
||||
0.52 My name is Gregory
|
||||
|
||||
Since there is a lot of complexity using logger.log directly, it is
|
||||
recommended to go through a wrapping layer that hides part of the
|
||||
complexity for you. The easiest way to do this is by utilizing the
|
||||
LoggingMixin:
|
||||
|
||||
import logging
|
||||
from mach.mixin.logging import LoggingMixin
|
||||
|
||||
class MyClass(LoggingMixin):
|
||||
def foo(self):
|
||||
self.log(logging.INFO, 'foo_start', {'bar': True},
|
||||
'Foo performed. Bar: {bar}')
|
||||
|
||||
Entry Points
|
||||
============
|
||||
|
||||
It is possible to use setuptools' entry points to load commands
|
||||
directly from python packages. A mach entry point is a function which
|
||||
returns a list of files or directories containing mach command
|
||||
providers. e.g.::
|
||||
|
||||
def list_providers():
|
||||
providers = []
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
for p in os.listdir(here):
|
||||
if p.endswith('.py'):
|
||||
providers.append(os.path.join(here, p))
|
||||
return providers
|
||||
|
||||
See http://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins
|
||||
for more information on creating an entry point. To search for entry
|
||||
point plugins, you can call *load_commands_from_entry_point*. This
|
||||
takes a single parameter called *group*. This is the name of the entry
|
||||
point group to load and defaults to ``mach.providers``. e.g.::
|
||||
|
||||
mach.load_commands_from_entry_point("mach.external.providers")
|
||||
|
||||
Adding Global Arguments
|
||||
=======================
|
||||
|
||||
Arguments to mach commands are usually command-specific. However,
|
||||
mach ships with a handful of global arguments that apply to all
|
||||
commands.
|
||||
|
||||
It is possible to extend the list of global arguments. In your
|
||||
*mach driver*, simply call ``add_global_argument()`` on your
|
||||
``mach.main.Mach`` instance. e.g.::
|
||||
|
||||
mach = mach.main.Mach(os.getcwd())
|
||||
|
||||
# Will allow --example to be specified on every mach command.
|
||||
mach.add_global_argument('--example', action='store_true',
|
||||
help='Demonstrate an example global argument.')
|
||||
To learn more, read the docs in ``docs/``.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user