merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-12-18 13:50:27 +01:00
commit 4755deb0c5
140 changed files with 1700 additions and 800 deletions

View File

@ -51,3 +51,5 @@ if CONFIG['MOZ_ENABLE_DBUS']:
CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
include('/ipc/chromium/chromium-config.mozbuild')
FAIL_ON_WARNINGS = True

View File

@ -29,3 +29,5 @@ if CONFIG['ACCESSIBILITY']:
FINAL_LIBRARY = 'xul'
include('/ipc/chromium/chromium-config.mozbuild')
FAIL_ON_WARNINGS = True

View File

@ -22,3 +22,5 @@ LOCAL_INCLUDES += [
]
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

View File

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

View File

@ -59,3 +59,5 @@ LOCAL_INCLUDES += [
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

View File

@ -24,3 +24,5 @@ LOCAL_INCLUDES += [
DEFINES['NOMINMAX'] = True
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

View File

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

View File

@ -47,3 +47,6 @@ else:
]
FINAL_LIBRARY = 'xul'
if CONFIG['GNU_CXX']:
FAIL_ON_WARNINGS = True

View File

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

View File

@ -74,3 +74,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
OS_LIBS += [
'version',
]
FAIL_ON_WARNINGS = True

View File

@ -18,3 +18,5 @@ else:
]
DEFINES['B2G_NAME'] = '"%s-bin%s"' % (PROGRAM, CONFIG['BIN_SUFFIX'])
DEFINES['GAIA_PATH'] = '"gaia/profile"'
FAIL_ON_WARNINGS = True

View File

@ -72,3 +72,5 @@ if CONFIG['HAVE_CLOCK_MONOTONIC']:
OS_LIBS += CONFIG['REALTIME_LIBS']
JAR_MANIFESTS += ['jar.mn']
FAIL_ON_WARNINGS = True

View File

@ -17,3 +17,5 @@ FINAL_LIBRARY = 'browsercomps'
LOCAL_INCLUDES += [
'../build',
]
FAIL_ON_WARNINGS = True

View File

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

View File

@ -21,3 +21,5 @@ FINAL_LIBRARY = 'browsercomps'
LOCAL_INCLUDES += [
'../build'
]
FAIL_ON_WARNINGS = True

View File

@ -39,3 +39,5 @@ for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'):
LOCAL_INCLUDES += [
'../build',
]
FAIL_ON_WARNINGS = True

View File

@ -52,3 +52,5 @@ EXTRA_PP_JS_MODULES += [
]
FINAL_LIBRARY = 'browsercomps'
FAIL_ON_WARNINGS = True

View File

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

View File

@ -24,3 +24,5 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
LOCAL_INCLUDES += ['/uriloader/exthandler/mac']
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -39,3 +39,5 @@ LOCAL_INCLUDES += [
'/dom/base',
]
if not CONFIG['CLANG_CXX']:
FAIL_ON_WARNINGS = True

View File

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

View File

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

View File

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

View File

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

View File

@ -26,3 +26,5 @@ LOCAL_INCLUDES += [
]
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

View File

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

View File

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

View File

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

View File

@ -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()
{

View File

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

View File

@ -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()
{

View File

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

View File

@ -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()
{

View File

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

View File

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

View File

@ -162,6 +162,7 @@ private:
void ContinueShutdown();
MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
bool mDecoderPerSegment;
};
} // namespace mozilla

View File

@ -36,3 +36,5 @@ CXXFLAGS += [
]
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

View File

@ -105,3 +105,5 @@ CXXFLAGS += [
if CONFIG['ANDROID_VERSION'] > '15':
DEFINES['MOZ_OMX_WEBM_DECODER'] = True
FAIL_ON_WARNINGS = True

View File

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

View File

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

View File

@ -75,7 +75,7 @@ struct IPCByteRange
uint32_t length;
};
typedef std::vector<IPCByteRange> IPCByteRanges;
typedef nsTArray<IPCByteRange> IPCByteRanges;
typedef nsCString Buffer;

View File

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

View File

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

View File

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

View File

@ -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 ();
*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -985,6 +985,8 @@ class TypeNewScript
js_free(initializerList);
}
static inline void writeBarrierPre(TypeNewScript *newScript);
bool analyzed() const {
if (preliminaryObjects) {
MOZ_ASSERT(!templateObject());

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -300,6 +300,7 @@ nsTransitionManager::StyleContextChanged(dom::Element *aElement,
currentValue) ||
currentValue != segment.mToValue) {
// stop the transition
player->Cancel();
players.RemoveElementAt(i);
collection->UpdateAnimationGeneration(mPresContext);
}

View File

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

View File

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

View File

@ -33,3 +33,5 @@ TEST_DIRS += ['tests']
HOST_OS_LIBS += [
'z',
]
FAIL_ON_WARNINGS = True

View File

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

View File

@ -36,3 +36,5 @@ SOURCES += [
MSVC_ENABLE_PGO = True
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True

View File

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