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

This commit is contained in:
Gurzau Raul 2019-05-20 16:49:58 +03:00
Родитель 9398841338 ce435076fa
Коммит e0cadccc1d
112 изменённых файлов: 3118 добавлений и 525 удалений

Просмотреть файл

@ -161,3 +161,4 @@ c44fbdd5173548c9035256dda8fd3512f67118a8 FIREFOX_NIGHTLY_64_END
f4c23517cec8626038a915bfe3bc7c0e1f6af55d FIREFOX_BETA_67_BASE f4c23517cec8626038a915bfe3bc7c0e1f6af55d FIREFOX_BETA_67_BASE
9421b477d67cfc4c9e03350cd554a9e6acc7f435 FIREFOX_NIGHTLY_67_END 9421b477d67cfc4c9e03350cd554a9e6acc7f435 FIREFOX_NIGHTLY_67_END
390914f7108f4f6065834d8983af9ac855cbf2df FIREFOX_BETA_68_BASE 390914f7108f4f6065834d8983af9ac855cbf2df FIREFOX_BETA_68_BASE
97dae745c1b3ef2292127ba1c4e90b1345c8f576 FIREFOX_NIGHTLY_68_END

Просмотреть файл

@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please # changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more. # don't change CLOBBER for WebIDL changes any more.
Bug 1533481 - Update to ICU 64 requires clobber Merge day clobber

Просмотреть файл

@ -753,11 +753,6 @@ class ContentSessionStore {
this.restoreHistory(data); this.restoreHistory(data);
break; break;
case "SessionStore:restoreTabContent": case "SessionStore:restoreTabContent":
if (data.isRemotenessUpdate) {
let histogram = Services.telemetry.getKeyedHistogramById("FX_TAB_REMOTE_NAVIGATION_DELAY_MS");
histogram.add("SessionStore:restoreTabContent",
Services.telemetry.msSystemNow() - data.requestTime);
}
this.restoreTabContent(data); this.restoreTabContent(data);
break; break;
case "SessionStore:resetRestore": case "SessionStore:resetRestore":

Просмотреть файл

@ -4310,8 +4310,7 @@ var SessionStoreInternal = {
browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent", browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent",
{loadArguments, isRemotenessUpdate, {loadArguments, isRemotenessUpdate,
reason: aOptions.restoreContentReason || reason: aOptions.restoreContentReason ||
RESTORE_TAB_CONTENT_REASON.SET_STATE, RESTORE_TAB_CONTENT_REASON.SET_STATE});
requestTime: Services.telemetry.msSystemNow()});
// Focus the tab's content area. // Focus the tab's content area.
if (aTab.selected && !window.isBlankPageURL(uri)) { if (aTab.selected && !window.isBlankPageURL(uri)) {

Просмотреть файл

@ -1 +1 @@
68.0a1 69.0a1

Просмотреть файл

@ -1 +1 @@
68.0a1 69.0a1

Просмотреть файл

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files. # hardcoded milestones in the tree from these two files.
#-------------------------------------------------------- #--------------------------------------------------------
68.0a1 69.0a1

Просмотреть файл

@ -55,6 +55,7 @@ const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled";
const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled"; const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled";
const THREE_PANE_CHROME_ENABLED_PREF = "devtools.inspector.chrome.three-pane-enabled"; const THREE_PANE_CHROME_ENABLED_PREF = "devtools.inspector.chrome.three-pane-enabled";
const TELEMETRY_EYEDROPPER_OPENED = "devtools.toolbar.eyedropper.opened"; const TELEMETRY_EYEDROPPER_OPENED = "devtools.toolbar.eyedropper.opened";
const TELEMETRY_SCALAR_NODE_SELECTION_COUNT = "devtools.inspector.node_selection_count";
/** /**
* Represents an open instance of the Inspector for a tab. * Represents an open instance of the Inspector for a tab.
@ -1299,6 +1300,7 @@ Inspector.prototype = {
executeSoon(() => { executeSoon(() => {
try { try {
selfUpdate(this.selection.nodeFront); selfUpdate(this.selection.nodeFront);
this.telemetry.scalarAdd(TELEMETRY_SCALAR_NODE_SELECTION_COUNT, 1);
} catch (ex) { } catch (ex) {
console.error(ex); console.error(ex);
} }

Просмотреть файл

@ -9,9 +9,6 @@ const ChromeUtils = require("ChromeUtils");
const Services = require("Services"); const Services = require("Services");
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm"); const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
const { E10SUtils } = require("resource://gre/modules/E10SUtils.jsm"); const { E10SUtils } = require("resource://gre/modules/E10SUtils.jsm");
const Telemetry = require("devtools/client/shared/telemetry");
const telemetry = new Telemetry();
function readInputStreamToString(stream) { function readInputStreamToString(stream) {
return NetUtil.readInputStreamToString(stream, stream.available()); return NetUtil.readInputStreamToString(stream, stream.available());
@ -85,7 +82,6 @@ BrowserElementWebNavigation.prototype = {
triggeringPrincipal: E10SUtils.serializePrincipal( triggeringPrincipal: E10SUtils.serializePrincipal(
triggeringPrincipal || triggeringPrincipal ||
Services.scriptSecurityManager.createNullPrincipal({})), Services.scriptSecurityManager.createNullPrincipal({})),
requestTime: telemetry.msSystemNow(),
}); });
}, },

Просмотреть файл

@ -5,7 +5,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "Animation.h" #include "Animation.h"
#include "AnimationUtils.h" #include "AnimationUtils.h"
#include "mozAutoDocUpdate.h"
#include "mozilla/dom/AnimationBinding.h" #include "mozilla/dom/AnimationBinding.h"
#include "mozilla/dom/AnimationPlaybackEvent.h" #include "mozilla/dom/AnimationPlaybackEvent.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
@ -14,10 +16,13 @@
#include "mozilla/AnimationEventDispatcher.h" #include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/AnimationTarget.h" #include "mozilla/AnimationTarget.h"
#include "mozilla/AutoRestore.h" #include "mozilla/AutoRestore.h"
#include "mozilla/DeclarationBlock.h"
#include "mozilla/Maybe.h" // For Maybe #include "mozilla/Maybe.h" // For Maybe
#include "mozilla/TypeTraits.h" // For std::forward<> #include "mozilla/TypeTraits.h" // For std::forward<>
#include "nsAnimationManager.h" // For CSSAnimation #include "nsAnimationManager.h" // For CSSAnimation
#include "nsComputedDOMStyle.h"
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch #include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
#include "nsDOMCSSAttrDeclaration.h" // For nsDOMCSSAttributeDeclaration
#include "nsThreadUtils.h" // For nsRunnableMethod and nsRevocableEventPtr #include "nsThreadUtils.h" // For nsRunnableMethod and nsRevocableEventPtr
#include "nsTransitionManager.h" // For CSSTransition #include "nsTransitionManager.h" // For CSSTransition
#include "PendingAnimationTracker.h" // For PendingAnimationTracker #include "PendingAnimationTracker.h" // For PendingAnimationTracker
@ -168,6 +173,8 @@ void Animation::SetEffectNoUpdate(AnimationEffect* aEffect) {
ReschedulePendingTasks(); ReschedulePendingTasks();
} }
MaybeScheduleReplacementCheck();
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
} }
@ -594,6 +601,124 @@ void Animation::Reverse(ErrorResult& aRv) {
// it here. // it here.
} }
void Animation::Persist() {
if (mReplaceState == AnimationReplaceState::Persisted) {
return;
}
bool wasRemoved = mReplaceState == AnimationReplaceState::Removed;
mReplaceState = AnimationReplaceState::Persisted;
// If the animation is not (yet) removed, there should be no side effects of
// persisting it.
if (wasRemoved) {
UpdateEffect(PostRestyleMode::IfNeeded);
PostUpdate();
}
}
// https://drafts.csswg.org/web-animations/#dom-animation-commitstyles
void Animation::CommitStyles(ErrorResult& aRv) {
if (!mEffect) {
return;
}
// Take an owning reference to the keyframe effect. This will ensure that
// this Animation and the target element remain alive after flushing style.
RefPtr<KeyframeEffect> keyframeEffect = mEffect->AsKeyframeEffect();
if (!keyframeEffect) {
return;
}
Maybe<NonOwningAnimationTarget> target = keyframeEffect->GetTarget();
if (!target) {
return;
}
if (target->mPseudoType != PseudoStyleType::NotPseudo) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
}
// Check it is an element with a style attribute
nsCOMPtr<nsStyledElement> styledElement = do_QueryInterface(target->mElement);
if (!styledElement) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
}
// Flush style before checking if the target element is rendered since the
// result could depend on pending style changes.
if (Document* doc = target->mElement->GetComposedDoc()) {
doc->FlushPendingNotifications(FlushType::Style);
}
if (!target->mElement->IsRendered()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
nsPresContext* presContext =
nsContentUtils::GetContextForContent(target->mElement);
if (!presContext) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
// Get the computed animation values
UniquePtr<RawServoAnimationValueMap> animationValues =
Servo_AnimationValueMap_Create().Consume();
if (!presContext->EffectCompositor()->ComposeServoAnimationRuleForEffect(
*keyframeEffect, CascadeLevel(), animationValues.get())) {
NS_WARNING("Failed to compose animation style to commit");
return;
}
// Calling SetCSSDeclaration will trigger attribute setting code.
// Start the update now so that the old rule doesn't get used
// between when we mutate the declaration and when we set the new
// rule.
mozAutoDocUpdate autoUpdate(target->mElement->OwnerDoc(), true);
// Get the inline style to append to
RefPtr<DeclarationBlock> declarationBlock;
if (auto* existing = target->mElement->GetInlineStyleDeclaration()) {
declarationBlock = existing->EnsureMutable();
} else {
declarationBlock = new DeclarationBlock();
declarationBlock->SetDirty();
}
// Prepare the callback
MutationClosureData closureData;
closureData.mClosure = nsDOMCSSAttributeDeclaration::MutationClosureFunction;
closureData.mElement = target->mElement;
DeclarationBlockMutationClosure beforeChangeClosure = {
nsDOMCSSAttributeDeclaration::MutationClosureFunction,
&closureData,
};
// Set the animated styles
bool changed = false;
nsCSSPropertyIDSet properties = keyframeEffect->GetPropertySet();
for (nsCSSPropertyID property : properties) {
RefPtr<RawServoAnimationValue> computedValue =
Servo_AnimationValueMap_GetValue(animationValues.get(), property)
.Consume();
if (computedValue) {
changed |= Servo_DeclarationBlock_SetPropertyToAnimationValue(
declarationBlock->Raw(), computedValue, beforeChangeClosure);
}
}
if (!changed) {
return;
}
// Update inline style declaration
target->mElement->SetInlineStyleDeclaration(*declarationBlock, closureData);
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// //
// JS wrappers for Animation interface: // JS wrappers for Animation interface:
@ -650,7 +775,14 @@ void Animation::Tick() {
FinishPendingAt(mTimeline->GetCurrentTimeAsDuration().Value()); FinishPendingAt(mTimeline->GetCurrentTimeAsDuration().Value());
} }
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Sync);
// Check for changes to whether or not this animation is replaceable.
bool isReplaceable = IsReplaceable();
if (isReplaceable && !mWasReplaceableAtLastTick) {
ScheduleReplacementCheck();
}
mWasReplaceableAtLastTick = isReplaceable;
if (!mEffect) { if (!mEffect) {
return; return;
@ -837,7 +969,8 @@ bool Animation::ShouldBeSynchronizedWithMainThread(
void Animation::UpdateRelevance() { void Animation::UpdateRelevance() {
bool wasRelevant = mIsRelevant; bool wasRelevant = mIsRelevant;
mIsRelevant = HasCurrentEffect() || IsInEffect(); mIsRelevant = mReplaceState != AnimationReplaceState::Removed &&
(HasCurrentEffect() || IsInEffect());
// Notify animation observers. // Notify animation observers.
if (wasRelevant && !mIsRelevant) { if (wasRelevant && !mIsRelevant) {
@ -847,6 +980,118 @@ void Animation::UpdateRelevance() {
} }
} }
template <class T>
bool IsMarkupAnimation(T* aAnimation) {
return aAnimation && aAnimation->IsTiedToMarkup();
}
// https://drafts.csswg.org/web-animations/#replaceable-animation
bool Animation::IsReplaceable() const {
// We never replace CSS animations or CSS transitions since they are managed
// by CSS.
if (IsMarkupAnimation(AsCSSAnimation()) ||
IsMarkupAnimation(AsCSSTransition())) {
return false;
}
// Only finished animations can be replaced.
if (PlayState() != AnimationPlayState::Finished) {
return false;
}
// Already removed animations cannot be replaced.
if (ReplaceState() == AnimationReplaceState::Removed) {
return false;
}
// We can only replace an animation if we know that, uninterfered, it would
// never start playing again. That excludes any animations on timelines that
// aren't monotonically increasing.
//
// If we don't have any timeline at all, then we can't be in the finished
// state (since we need both a resolved start time and current time for that)
// and will have already returned false above.
//
// (However, if it ever does become possible to be finished without a timeline
// then we will want to return false here since it probably suggests an
// animation being driven directly by script, in which case we can't assume
// anything about how they will behave.)
if (!GetTimeline() || !GetTimeline()->TracksWallclockTime()) {
return false;
}
// If the animation doesn't have an effect then we can't determine if it is
// filling or not so just leave it alone.
if (!GetEffect()) {
return false;
}
// At the time of writing we only know about KeyframeEffects. If we introduce
// other types of effects we will need to decide if they are replaceable or
// not.
MOZ_ASSERT(GetEffect()->AsKeyframeEffect(),
"Effect should be a keyframe effect");
// We only replace animations that are filling.
if (GetEffect()->GetComputedTiming().mProgress.IsNull()) {
return false;
}
// We should only replace animations with a target element (since otherwise
// what other effects would we consider when determining if they are covered
// or not?).
if (!GetEffect()->AsKeyframeEffect()->GetTarget()) {
return false;
}
return true;
}
bool Animation::IsRemovable() const {
return ReplaceState() == AnimationReplaceState::Active && IsReplaceable();
}
void Animation::ScheduleReplacementCheck() {
MOZ_ASSERT(
IsReplaceable(),
"Should only schedule a replacement check for a replaceable animation");
// If IsReplaceable() is true, the following should also hold
MOZ_ASSERT(GetEffect());
MOZ_ASSERT(GetEffect()->AsKeyframeEffect());
MOZ_ASSERT(GetEffect()->AsKeyframeEffect()->GetTarget());
Maybe<NonOwningAnimationTarget> target =
GetEffect()->AsKeyframeEffect()->GetTarget();
nsPresContext* presContext =
nsContentUtils::GetContextForContent(target->mElement);
if (presContext) {
presContext->EffectCompositor()->NoteElementForReducing(*target);
}
}
void Animation::MaybeScheduleReplacementCheck() {
if (!IsReplaceable()) {
return;
}
ScheduleReplacementCheck();
}
void Animation::Remove() {
MOZ_ASSERT(IsRemovable(),
"Should not be trying to remove an effect that is not removable");
mReplaceState = AnimationReplaceState::Removed;
UpdateEffect(PostRestyleMode::IfNeeded);
PostUpdate();
QueuePlaybackEvent(NS_LITERAL_STRING("remove"),
GetTimelineCurrentTimeAsTimeStamp());
}
bool Animation::HasLowerCompositeOrderThan(const Animation& aOther) const { bool Animation::HasLowerCompositeOrderThan(const Animation& aOther) const {
// 0. Object-equality case // 0. Object-equality case
if (&aOther == this) { if (&aOther == this) {
@ -989,11 +1234,27 @@ void Animation::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
void Animation::NotifyEffectTimingUpdated() { void Animation::NotifyEffectTimingUpdated() {
MOZ_ASSERT(mEffect, MOZ_ASSERT(mEffect,
"We should only update timing effect when we have a target " "We should only update effect timing when we have a target "
"effect"); "effect");
UpdateTiming(Animation::SeekFlag::NoSeek, Animation::SyncNotifyFlag::Async); UpdateTiming(Animation::SeekFlag::NoSeek, Animation::SyncNotifyFlag::Async);
} }
void Animation::NotifyEffectPropertiesUpdated() {
MOZ_ASSERT(mEffect,
"We should only update effect properties when we have a target "
"effect");
MaybeScheduleReplacementCheck();
}
void Animation::NotifyEffectTargetUpdated() {
MOZ_ASSERT(mEffect,
"We should only update the effect target when we have a target "
"effect");
MaybeScheduleReplacementCheck();
}
void Animation::NotifyGeometricAnimationsStartingThisFrame() { void Animation::NotifyGeometricAnimationsStartingThisFrame() {
if (!IsNewlyStarted() || !mEffect) { if (!IsNewlyStarted() || !mEffect) {
return; return;
@ -1179,7 +1440,7 @@ void Animation::ResumeAt(const TimeDuration& aReadyTime) {
mPendingState = PendingState::NotPending; mPendingState = PendingState::NotPending;
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Sync);
// If we had a pending playback rate, we will have now applied it so we need // If we had a pending playback rate, we will have now applied it so we need
// to notify observers. // to notify observers.
@ -1503,7 +1764,7 @@ void Animation::QueuePlaybackEvent(const nsAString& aName,
AnimationPlaybackEventInit init; AnimationPlaybackEventInit init;
if (aName.EqualsLiteral("finish")) { if (aName.EqualsLiteral("finish") || aName.EqualsLiteral("remove")) {
init.mCurrentTime = GetCurrentTimeAsDouble(); init.mCurrentTime = GetCurrentTimeAsDouble();
} }
if (mTimeline) { if (mTimeline) {

Просмотреть файл

@ -51,15 +51,7 @@ class Animation : public DOMEventTargetHelper,
public: public:
explicit Animation(nsIGlobalObject* aGlobal) explicit Animation(nsIGlobalObject* aGlobal)
: DOMEventTargetHelper(aGlobal), : DOMEventTargetHelper(aGlobal), mAnimationIndex(sNextAnimationIndex++) {}
mPlaybackRate(1.0),
mAnimationIndex(sNextAnimationIndex++),
mCachedChildIndex(-1),
mPendingState(PendingState::NotPending),
mFinishedAtLastComposeStyle(false),
mIsRelevant(false),
mFinishedIsResolved(false),
mSyncWithGeometricAnimations(false) {}
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Animation, DOMEventTargetHelper) NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Animation, DOMEventTargetHelper)
@ -120,12 +112,14 @@ class Animation : public DOMEventTargetHelper,
bool Pending() const { return mPendingState != PendingState::NotPending; } bool Pending() const { return mPendingState != PendingState::NotPending; }
virtual bool PendingFromJS() const { return Pending(); } virtual bool PendingFromJS() const { return Pending(); }
AnimationReplaceState ReplaceState() const { return mReplaceState; }
virtual Promise* GetReady(ErrorResult& aRv); virtual Promise* GetReady(ErrorResult& aRv);
Promise* GetFinished(ErrorResult& aRv); Promise* GetFinished(ErrorResult& aRv);
IMPL_EVENT_HANDLER(finish); IMPL_EVENT_HANDLER(finish);
IMPL_EVENT_HANDLER(cancel); IMPL_EVENT_HANDLER(cancel);
IMPL_EVENT_HANDLER(remove);
void Cancel(PostRestyleMode aPostRestyle = PostRestyleMode::IfNeeded); void Cancel(PostRestyleMode aPostRestyle = PostRestyleMode::IfNeeded);
@ -147,6 +141,9 @@ class Animation : public DOMEventTargetHelper,
void UpdatePlaybackRate(double aPlaybackRate); void UpdatePlaybackRate(double aPlaybackRate);
void Reverse(ErrorResult& aRv); void Reverse(ErrorResult& aRv);
void Persist();
void CommitStyles(ErrorResult& aRv);
bool IsRunningOnCompositor() const; bool IsRunningOnCompositor() const;
virtual void Tick(); virtual void Tick();
@ -329,6 +326,25 @@ class Animation : public DOMEventTargetHelper,
bool IsRelevant() const { return mIsRelevant; } bool IsRelevant() const { return mIsRelevant; }
void UpdateRelevance(); void UpdateRelevance();
// https://drafts.csswg.org/web-animations-1/#replaceable-animation
bool IsReplaceable() const;
/**
* Returns true if this Animation satisfies the requirements for being
* removed when it is replaced.
*
* Returning true does not imply this animation _should_ be removed.
* Determining that depends on the other effects in the same EffectSet to
* which this animation's effect, if any, contributes.
*/
bool IsRemovable() const;
/**
* Make this animation's target effect no-longer part of the effect stack
* while preserving its timing information.
*/
void Remove();
/** /**
* Returns true if this Animation has a lower composite order than aOther. * Returns true if this Animation has a lower composite order than aOther.
*/ */
@ -366,6 +382,8 @@ class Animation : public DOMEventTargetHelper,
const nsCSSPropertyIDSet& aPropertiesToSkip); const nsCSSPropertyIDSet& aPropertiesToSkip);
void NotifyEffectTimingUpdated(); void NotifyEffectTimingUpdated();
void NotifyEffectPropertiesUpdated();
void NotifyEffectTargetUpdated();
void NotifyGeometricAnimationsStartingThisFrame(); void NotifyGeometricAnimationsStartingThisFrame();
/** /**
@ -481,6 +499,9 @@ class Animation : public DOMEventTargetHelper,
return GetCurrentTimeForHoldTime(Nullable<TimeDuration>()); return GetCurrentTimeForHoldTime(Nullable<TimeDuration>());
} }
void ScheduleReplacementCheck();
void MaybeScheduleReplacementCheck();
// Earlier side of the elapsed time range reported in CSS Animations and CSS // Earlier side of the elapsed time range reported in CSS Animations and CSS
// Transitions events. // Transitions events.
// //
@ -527,7 +548,7 @@ class Animation : public DOMEventTargetHelper,
Nullable<TimeDuration> mHoldTime; // Animation timescale Nullable<TimeDuration> mHoldTime; // Animation timescale
Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
Nullable<TimeDuration> mPreviousCurrentTime; // Animation timescale Nullable<TimeDuration> mPreviousCurrentTime; // Animation timescale
double mPlaybackRate; double mPlaybackRate = 1.0;
Maybe<double> mPendingPlaybackRate; Maybe<double> mPendingPlaybackRate;
// A Promise that is replaced on each call to Play() // A Promise that is replaced on each call to Play()
@ -554,7 +575,7 @@ class Animation : public DOMEventTargetHelper,
// While ordering Animation objects for event dispatch, the index of the // While ordering Animation objects for event dispatch, the index of the
// target node in its parent may be cached in mCachedChildIndex. // target node in its parent may be cached in mCachedChildIndex.
int32_t mCachedChildIndex; int32_t mCachedChildIndex = -1;
// Indicates if the animation is in the pending state (and what state it is // Indicates if the animation is in the pending state (and what state it is
// waiting to enter when it finished pending). We use this rather than // waiting to enter when it finished pending). We use this rather than
@ -563,23 +584,28 @@ class Animation : public DOMEventTargetHelper,
// from the PendingAnimationTracker while it is waiting for the next tick // from the PendingAnimationTracker while it is waiting for the next tick
// (see TriggerOnNextTick for details). // (see TriggerOnNextTick for details).
enum class PendingState : uint8_t { NotPending, PlayPending, PausePending }; enum class PendingState : uint8_t { NotPending, PlayPending, PausePending };
PendingState mPendingState; PendingState mPendingState = PendingState::NotPending;
// Handling of this animation's target effect when filling while finished.
AnimationReplaceState mReplaceState = AnimationReplaceState::Active;
bool mFinishedAtLastComposeStyle = false;
bool mWasReplaceableAtLastTick = false;
bool mFinishedAtLastComposeStyle;
// Indicates that the animation should be exposed in an element's // Indicates that the animation should be exposed in an element's
// getAnimations() list. // getAnimations() list.
bool mIsRelevant; bool mIsRelevant = false;
// True if mFinished is resolved or would be resolved if mFinished has // True if mFinished is resolved or would be resolved if mFinished has
// yet to be created. This is not set when mFinished is rejected since // yet to be created. This is not set when mFinished is rejected since
// in that case mFinished is immediately reset to represent a new current // in that case mFinished is immediately reset to represent a new current
// finished promise. // finished promise.
bool mFinishedIsResolved; bool mFinishedIsResolved = false;
// True if this animation was triggered at the same time as one or more // True if this animation was triggered at the same time as one or more
// geometric animations and hence we should run any transform animations on // geometric animations and hence we should run any transform animations on
// the main thread. // the main thread.
bool mSyncWithGeometricAnimations; bool mSyncWithGeometricAnimations = false;
RefPtr<MicroTaskRunnable> mFinishNotificationTask; RefPtr<MicroTaskRunnable> mFinishNotificationTask;

Просмотреть файл

@ -8,6 +8,8 @@
#define mozilla_AnimationTarget_h #define mozilla_AnimationTarget_h
#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF #include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF
#include "mozilla/HashFunctions.h" // For HashNumber, AddToHash
#include "mozilla/HashTable.h" // For DefaultHasher, PointerHasher
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "nsCSSPseudoElements.h" #include "nsCSSPseudoElements.h"
@ -69,6 +71,26 @@ inline void ImplCycleCollectionUnlink(Maybe<OwningAnimationTarget>& aTarget) {
} }
} }
// A DefaultHasher specialization for OwningAnimationTarget.
template <>
struct DefaultHasher<OwningAnimationTarget> {
using Key = OwningAnimationTarget;
using Lookup = OwningAnimationTarget;
using PtrHasher = PointerHasher<dom::Element*>;
static HashNumber hash(const Lookup& aLookup) {
return AddToHash(PtrHasher::hash(aLookup.mElement.get()),
static_cast<uint8_t>(aLookup.mPseudoType));
}
static bool match(const Key& aKey, const Lookup& aLookup) {
return PtrHasher::match(aKey.mElement.get(), aLookup.mElement.get()) &&
aKey.mPseudoType == aLookup.mPseudoType;
}
static void rekey(Key& aKey, Key&& aNewKey) { aKey = std::move(aNewKey); }
};
} // namespace mozilla } // namespace mozilla
#endif // mozilla_AnimationTarget_h #endif // mozilla_AnimationTarget_h

Просмотреть файл

@ -188,13 +188,6 @@ void DocumentTimeline::MostRecentRefreshTimeUpdated() {
} }
void DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime) { void DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime) {
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events,
// step2.
// Note that this should be done before nsAutoAnimationMutationBatch which is
// inside MostRecentRefreshTimeUpdated(). If PerformMicroTaskCheckpoint was
// called before nsAutoAnimationMutationBatch is destroyed, some mutation
// records might not be delivered in this checkpoint.
nsAutoMicroTask mt;
MostRecentRefreshTimeUpdated(); MostRecentRefreshTimeUpdated();
} }

Просмотреть файл

@ -394,6 +394,26 @@ class EffectCompositeOrderComparator {
}; };
} // namespace } // namespace
static void ComposeSortedEffects(
const nsTArray<KeyframeEffect*>& aSortedEffects,
const EffectSet* aEffectSet, EffectCompositor::CascadeLevel aCascadeLevel,
RawServoAnimationValueMap* aAnimationValues) {
// If multiple animations affect the same property, animations with higher
// composite order (priority) override or add to animations with lower
// priority.
nsCSSPropertyIDSet propertiesToSkip;
if (aEffectSet) {
propertiesToSkip =
aCascadeLevel == EffectCompositor::CascadeLevel::Animations
? aEffectSet->PropertiesForAnimationsLevel().Inverse()
: aEffectSet->PropertiesForAnimationsLevel();
}
for (KeyframeEffect* effect : aSortedEffects) {
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
}
}
bool EffectCompositor::GetServoAnimationRule( bool EffectCompositor::GetServoAnimationRule(
const dom::Element* aElement, PseudoStyleType aPseudoType, const dom::Element* aElement, PseudoStyleType aPseudoType,
CascadeLevel aCascadeLevel, RawServoAnimationValueMap* aAnimationValues) { CascadeLevel aCascadeLevel, RawServoAnimationValueMap* aAnimationValues) {
@ -417,16 +437,8 @@ bool EffectCompositor::GetServoAnimationRule(
} }
sortedEffectList.Sort(EffectCompositeOrderComparator()); sortedEffectList.Sort(EffectCompositeOrderComparator());
// If multiple animations affect the same property, animations with higher ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
// composite order (priority) override or add or animations with lower aAnimationValues);
// priority.
const nsCSSPropertyIDSet propertiesToSkip =
aCascadeLevel == CascadeLevel::Animations
? effectSet->PropertiesForAnimationsLevel().Inverse()
: effectSet->PropertiesForAnimationsLevel();
for (KeyframeEffect* effect : sortedEffectList) {
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
}
MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType), MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType),
"EffectSet should not change while composing style"); "EffectSet should not change while composing style");
@ -434,6 +446,59 @@ bool EffectCompositor::GetServoAnimationRule(
return true; return true;
} }
bool EffectCompositor::ComposeServoAnimationRuleForEffect(
KeyframeEffect& aEffect, CascadeLevel aCascadeLevel,
RawServoAnimationValueMap* aAnimationValues) {
MOZ_ASSERT(aAnimationValues);
MOZ_ASSERT(mPresContext && mPresContext->IsDynamic(),
"Should not be in print preview");
Maybe<NonOwningAnimationTarget> target = aEffect.GetTarget();
if (!target) {
return false;
}
// Don't try to compose animations for elements in documents without a pres
// shell (e.g. XMLHttpRequest documents).
if (!nsContentUtils::GetPresShellForContent(target->mElement)) {
return false;
}
// GetServoAnimationRule is called as part of the regular style resolution
// where the cascade results are updated in the pre-traversal as needed.
// This function, however, is only called when committing styles so we
// need to ensure the cascade results are up-to-date manually.
EffectCompositor::MaybeUpdateCascadeResults(target->mElement,
target->mPseudoType);
EffectSet* effectSet =
EffectSet::GetEffectSet(target->mElement, target->mPseudoType);
// Get a list of effects sorted by composite order up to and including
// |aEffect|, even if it is not in the EffectSet.
auto comparator = EffectCompositeOrderComparator();
nsTArray<KeyframeEffect*> sortedEffectList(effectSet ? effectSet->Count() + 1
: 1);
if (effectSet) {
for (KeyframeEffect* effect : *effectSet) {
if (comparator.LessThan(effect, &aEffect)) {
sortedEffectList.AppendElement(effect);
}
}
sortedEffectList.Sort(comparator);
}
sortedEffectList.AppendElement(&aEffect);
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
aAnimationValues);
MOZ_ASSERT(effectSet ==
EffectSet::GetEffectSet(target->mElement, target->mPseudoType),
"EffectSet should not change while composing style");
return true;
}
/* static */ dom::Element* EffectCompositor::GetElementToRestyle( /* static */ dom::Element* EffectCompositor::GetElementToRestyle(
dom::Element* aElement, PseudoStyleType aPseudoType) { dom::Element* aElement, PseudoStyleType aPseudoType) {
if (aPseudoType == PseudoStyleType::NotPseudo) { if (aPseudoType == PseudoStyleType::NotPseudo) {
@ -862,4 +927,53 @@ bool EffectCompositor::PreTraverseInSubtree(ServoTraversalFlags aFlags,
return foundElementsNeedingRestyle; return foundElementsNeedingRestyle;
} }
void EffectCompositor::NoteElementForReducing(
const NonOwningAnimationTarget& aTarget) {
if (!StaticPrefs::dom_animations_api_autoremove_enabled()) {
return;
}
Unused << mElementsToReduce.put(
OwningAnimationTarget{aTarget.mElement, aTarget.mPseudoType});
}
static void ReduceEffectSet(EffectSet& aEffectSet) {
// Get a list of effects sorted by composite order.
nsTArray<KeyframeEffect*> sortedEffectList(aEffectSet.Count());
for (KeyframeEffect* effect : aEffectSet) {
sortedEffectList.AppendElement(effect);
}
sortedEffectList.Sort(EffectCompositeOrderComparator());
nsCSSPropertyIDSet setProperties;
// Iterate in reverse
for (auto iter = sortedEffectList.rbegin(); iter != sortedEffectList.rend();
++iter) {
MOZ_ASSERT(*iter && (*iter)->GetAnimation(),
"Effect in an EffectSet should have an animation");
KeyframeEffect& effect = **iter;
Animation& animation = *effect.GetAnimation();
if (animation.IsRemovable() &&
effect.GetPropertySet().IsSubsetOf(setProperties)) {
animation.Remove();
} else if (animation.IsReplaceable()) {
setProperties |= effect.GetPropertySet();
}
}
}
void EffectCompositor::ReduceAnimations() {
for (auto iter = mElementsToReduce.iter(); !iter.done(); iter.next()) {
const OwningAnimationTarget& target = iter.get();
EffectSet* effectSet =
EffectSet::GetEffectSet(target.mElement, target.mPseudoType);
if (effectSet) {
ReduceEffectSet(*effectSet);
}
}
mElementsToReduce.clear();
}
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -8,7 +8,9 @@
#define mozilla_EffectCompositor_h #define mozilla_EffectCompositor_h
#include "mozilla/AnimationPerformanceWarning.h" #include "mozilla/AnimationPerformanceWarning.h"
#include "mozilla/AnimationTarget.h"
#include "mozilla/EnumeratedArray.h" #include "mozilla/EnumeratedArray.h"
#include "mozilla/HashTable.h"
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "mozilla/OwningNonNull.h" #include "mozilla/OwningNonNull.h"
#include "mozilla/PseudoElementHashEntry.h" #include "mozilla/PseudoElementHashEntry.h"
@ -38,6 +40,7 @@ struct NonOwningAnimationTarget;
namespace dom { namespace dom {
class Animation; class Animation;
class Element; class Element;
class KeyframeEffect;
} // namespace dom } // namespace dom
class EffectCompositor { class EffectCompositor {
@ -116,8 +119,9 @@ class EffectCompositor {
dom::Element* aElement, dom::Element* aElement,
PseudoStyleType aPseudoType); PseudoStyleType aPseudoType);
// Get animation rule for stylo. This is an equivalent of GetAnimationRule // Get the animation rule for the appropriate level of the cascade for
// and will be called from servo side. // a (pseudo-)element. Called from the Servo side.
//
// The animation rule is stored in |RawServoAnimationValueMap|. // The animation rule is stored in |RawServoAnimationValueMap|.
// We need to be careful while doing any modification because it may cause // We need to be careful while doing any modification because it may cause
// some thread-safe issues. // some thread-safe issues.
@ -126,6 +130,15 @@ class EffectCompositor {
CascadeLevel aCascadeLevel, CascadeLevel aCascadeLevel,
RawServoAnimationValueMap* aAnimationValues); RawServoAnimationValueMap* aAnimationValues);
// A variant on GetServoAnimationRule that composes all the effects for an
// element up to and including |aEffect|.
//
// Note that |aEffect| might not be in the EffectSet since we can use this for
// committing the computed style of a removed Animation.
bool ComposeServoAnimationRuleForEffect(
dom::KeyframeEffect& aEffect, CascadeLevel aCascadeLevel,
RawServoAnimationValueMap* aAnimationValues);
bool HasPendingStyleUpdates() const; bool HasPendingStyleUpdates() const;
static bool HasAnimationsForCompositor(const nsIFrame* aFrame, static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
@ -199,6 +212,12 @@ class EffectCompositor {
// at aElement. // at aElement.
bool PreTraverseInSubtree(ServoTraversalFlags aFlags, dom::Element* aRoot); bool PreTraverseInSubtree(ServoTraversalFlags aFlags, dom::Element* aRoot);
// Record a (pseudo-)element that may have animations that can be removed.
void NoteElementForReducing(const NonOwningAnimationTarget& aTarget);
bool NeedsReducing() const { return !mElementsToReduce.empty(); }
void ReduceAnimations();
// Returns the target element for restyling. // Returns the target element for restyling.
// //
// If |aPseudoType| is ::after, ::before or ::marker, returns the generated // If |aPseudoType| is ::after, ::before or ::marker, returns the generated
@ -239,6 +258,8 @@ class EffectCompositor {
mElementsToRestyle; mElementsToRestyle;
bool mIsInPreTraverse = false; bool mIsInPreTraverse = false;
HashSet<OwningAnimationTarget> mElementsToReduce;
}; };
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -317,7 +317,7 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
nsCSSPropertyIDSet properties; nsCSSPropertyIDSet properties;
if (!IsInEffect() && !IsCurrent()) { if (!mAnimation || !mAnimation->IsRelevant()) {
return properties; return properties;
} }
@ -342,14 +342,14 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
return properties; return properties;
} }
bool KeyframeEffect::HasAnimationOfPropertySet( nsCSSPropertyIDSet KeyframeEffect::GetPropertySet() const {
const nsCSSPropertyIDSet& aPropertySet) const { nsCSSPropertyIDSet result;
for (const AnimationProperty& property : mProperties) { for (const AnimationProperty& property : mProperties) {
if (aPropertySet.HasProperty(property.mProperty)) { result.AddProperty(property.mProperty);
return true;
} }
}
return false; return result;
} }
#ifdef DEBUG #ifdef DEBUG
@ -418,6 +418,10 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
MarkCascadeNeedsUpdate(); MarkCascadeNeedsUpdate();
if (mAnimation) {
mAnimation->NotifyEffectPropertiesUpdated();
}
RequestRestyle(EffectCompositor::RestyleType::Layer); RequestRestyle(EffectCompositor::RestyleType::Layer);
} }
@ -575,13 +579,10 @@ void KeyframeEffect::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
if (HasPropertiesThatMightAffectOverflow()) { if (HasPropertiesThatMightAffectOverflow()) {
nsPresContext* presContext = nsPresContext* presContext =
nsContentUtils::GetContextForContent(mTarget->mElement); nsContentUtils::GetContextForContent(mTarget->mElement);
if (presContext) {
TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
EffectSet* effectSet = EffectSet* effectSet =
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType); EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
MOZ_ASSERT(effectSet, if (presContext && effectSet) {
"ComposeStyle should only be called on an effect " TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
"that is part of an effect set");
effectSet->UpdateLastOverflowAnimationSyncTime(now); effectSet->UpdateLastOverflowAnimationSyncTime(now);
} }
} }
@ -792,7 +793,9 @@ void KeyframeEffect::UpdateTargetRegistration() {
// something calls Animation::UpdateRelevance. Whenever our timing changes, // something calls Animation::UpdateRelevance. Whenever our timing changes,
// we should be notifying our Animation before calling this, so // we should be notifying our Animation before calling this, so
// Animation::IsRelevant() should be up-to-date by the time we get here. // Animation::IsRelevant() should be up-to-date by the time we get here.
MOZ_ASSERT(isRelevant == IsCurrent() || IsInEffect(), MOZ_ASSERT(isRelevant ==
((IsCurrent() || IsInEffect()) && mAnimation &&
mAnimation->ReplaceState() != AnimationReplaceState::Removed),
"Out of date Animation::IsRelevant value"); "Out of date Animation::IsRelevant value");
if (isRelevant && !mInEffectSet) { if (isRelevant && !mInEffectSet) {
@ -999,6 +1002,10 @@ void KeyframeEffect::SetTarget(
mAnimation->ReschedulePendingTasks(); mAnimation->ReschedulePendingTasks();
} }
} }
if (mAnimation) {
mAnimation->NotifyEffectTargetUpdated();
}
} }
static void CreatePropertyValue( static void CreatePropertyValue(
@ -1728,6 +1735,11 @@ bool KeyframeEffect::ContainsAnimatedScale(const nsIFrame* aFrame) const {
return false; return false;
} }
if (!mAnimation ||
mAnimation->ReplaceState() == AnimationReplaceState::Removed) {
return false;
}
for (const AnimationProperty& prop : mProperties) { for (const AnimationProperty& prop : mProperties) {
if (prop.mProperty != eCSSProperty_transform && if (prop.mProperty != eCSSProperty_transform &&
prop.mProperty != eCSSProperty_scale && prop.mProperty != eCSSProperty_scale &&

Просмотреть файл

@ -183,9 +183,15 @@ class KeyframeEffect : public AnimationEffect {
void SetKeyframes(nsTArray<Keyframe>&& aKeyframes, void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
const ComputedStyle* aStyle); const ComputedStyle* aStyle);
// Returns the set of properties affected by this effect regardless of
// whether any of these properties is overridden by an !important rule.
nsCSSPropertyIDSet GetPropertySet() const;
// Returns true if the effect includes a property in |aPropertySet| regardless // Returns true if the effect includes a property in |aPropertySet| regardless
// of whether any property in the set is overridden by !important rule. // of whether any property in the set is overridden by an !important rule.
bool HasAnimationOfPropertySet(const nsCSSPropertyIDSet& aPropertySet) const; bool HasAnimationOfPropertySet(const nsCSSPropertyIDSet& aPropertySet) const {
return GetPropertySet().Intersects(aPropertySet);
}
// GetEffectiveAnimationOfProperty returns AnimationProperty corresponding // GetEffectiveAnimationOfProperty returns AnimationProperty corresponding
// to a given CSS property if the effect includes the property and the // to a given CSS property if the effect includes the property and the

Просмотреть файл

@ -1,5 +1,6 @@
[DEFAULT] [DEFAULT]
prefs = prefs =
dom.animations-api.autoremove.enabled=true
dom.animations-api.compositing.enabled=true dom.animations-api.compositing.enabled=true
gfx.omta.background-color=true gfx.omta.background-color=true
layout.css.individual-transform.enabled=true layout.css.individual-transform.enabled=true

Просмотреть файл

@ -578,5 +578,74 @@ promise_test(t => {
}); });
}, "tree_ordering: subtree"); }, "tree_ordering: subtree");
// Test that animations removed by auto-removal trigger an event
promise_test(async t => {
setupAsynchronousObserver(t, { observe: div, subtree: false });
// Start two animations such that one will be auto-removed
const animA = div.animate(
{ opacity: 1 },
{ duration: 100 * MS_PER_SEC, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: 1 },
{ duration: 100 * MS_PER_SEC, fill: 'forwards' }
);
// Wait for the MutationRecords corresponding to each addition.
await waitForNextFrame();
assert_records(
[
{ added: [animA], changed: [], removed: [] },
{ added: [animB], changed: [], removed: [] },
],
'records after animation start'
);
// Finish the animations -- this should cause animA to be replaced, and
// automatically removed.
animA.finish();
animB.finish();
// Wait for the MutationRecords corresponding to the timing changes and the
// subsequent removal to be delivered.
await waitForNextFrame();
assert_records(
[
{ added: [], changed: [animA], removed: [] },
{ added: [], changed: [animB], removed: [] },
{ added: [], changed: [], removed: [animA] },
],
'records after finishing'
);
// Restore animA.
animA.persist();
// Wait for the MutationRecord corresponding to the re-addition of animA.
await waitForNextFrame();
assert_records(
[{ added: [animA], changed: [], removed: [] }],
'records after persisting'
);
// Tidy up
animA.cancel();
animB.cancel();
await waitForNextFrame();
assert_records(
[
{ added: [], changed: [], removed: [animA] },
{ added: [], changed: [], removed: [animB] },
],
'records after tidying up end'
);
}, 'Animations automatically removed are reported');
runTest(); runTest();
</script> </script>

Просмотреть файл

@ -14,6 +14,7 @@ support-files =
chrome/file_animate_xrays.html chrome/file_animate_xrays.html
mozilla/xhr_doc.html mozilla/xhr_doc.html
mozilla/file_deferred_start.html mozilla/file_deferred_start.html
mozilla/file_disable_animations_api_autoremove.html
mozilla/file_disable_animations_api_compositing.html mozilla/file_disable_animations_api_compositing.html
mozilla/file_disable_animations_api_get_animations.html mozilla/file_disable_animations_api_get_animations.html
mozilla/file_disable_animations_api_implicit_keyframes.html mozilla/file_disable_animations_api_implicit_keyframes.html
@ -32,6 +33,7 @@ skip-if = (verify && !debug && (os == 'mac'))
[mozilla/test_cubic_bezier_limits.html] [mozilla/test_cubic_bezier_limits.html]
[mozilla/test_deferred_start.html] [mozilla/test_deferred_start.html]
skip-if = (toolkit == 'android' && debug) || (os == 'win' && bits == 64) # Bug 1363957 skip-if = (toolkit == 'android' && debug) || (os == 'win' && bits == 64) # Bug 1363957
[mozilla/test_disable_animations_api_autoremove.html]
[mozilla/test_disable_animations_api_compositing.html] [mozilla/test_disable_animations_api_compositing.html]
[mozilla/test_disable_animations_api_get_animations.html] [mozilla/test_disable_animations_api_get_animations.html]
[mozilla/test_disable_animations_api_implicit_keyframes.html] [mozilla/test_disable_animations_api_implicit_keyframes.html]

Просмотреть файл

@ -0,0 +1,69 @@
<!doctype html>
<meta charset=utf-8>
<script src="../testcommon.js"></script>
<body>
<script>
'use strict';
promise_test(async t => {
const div = addDiv(t);
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
// This should be assert_not_own_property but our local copy of testharness.js
// is old.
assert_equals(
animA.replaceState,
undefined,
'Should not have a replaceState member'
);
animA.addEventListener(
'remove',
t.step_func(() => {
assert_unreached('Should not fire a remove event');
})
);
// Allow a chance for the remove event to be fired
await animA.finished;
await waitForNextFrame();
}, 'Remove events should not be fired if the pref is not set');
promise_test(async t => {
const div = addDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: 0.3, composite: 'add' },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
assert_approx_equals(
parseFloat(getComputedStyle(div).opacity),
0.5,
0.0001,
'Covered animation should still contribute to effect stack when adding'
);
animB.cancel();
assert_approx_equals(
parseFloat(getComputedStyle(div).opacity),
0.2,
0.0001,
'Covered animation should still contribute to animated style when replacing'
);
}, 'Covered animations should still affect style if the pref is not set');
done();
</script>
</body>

Просмотреть файл

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

Просмотреть файл

@ -1407,6 +1407,14 @@ class Element : public FragmentOrElement {
return HasServoData() && Servo_Element_IsDisplayContents(this); return HasServoData() && Servo_Element_IsDisplayContents(this);
} }
/*
* https://html.spec.whatwg.org/#being-rendered
*
* With a gotcha for display contents:
* https://github.com/whatwg/html/issues/1837
*/
bool IsRendered() const { return GetPrimaryFrame() || IsDisplayContents(); }
const nsAttrValue* GetParsedAttr(const nsAtom* aAttr) const { const nsAttrValue* GetParsedAttr(const nsAtom* aAttr) const {
return mAttrs.GetAttr(aAttr); return mAttrs.GetAttr(aAttr);
} }

Просмотреть файл

@ -346,8 +346,8 @@ static nsresult PutToClipboard(
return rv; return rv;
} }
nsresult nsCopySupport::HTMLCopy(Selection* aSel, Document* aDoc, nsresult nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
int16_t aClipboardID, Selection* aSel, Document* aDoc, int16_t aClipboardID,
bool aWithRubyAnnotation) { bool aWithRubyAnnotation) {
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER); NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
@ -915,8 +915,8 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
// selection is inside a same ruby container. But we really should // selection is inside a same ruby container. But we really should
// expose the full functionality in browser. See bug 1130891. // expose the full functionality in browser. See bug 1130891.
bool withRubyAnnotation = IsSelectionInsideRuby(sel); bool withRubyAnnotation = IsSelectionInsideRuby(sel);
// call the copy code nsresult rv = EncodeDocumentWithContextAndPutToClipboard(
nsresult rv = HTMLCopy(sel, doc, aClipboardType, withRubyAnnotation); sel, doc, aClipboardType, withRubyAnnotation);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return false; return false;
} }

Просмотреть файл

@ -33,9 +33,9 @@ class nsCopySupport {
/** /**
* @param aDoc Needs to be not nullptr. * @param aDoc Needs to be not nullptr.
*/ */
static nsresult HTMLCopy(mozilla::dom::Selection* aSel, static nsresult EncodeDocumentWithContextAndPutToClipboard(
mozilla::dom::Document* aDoc, int16_t aClipboardID, mozilla::dom::Selection* aSel, mozilla::dom::Document* aDoc,
bool aWithRubyAnnotation); int16_t aClipboardID, bool aWithRubyAnnotation);
// Get the selection, or entire document, in the format specified by the mime // Get the selection, or entire document, in the format specified by the mime
// type (text/html or text/plain). If aSel is non-null, use it, otherwise get // type (text/html or text/plain). If aSel is non-null, use it, otherwise get
@ -47,8 +47,7 @@ class nsCopySupport {
static nsresult ImageCopy(nsIImageLoadingContent* aImageElement, static nsresult ImageCopy(nsIImageLoadingContent* aImageElement,
nsILoadContext* aLoadContext, int32_t aCopyFlags); nsILoadContext* aLoadContext, int32_t aCopyFlags);
// Get the selection as a transferable. Similar to HTMLCopy except does // Get the selection as a transferable.
// not deal with the clipboard.
// @param aSelection Can be nullptr. // @param aSelection Can be nullptr.
// @param aDocument Needs to be not nullptr. // @param aDocument Needs to be not nullptr.
// @param aTransferable Needs to be not nullptr. // @param aTransferable Needs to be not nullptr.

Просмотреть файл

@ -85,9 +85,7 @@ class nsDocumentEncoder : public nsIDocumentEncoder {
nsresult FlushText(nsAString& aString, bool aForce); nsresult FlushText(nsAString& aString, bool aForce);
bool IsVisibleNode(nsINode* aNode) { bool IsInvisibleNodeAndShouldBeSkipped(nsINode& aNode) const {
MOZ_ASSERT(aNode, "null node");
if (mFlags & SkipInvisibleContent) { if (mFlags & SkipInvisibleContent) {
// Treat the visibility of the ShadowRoot as if it were // Treat the visibility of the ShadowRoot as if it were
// the host content. // the host content.
@ -95,36 +93,39 @@ class nsDocumentEncoder : public nsIDocumentEncoder {
// FIXME(emilio): I suspect instead of this a bunch of the GetParent() // FIXME(emilio): I suspect instead of this a bunch of the GetParent()
// calls here should be doing GetFlattenedTreeParent, then this condition // calls here should be doing GetFlattenedTreeParent, then this condition
// should be unreachable... // should be unreachable...
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode)) { nsINode* node{&aNode};
aNode = shadowRoot->GetHost(); if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(node)) {
node = shadowRoot->GetHost();
} }
if (aNode->IsContent()) { if (node->IsContent()) {
nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame(); nsIFrame* frame = node->AsContent()->GetPrimaryFrame();
if (!frame) { if (!frame) {
if (aNode->IsElement() && aNode->AsElement()->IsDisplayContents()) { if (node->IsElement() && node->AsElement()->IsDisplayContents()) {
return true; return false;
} }
if (aNode->IsText()) { if (node->IsText()) {
// We have already checked that our parent is visible. // We have already checked that our parent is visible.
// //
// FIXME(emilio): Text not assigned to a <slot> in Shadow DOM should // FIXME(emilio): Text not assigned to a <slot> in Shadow DOM should
// probably return false... // probably return false...
return true;
}
if (aNode->IsHTMLElement(nsGkAtoms::rp)) {
// Ruby parentheses are part of ruby structure, hence
// shouldn't be stripped out even if it is not displayed.
return true;
}
return false; return false;
} }
bool isVisible = frame->StyleVisibility()->IsVisible(); if (node->IsHTMLElement(nsGkAtoms::rp)) {
if (!isVisible && aNode->IsText()) return false; // Ruby parentheses are part of ruby structure, hence
} // shouldn't be stripped out even if it is not displayed.
return false;
} }
return true; return true;
} }
bool isVisible = frame->StyleVisibility()->IsVisible();
if (!isVisible && node->IsText()) {
return true;
}
}
}
return false;
}
virtual bool IncludeInContext(nsINode* aNode); virtual bool IncludeInContext(nsINode* aNode);
@ -349,7 +350,7 @@ nsresult nsDocumentEncoder::SerializeNodeStart(nsINode& aOriginalNode,
} }
} }
if (!IsVisibleNode(&aOriginalNode)) { if (IsInvisibleNodeAndShouldBeSkipped(aOriginalNode)) {
return NS_OK; return NS_OK;
} }
@ -411,7 +412,7 @@ nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode& aNode, nsAString& aStr) {
} }
} }
if (!IsVisibleNode(&aNode)) { if (IsInvisibleNodeAndShouldBeSkipped(aNode)) {
return NS_OK; return NS_OK;
} }
@ -429,11 +430,12 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
return NS_OK; return NS_OK;
} }
if (!IsVisibleNode(aNode)) return NS_OK; NS_ENSURE_TRUE(aNode, NS_ERROR_NULL_POINTER);
nsresult rv = NS_OK; if (IsInvisibleNodeAndShouldBeSkipped(*aNode)) {
return NS_OK;
}
MOZ_ASSERT(aNode, "aNode shouldn't be nullptr.");
FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, nullptr, *aNode}; FixupNodeDeterminer fixupNodeDeterminer{mNodeFixup, nullptr, *aNode};
nsINode* maybeFixedNode = nsINode* maybeFixedNode =
&fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode(); &fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
@ -449,6 +451,8 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
} }
} }
nsresult rv = NS_OK;
if (!aDontSerializeRoot) { if (!aDontSerializeRoot) {
int32_t endOffset = -1; int32_t endOffset = -1;
if (aMaxLength > 0) { if (aMaxLength > 0) {
@ -585,7 +589,9 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode); nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
NS_ENSURE_TRUE(content, NS_ERROR_FAILURE); NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
if (!IsVisibleNode(aNode)) return NS_OK; if (IsInvisibleNodeAndShouldBeSkipped(*aNode)) {
return NS_OK;
}
nsresult rv = NS_OK; nsresult rv = NS_OK;
@ -788,11 +794,13 @@ nsresult nsDocumentEncoder::SerializeRangeToString(nsRange* aRange,
if (startContainer == endContainer && IsTextNode(startContainer)) { if (startContainer == endContainer && IsTextNode(startContainer)) {
if (mFlags & SkipInvisibleContent) { if (mFlags & SkipInvisibleContent) {
// Check that the parent is visible if we don't a frame. // Check that the parent is visible if we don't a frame.
// IsVisibleNode() will do it when there's a frame. // IsInvisibleNodeAndShouldBeSkipped() will do it when there's a frame.
nsCOMPtr<nsIContent> content = do_QueryInterface(startContainer); nsCOMPtr<nsIContent> content = do_QueryInterface(startContainer);
if (content && !content->GetPrimaryFrame()) { if (content && !content->GetPrimaryFrame()) {
nsIContent* parent = content->GetParent(); nsIContent* parent = content->GetParent();
if (!parent || !IsVisibleNode(parent)) return NS_OK; if (!parent || IsInvisibleNodeAndShouldBeSkipped(*parent)) {
return NS_OK;
}
} }
} }
rv = SerializeNodeStart(*startContainer, startOffset, endOffset, rv = SerializeNodeStart(*startContainer, startOffset, endOffset,
@ -847,9 +855,9 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
aOutputString.Truncate(); aOutputString.Truncate();
nsString output; nsString output;
static const size_t bufferSize = 2048; static const size_t kStringBufferSizeInBytes = 2048;
if (!mCachedBuffer) { if (!mCachedBuffer) {
mCachedBuffer = nsStringBuffer::Alloc(bufferSize).take(); mCachedBuffer = nsStringBuffer::Alloc(kStringBufferSizeInBytes).take();
if (NS_WARN_IF(!mCachedBuffer)) { if (NS_WARN_IF(!mCachedBuffer)) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
@ -975,7 +983,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
bool setOutput = false; bool setOutput = false;
// Try to cache the buffer. // Try to cache the buffer.
if (mCachedBuffer) { if (mCachedBuffer) {
if (mCachedBuffer->StorageSize() == bufferSize && if ((mCachedBuffer->StorageSize() == kStringBufferSizeInBytes) &&
!mCachedBuffer->IsReadonly()) { !mCachedBuffer->IsReadonly()) {
mCachedBuffer->AddRef(); mCachedBuffer->AddRef();
} else { } else {

Просмотреть файл

@ -466,7 +466,7 @@ class nsINode : public mozilla::dom::EventTarget {
MOZ_CAN_RUN_SCRIPT mozilla::dom::Element* GetParentFlexElement(); MOZ_CAN_RUN_SCRIPT mozilla::dom::Element* GetParentFlexElement();
/** /**
* Return whether the node is an Element node * Return whether the node is an Element node. Faster than using `NodeType()`.
*/ */
bool IsElement() const { return GetBoolFlag(NodeIsElement); } bool IsElement() const { return GetBoolFlag(NodeIsElement); }

Просмотреть файл

@ -35,6 +35,9 @@ interface JSWindowActorChild {
[Throws] [Throws]
readonly attribute BrowsingContext? browsingContext; readonly attribute BrowsingContext? browsingContext;
[Throws]
readonly attribute nsIDocShell? docShell;
// NOTE: As this returns a window proxy, it may not be currently referencing // NOTE: As this returns a window proxy, it may not be currently referencing
// the document associated with this JSWindowActor. Generally prefer using // the document associated with this JSWindowActor. Generally prefer using
// `document`. // `document`.

Просмотреть файл

@ -76,7 +76,7 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
[test_bug563329.html] [test_bug563329.html]
skip-if = true # Disabled due to timeouts. skip-if = true # Disabled due to timeouts.
[test_bug574663.html] [test_bug574663.html]
skip-if = (toolkit == 'android') || (os == 'win' && bits == 32 && !debug) || (os == 'linux' && !debug) #CRASH_DUMP, RANDOM, Bug 1523853 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
[test_bug591815.html] [test_bug591815.html]
[test_bug593959.html] [test_bug593959.html]
[test_bug603008.html] [test_bug603008.html]

Просмотреть файл

@ -63,7 +63,6 @@ function forceScrollAndWait(scrollbox, callback) {
} }
} }
var kExtraEvents = 5;
var kDelta = 3; var kDelta = 3;
function sendTouchpadScrollMotion(scrollbox, direction, ctrl, momentum, callback) { function sendTouchpadScrollMotion(scrollbox, direction, ctrl, momentum, callback) {
@ -91,11 +90,9 @@ function sendTouchpadScrollMotion(scrollbox, direction, ctrl, momentum, callback
scrollbox.addEventListener("wheel", onwheel); scrollbox.addEventListener("wheel", onwheel);
synthesizeWheel(scrollbox, 10, 10, event, win); synthesizeWheel(scrollbox, 10, 10, event, win);
// then 5 additional pixel scrolls // then additional pixel scroll
event.lineOrPageDeltaY = 0; event.lineOrPageDeltaY = 0;
for (let i = 1; i <= kExtraEvents; ++i) {
synthesizeWheel(scrollbox, 10, 10, event, win); synthesizeWheel(scrollbox, 10, 10, event, win);
}
} }
function runTest() { function runTest() {
@ -147,7 +144,7 @@ function runTest() {
let postfix = isMomentum ? ", even after releasing the touchpad" : ""; let postfix = isMomentum ? ", even after releasing the touchpad" : "";
// Normal scroll: scroll // Normal scroll: scroll
is(winUtils.fullZoom, zoomFactorBefore, "Normal scrolling shouldn't change zoom" + postfix); is(winUtils.fullZoom, zoomFactorBefore, "Normal scrolling shouldn't change zoom" + postfix);
is(scrollbox.scrollTop, scrollTopBefore + kDelta * (kExtraEvents + 1), is(scrollbox.scrollTop, scrollTopBefore + kDelta * 2,
"Normal scrolling should scroll" + postfix); "Normal scrolling should scroll" + postfix);
} else { } else {
if (!isMomentum) { if (!isMomentum) {
@ -155,7 +152,7 @@ function runTest() {
is(scrollbox.scrollTop, scrollTopBefore, "Ctrl-scrolling shouldn't scroll while the user is touching the touchpad"); is(scrollbox.scrollTop, scrollTopBefore, "Ctrl-scrolling shouldn't scroll while the user is touching the touchpad");
} else { } else {
is(winUtils.fullZoom, zoomFactorBefore, "Momentum scrolling shouldn't zoom, even when pressing Ctrl"); is(winUtils.fullZoom, zoomFactorBefore, "Momentum scrolling shouldn't zoom, even when pressing Ctrl");
is(scrollbox.scrollTop, scrollTopBefore + kDelta * (kExtraEvents + 1), is(scrollbox.scrollTop, scrollTopBefore + kDelta * 2,
"Momentum scrolling should scroll, even when pressing Ctrl"); "Momentum scrolling should scroll, even when pressing Ctrl");
} }
} }

Просмотреть файл

@ -2653,14 +2653,6 @@ nsresult nsGenericHTMLElement::NewURIFromString(const nsAString& aURISpec,
return NS_OK; return NS_OK;
} }
// https://html.spec.whatwg.org/#being-rendered
//
// With a gotcha for display contents:
// https://github.com/whatwg/html/issues/3947
static bool IsRendered(const Element& aElement) {
return aElement.GetPrimaryFrame() || aElement.IsDisplayContents();
}
void nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue, void nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
mozilla::ErrorResult& aError) { mozilla::ErrorResult& aError) {
// innerText depends on layout. For example, white space processing is // innerText depends on layout. For example, white space processing is
@ -2715,7 +2707,7 @@ void nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
doc->FlushPendingNotifications(FlushType::Layout); doc->FlushPendingNotifications(FlushType::Layout);
} }
if (!IsRendered(*this)) { if (!IsRendered()) {
GetTextContentInternal(aValue, aError); GetTextContentInternal(aValue, aError);
} else { } else {
nsRange::GetInnerTextNoFlush(aValue, aError, this); nsRange::GetInnerTextNoFlush(aValue, aError, this);

Просмотреть файл

@ -106,6 +106,14 @@ BrowsingContext* JSWindowActorChild::GetBrowsingContext(ErrorResult& aRv) {
return mManager->BrowsingContext(); return mManager->BrowsingContext();
} }
nsIDocShell* JSWindowActorChild::GetDocShell(ErrorResult& aRv) {
if (BrowsingContext* bc = GetBrowsingContext(aRv)) {
return bc->GetDocShell();
}
return nullptr;
}
Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow( Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow(
ErrorResult& aRv) { ErrorResult& aRv) {
if (BrowsingContext* bc = GetBrowsingContext(aRv)) { if (BrowsingContext* bc = GetBrowsingContext(aRv)) {

Просмотреть файл

@ -51,6 +51,7 @@ class JSWindowActorChild final : public JSWindowActor {
void AfterDestroy(); void AfterDestroy();
Document* GetDocument(ErrorResult& aRv); Document* GetDocument(ErrorResult& aRv);
BrowsingContext* GetBrowsingContext(ErrorResult& aRv); BrowsingContext* GetBrowsingContext(ErrorResult& aRv);
nsIDocShell* GetDocShell(ErrorResult& aRv);
Nullable<WindowProxyHolder> GetContentWindow(ErrorResult& aRv); Nullable<WindowProxyHolder> GetContentWindow(ErrorResult& aRv);
protected: protected:

Просмотреть файл

@ -7,7 +7,5 @@ support-files =
[browser_domainPolicy.js] [browser_domainPolicy.js]
[browser_memory_distribution_telemetry.js] [browser_memory_distribution_telemetry.js]
skip-if = !e10 # This is an e10s only probe. skip-if = !e10 # This is an e10s only probe.
[browser_remote_navigation_delay_telemetry.js]
skip-if = !e10s # This is an e10s only probe.
[browser_cancel_content_js.js] [browser_cancel_content_js.js]
skip-if = !e10s # This is an e10s only probe. skip-if = !e10s # This is an e10s only probe.

Просмотреть файл

@ -1,48 +0,0 @@
"use strict";
var session = ChromeUtils.import("resource://gre/modules/TelemetrySession.jsm", null);
add_task(async function test_memory_distribution() {
if (Services.prefs.getIntPref("dom.ipc.processCount", 1) < 2) {
ok(true, "Skip this test if e10s-multi is disabled.");
return;
}
let canRecordExtended = Services.telemetry.canRecordExtended;
Services.telemetry.canRecordExtended = true;
registerCleanupFunction(() => Services.telemetry.canRecordExtended = canRecordExtended);
Services.telemetry.getSnapshotForKeyedHistograms("main", true /* clear */);
// Open a remote page in a new tab to trigger the WebNavigation:LoadURI.
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
ok(tab1.linkedBrowser.isRemoteBrowser, "|tab1| should have a remote browser.");
// Open a new tab with about:robots, so it ends up in the parent process with a non-remote browser.
let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
ok(!tab2.linkedBrowser.isRemoteBrowser, "|tab2| should have a non-remote browser.");
// Navigate the tab, so it will change remotness and it triggers the SessionStore:restoreTabContent case.
await BrowserTestUtils.loadURI(tab2.linkedBrowser, "http://example.com");
ok(tab2.linkedBrowser.isRemoteBrowser, "|tab2| should have a remote browser by now.");
// There is no good way to make sure that the parent received the histogram entries from the child processes.
// Let's stick to the ugly, spinning the event loop until we have a good approach (Bug 1357509).
await BrowserTestUtils.waitForCondition(() => {
let s = Services.telemetry.getSnapshotForKeyedHistograms("main", false).content.FX_TAB_REMOTE_NAVIGATION_DELAY_MS;
return s && "WebNavigation:LoadURI" in s && "SessionStore:restoreTabContent" in s;
});
let s = Services.telemetry.getSnapshotForKeyedHistograms("main", false).content.FX_TAB_REMOTE_NAVIGATION_DELAY_MS;
let restoreTabSnapshot = s["SessionStore:restoreTabContent"];
ok(restoreTabSnapshot.sum > 0, "Zero delay for the restoreTabContent case is unlikely.");
ok(restoreTabSnapshot.sum < 10000, "More than 10 seconds delay for the restoreTabContent case is unlikely.");
let loadURISnapshot = s["WebNavigation:LoadURI"];
ok(loadURISnapshot.sum > 0, "Zero delay for the LoadURI case is unlikely.");
ok(loadURISnapshot.sum < 10000, "More than 10 seconds delay for the LoadURI case is unlikely.");
Services.telemetry.getSnapshotForKeyedHistograms("main", true /* clear */);
BrowserTestUtils.removeTab(tab2);
BrowserTestUtils.removeTab(tab1);
});

Просмотреть файл

@ -424,14 +424,16 @@ void cubeb_mock_stream_destroy(cubeb_stream* stream) {
} }
static char const* cubeb_mock_get_backend_id(cubeb* context) { static char const* cubeb_mock_get_backend_id(cubeb* context) {
#if defined(XP_LINUX) #if defined(XP_MACOSX)
return "pulse";
#elif defined(XP_MACOSX)
return "audiounit"; return "audiounit";
#elif defined(XP_WIN) #elif defined(XP_WIN)
return "wasapi"; return "wasapi";
#elif defined(ANDROID) #elif defined(ANDROID)
return "opensl"; return "opensl";
#elif defined(__OpenBSD__)
return "sndio";
#else
return "pulse";
#endif #endif
} }

Просмотреть файл

@ -516,8 +516,8 @@ bool SMILCSSValueType::SetPropertyValues(const SMILValue& aValue,
bool changed = false; bool changed = false;
for (const auto& value : wrapper->mServoValues) { for (const auto& value : wrapper->mServoValues) {
changed |= changed |= Servo_DeclarationBlock_SetPropertyToAnimationValue(aDecl.Raw(),
Servo_DeclarationBlock_SetPropertyToAnimationValue(aDecl.Raw(), value); value, {});
} }
return changed; return changed;

Просмотреть файл

@ -12,7 +12,9 @@
enum AnimationPlayState { "idle", "running", "paused", "finished" }; enum AnimationPlayState { "idle", "running", "paused", "finished" };
[Constructor (optional AnimationEffect? effect = null, enum AnimationReplaceState { "active", "removed", "persisted" };
[Constructor(optional AnimationEffect? effect = null,
optional AnimationTimeline? timeline)] optional AnimationTimeline? timeline)]
interface Animation : EventTarget { interface Animation : EventTarget {
attribute DOMString id; attribute DOMString id;
@ -30,22 +32,30 @@ interface Animation : EventTarget {
readonly attribute AnimationPlayState playState; readonly attribute AnimationPlayState playState;
[BinaryName="pendingFromJS"] [BinaryName="pendingFromJS"]
readonly attribute boolean pending; readonly attribute boolean pending;
[Pref="dom.animations-api.autoremove.enabled"]
readonly attribute AnimationReplaceState replaceState;
[Func="Document::IsWebAnimationsEnabled", Throws] [Func="Document::IsWebAnimationsEnabled", Throws]
readonly attribute Promise<Animation> ready; readonly attribute Promise<Animation> ready;
[Func="Document::IsWebAnimationsEnabled", Throws] [Func="Document::IsWebAnimationsEnabled", Throws]
readonly attribute Promise<Animation> finished; readonly attribute Promise<Animation> finished;
attribute EventHandler onfinish; attribute EventHandler onfinish;
attribute EventHandler oncancel; attribute EventHandler oncancel;
void cancel (); [Pref="dom.animations-api.autoremove.enabled"]
attribute EventHandler onremove;
void cancel();
[Throws] [Throws]
void finish (); void finish();
[Throws, BinaryName="playFromJS"] [Throws, BinaryName="playFromJS"]
void play (); void play();
[Throws, BinaryName="pauseFromJS"] [Throws, BinaryName="pauseFromJS"]
void pause (); void pause();
void updatePlaybackRate (double playbackRate); void updatePlaybackRate (double playbackRate);
[Throws] [Throws]
void reverse (); void reverse();
[Pref="dom.animations-api.autoremove.enabled"]
void persist();
[Pref="dom.animations-api.autoremove.enabled", Throws]
void commitStyles();
}; };
// Non-standard extensions // Non-standard extensions

Просмотреть файл

@ -15,6 +15,6 @@ dictionary DocumentTimelineOptions {
}; };
[Func="Document::AreWebAnimationsTimelinesEnabled", [Func="Document::AreWebAnimationsTimelinesEnabled",
Constructor (optional DocumentTimelineOptions options)] Constructor(optional DocumentTimelineOptions options)]
interface DocumentTimeline : AnimationTimeline { interface DocumentTimeline : AnimationTimeline {
}; };

Просмотреть файл

@ -24,18 +24,18 @@ dictionary KeyframeEffectOptions : EffectTiming {
// processing on the `keyframes` object. // processing on the `keyframes` object.
[Func="Document::IsWebAnimationsEnabled", [Func="Document::IsWebAnimationsEnabled",
RunConstructorInCallerCompartment, RunConstructorInCallerCompartment,
Constructor ((Element or CSSPseudoElement)? target, Constructor((Element or CSSPseudoElement)? target,
object? keyframes, object? keyframes,
optional (unrestricted double or KeyframeEffectOptions) options), optional (unrestricted double or KeyframeEffectOptions) options),
Constructor (KeyframeEffect source)] Constructor(KeyframeEffect source)]
interface KeyframeEffect : AnimationEffect { interface KeyframeEffect : AnimationEffect {
attribute (Element or CSSPseudoElement)? target; attribute (Element or CSSPseudoElement)? target;
[Pref="dom.animations-api.compositing.enabled"] [Pref="dom.animations-api.compositing.enabled"]
attribute IterationCompositeOperation iterationComposite; attribute IterationCompositeOperation iterationComposite;
[Pref="dom.animations-api.compositing.enabled"] [Pref="dom.animations-api.compositing.enabled"]
attribute CompositeOperation composite; attribute CompositeOperation composite;
[Throws] sequence<object> getKeyframes (); [Throws] sequence<object> getKeyframes();
[Throws] void setKeyframes (object? keyframes); [Throws] void setKeyframes(object? keyframes);
}; };
// Non-standard extensions // Non-standard extensions

Просмотреть файл

@ -260,7 +260,7 @@ class MOZ_RAII JS_PUBLIC_API AutoDebuggerJobQueueInterruption {
enum class PromiseRejectionHandlingState { Unhandled, Handled }; enum class PromiseRejectionHandlingState { Unhandled, Handled };
typedef void (*PromiseRejectionTrackerCallback)( typedef void (*PromiseRejectionTrackerCallback)(
JSContext* cx, JS::HandleObject promise, JSContext* cx, bool mutedErrors, JS::HandleObject promise,
JS::PromiseRejectionHandlingState state, void* data); JS::PromiseRejectionHandlingState state, void* data);
/** /**

Просмотреть файл

@ -1137,7 +1137,7 @@ static bool TrackUnhandledRejections(JSContext* cx, JS::HandleObject promise,
} }
static void ForwardingPromiseRejectionTrackerCallback( static void ForwardingPromiseRejectionTrackerCallback(
JSContext* cx, JS::HandleObject promise, JSContext* cx, bool mutedErrors, JS::HandleObject promise,
JS::PromiseRejectionHandlingState state, void* data) { JS::PromiseRejectionHandlingState state, void* data) {
AutoReportException are(cx); AutoReportException are(cx);

Просмотреть файл

@ -637,9 +637,15 @@ void JSRuntime::addUnhandledRejectedPromise(JSContext* cx,
return; return;
} }
bool mutedErrors = false;
if (JSScript* script = cx->currentScript()) {
mutedErrors = script->mutedErrors();
}
void* data = cx->promiseRejectionTrackerCallbackData; void* data = cx->promiseRejectionTrackerCallbackData;
cx->promiseRejectionTrackerCallback( cx->promiseRejectionTrackerCallback(
cx, promise, JS::PromiseRejectionHandlingState::Unhandled, data); cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Unhandled,
data);
} }
void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx, void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx,
@ -649,9 +655,15 @@ void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx,
return; return;
} }
bool mutedErrors = false;
if (JSScript* script = cx->currentScript()) {
mutedErrors = script->mutedErrors();
}
void* data = cx->promiseRejectionTrackerCallbackData; void* data = cx->promiseRejectionTrackerCallbackData;
cx->promiseRejectionTrackerCallback( cx->promiseRejectionTrackerCallback(
cx, promise, JS::PromiseRejectionHandlingState::Handled, data); cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Handled,
data);
} }
mozilla::non_crypto::XorShift128PlusRNG& JSRuntime::randomKeyGenerator() { mozilla::non_crypto::XorShift128PlusRNG& JSRuntime::randomKeyGenerator() {

Просмотреть файл

@ -225,6 +225,10 @@ static ContentMap& GetContentMap() {
template <typename TestType> template <typename TestType>
static bool HasMatchingAnimations(EffectSet& aEffects, TestType&& aTest) { static bool HasMatchingAnimations(EffectSet& aEffects, TestType&& aTest) {
for (KeyframeEffect* effect : aEffects) { for (KeyframeEffect* effect : aEffects) {
if (!effect->GetAnimation() || !effect->GetAnimation()->IsRelevant()) {
continue;
}
if (aTest(*effect, aEffects)) { if (aTest(*effect, aEffects)) {
return true; return true;
} }
@ -263,8 +267,7 @@ bool nsLayoutUtils::HasAnimationOfPropertySet(
return HasMatchingAnimations( return HasMatchingAnimations(
aFrame, aPropertySet, aFrame, aPropertySet,
[&aPropertySet](KeyframeEffect& aEffect, const EffectSet&) { [&aPropertySet](KeyframeEffect& aEffect, const EffectSet&) {
return (aEffect.IsInEffect() || aEffect.IsCurrent()) && return aEffect.HasAnimationOfPropertySet(aPropertySet);
aEffect.HasAnimationOfPropertySet(aPropertySet);
}); });
} }
@ -294,8 +297,7 @@ bool nsLayoutUtils::HasAnimationOfPropertySet(
return HasMatchingAnimations( return HasMatchingAnimations(
*aEffectSet, *aEffectSet,
[&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) { [&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
return (aEffect.IsInEffect() || aEffect.IsCurrent()) && return aEffect.HasAnimationOfPropertySet(aPropertySet);
aEffect.HasAnimationOfPropertySet(aPropertySet);
}); });
} }
@ -305,8 +307,7 @@ bool nsLayoutUtils::HasEffectiveAnimation(
return HasMatchingAnimations( return HasMatchingAnimations(
aFrame, aPropertySet, aFrame, aPropertySet,
[&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) { [&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
return (aEffect.IsInEffect() || aEffect.IsCurrent()) && return aEffect.HasEffectiveAnimationOfPropertySet(aPropertySet,
aEffect.HasEffectiveAnimationOfPropertySet(aPropertySet,
aEffectSet); aEffectSet);
}); });
} }

Просмотреть файл

@ -41,6 +41,7 @@
#include "nsComponentManagerUtils.h" #include "nsComponentManagerUtils.h"
#include "mozilla/Logging.h" #include "mozilla/Logging.h"
#include "mozilla/dom/Document.h" #include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "nsIXULRuntime.h" #include "nsIXULRuntime.h"
#include "jsapi.h" #include "jsapi.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
@ -1747,6 +1748,16 @@ void nsRefreshDriver::CancelIdleRunnable(nsIRunnable* aRunnable) {
} }
} }
static bool ReduceAnimations(Document* aDocument, void* aData) {
if (aDocument->GetPresContext() &&
aDocument->GetPresContext()->EffectCompositor()->NeedsReducing()) {
aDocument->GetPresContext()->EffectCompositor()->ReduceAnimations();
}
aDocument->EnumerateSubDocuments(ReduceAnimations, nullptr);
return true;
}
void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) { void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(), MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
"Shouldn't have a JSContext on the stack"); "Shouldn't have a JSContext on the stack");
@ -1897,6 +1908,28 @@ void nsRefreshDriver::Tick(VsyncId aId, TimeStamp aNowTime) {
} }
} }
// Any animation timelines updated above may cause animations to queue
// Promise resolution microtasks. We shouldn't run these, however, until we
// have fully updated the animation state.
//
// As per the "update animations and send events" procedure[1], we should
// remove replaced animations and then run these microtasks before
// dispatching the corresponding animation events.
//
// [1]
// https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events
if (i == 1) {
nsAutoMicroTask mt;
ReduceAnimations(mPresContext->Document(), nullptr);
}
// Check if running the microtask checkpoint caused the pres context to
// be destroyed.
if (i == 1 && (!mPresContext || !mPresContext->GetPresShell())) {
StopTimer();
return;
}
if (i == 1) { if (i == 1) {
// This is the FlushType::Style case. // This is the FlushType::Style case.

Просмотреть файл

@ -2747,8 +2747,8 @@ nsresult nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(
nsCOMPtr<Document> aDoc = presShell->GetDocument(); nsCOMPtr<Document> aDoc = presShell->GetDocument();
if (aDoc && aSel && !aSel->IsCollapsed()) { if (aDoc && aSel && !aSel->IsCollapsed()) {
return nsCopySupport::HTMLCopy(aSel, aDoc, nsIClipboard::kSelectionCache, return nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
false); aSel, aDoc, nsIClipboard::kSelectionCache, false);
} }
return NS_OK; return NS_OK;
@ -2762,8 +2762,9 @@ int16_t AutoCopyListener::sClipboardID = -1;
* What we do now: * What we do now:
* On every selection change, we copy to the clipboard anew, creating a * On every selection change, we copy to the clipboard anew, creating a
* HTML buffer, a transferable, an nsISupportsString and * HTML buffer, a transferable, an nsISupportsString and
* a huge mess every time. This is basically what nsCopySupport::HTMLCopy() * a huge mess every time. This is basically what
* does to move the selection into the clipboard for Edit->Copy. * nsCopySupport::EncodeDocumentWithContextAndPutToClipboard() does to move the
* selection into the clipboard for Edit->Copy.
* *
* What we should do, to make our end of the deal faster: * What we should do, to make our end of the deal faster:
* Create a singleton transferable with our own magic converter. When selection * Create a singleton transferable with our own magic converter. When selection
@ -2830,8 +2831,10 @@ void AutoCopyListener::OnSelectionChange(Document* aDocument,
return; return;
} }
// Call the copy code.
DebugOnly<nsresult> rv = DebugOnly<nsresult> rv =
nsCopySupport::HTMLCopy(&aSelection, aDocument, sClipboardID, false); nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "nsCopySupport::HTMLCopy() failed"); &aSelection, aDocument, sClipboardID, false);
NS_WARNING_ASSERTION(
NS_SUCCEEDED(rv),
"nsCopySupport::EncodeDocumentWithContextAndPutToClipboard() failed");
} }

Просмотреть файл

@ -173,8 +173,6 @@ SERVO_ARC_TYPE(ComputedStyle, mozilla::ComputedStyle)
// Other special cases. // Other special cases.
// TODO(heycam): Handle these elsewhere.
struct RawServoAnimationValueTable; struct RawServoAnimationValueTable;
struct RawServoAnimationValueMap;
#endif // mozilla_ServoBindingTypes_h #endif // mozilla_ServoBindingTypes_h

Просмотреть файл

@ -24,6 +24,7 @@
// to just generate the forward declaration. // to just generate the forward declaration.
SERVO_BOXED_TYPE(StyleSet, RawServoStyleSet) SERVO_BOXED_TYPE(StyleSet, RawServoStyleSet)
SERVO_BOXED_TYPE(AnimationValueMap, RawServoAnimationValueMap)
SERVO_BOXED_TYPE(AuthorStyles, RawServoAuthorStyles) SERVO_BOXED_TYPE(AuthorStyles, RawServoAuthorStyles)
SERVO_BOXED_TYPE(SelectorList, RawServoSelectorList) SERVO_BOXED_TYPE(SelectorList, RawServoSelectorList)
SERVO_BOXED_TYPE(SharedMemoryBuilder, RawServoSharedMemoryBuilder) SERVO_BOXED_TYPE(SharedMemoryBuilder, RawServoSharedMemoryBuilder)

Просмотреть файл

@ -127,6 +127,19 @@ VARCACHE_PREF(
// DOM prefs // DOM prefs
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Is support for automatically removing replaced filling animations enabled?
#ifdef RELEASE_OR_BETA
# define PREF_VALUE false
#else
# define PREF_VALUE true
#endif
VARCACHE_PREF(
"dom.animations-api.autoremove.enabled",
dom_animations_api_autoremove_enabled,
bool, PREF_VALUE
)
#undef PREF_VALUE
// Is support for composite operations from the Web Animations API enabled? // Is support for composite operations from the Web Animations API enabled?
#ifdef RELEASE_OR_BETA #ifdef RELEASE_OR_BETA
# define PREF_VALUE false # define PREF_VALUE false

Просмотреть файл

@ -1622,6 +1622,9 @@ pref("network.protocol-handler.external.hcp", false);
pref("network.protocol-handler.external.vbscript", false); pref("network.protocol-handler.external.vbscript", false);
pref("network.protocol-handler.external.javascript", false); pref("network.protocol-handler.external.javascript", false);
pref("network.protocol-handler.external.data", false); pref("network.protocol-handler.external.data", false);
pref("network.protocol-handler.external.ie.http", false);
pref("network.protocol-handler.external.iehistory", false);
pref("network.protocol-handler.external.ierss", false);
pref("network.protocol-handler.external.ms-help", false); pref("network.protocol-handler.external.ms-help", false);
pref("network.protocol-handler.external.res", false); pref("network.protocol-handler.external.res", false);
pref("network.protocol-handler.external.shell", false); pref("network.protocol-handler.external.shell", false);

Просмотреть файл

@ -7,8 +7,6 @@
#include "PKCS11ModuleDB.h" #include "PKCS11ModuleDB.h"
#include "ScopedNSSTypes.h" #include "ScopedNSSTypes.h"
#include "mozilla/Telemetry.h"
#include "nsCRTGlue.h"
#include "nsIMutableArray.h" #include "nsIMutableArray.h"
#include "nsNSSCertHelper.h" #include "nsNSSCertHelper.h"
#include "nsNSSComponent.h" #include "nsNSSComponent.h"
@ -63,32 +61,6 @@ PKCS11ModuleDB::DeleteModule(const nsAString& aModuleName) {
return NS_OK; return NS_OK;
} }
// Given a PKCS#11 module, determines an appropriate name to identify it for the
// purposes of gathering telemetry. For 3rd party PKCS#11 modules, this should
// be the name of the dynamic library that implements the module. For built-in
// NSS modules, it will be the common name of the module.
// Because the result will be used as a telemetry scalar key (which must be less
// than 70 characters), this function will also truncate the result if it
// exceeds this limit. (Note that unfortunately telemetry doesn't expose a way
// to programmatically query the scalar key length limit, so we have to
// hard-code the value here.)
void GetModuleNameForTelemetry(/*in*/ const SECMODModule* module,
/*out*/ nsString& result) {
result.Truncate();
if (module->dllName) {
result.AssignASCII(module->dllName);
int32_t separatorIndex = result.RFind(FILE_PATH_SEPARATOR);
if (separatorIndex != kNotFound) {
result = Substring(result, separatorIndex + 1);
}
} else {
result.AssignASCII(module->commonName);
}
if (result.Length() >= 70) {
result.Truncate(69);
}
}
// Add a new PKCS11 module to the user's profile. // Add a new PKCS11 module to the user's profile.
NS_IMETHODIMP NS_IMETHODIMP
PKCS11ModuleDB::AddModule(const nsAString& aModuleName, PKCS11ModuleDB::AddModule(const nsAString& aModuleName,
@ -131,23 +103,6 @@ PKCS11ModuleDB::AddModule(const nsAString& aModuleName,
if (srv != SECSuccess) { if (srv != SECSuccess) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
UniqueSECMODModule module(SECMOD_FindModule(moduleNameNormalized.get()));
if (!module) {
return NS_ERROR_FAILURE;
}
nsAutoString scalarKey;
GetModuleNameForTelemetry(module.get(), scalarKey);
// Scalar keys must be between 0 and 70 characters (exclusive).
// GetModuleNameForTelemetry takes care of keys that are too long.
// If for some reason it couldn't come up with an appropriate name and
// returned an empty result, however, we need to not attempt to record this
// (it wouldn't give us anything useful anyway).
if (scalarKey.Length() > 0) {
Telemetry::ScalarSet(Telemetry::ScalarID::SECURITY_PKCS11_MODULES_LOADED,
scalarKey, true);
}
return NS_OK; return NS_OK;
} }

Просмотреть файл

@ -8,8 +8,6 @@
#include "nsIPKCS11ModuleDB.h" #include "nsIPKCS11ModuleDB.h"
#include "nsString.h"
namespace mozilla { namespace mozilla {
namespace psm { namespace psm {
@ -31,9 +29,6 @@ class PKCS11ModuleDB : public nsIPKCS11ModuleDB {
virtual ~PKCS11ModuleDB() {} virtual ~PKCS11ModuleDB() {}
}; };
void GetModuleNameForTelemetry(/*in*/ const SECMODModule* module,
/*out*/ nsString& result);
} // namespace psm } // namespace psm
} // namespace mozilla } // namespace mozilla

Просмотреть файл

@ -9,7 +9,6 @@
#include "EnterpriseRoots.h" #include "EnterpriseRoots.h"
#include "ExtendedValidation.h" #include "ExtendedValidation.h"
#include "NSSCertDBTrustDomain.h" #include "NSSCertDBTrustDomain.h"
#include "PKCS11ModuleDB.h"
#include "ScopedNSSTypes.h" #include "ScopedNSSTypes.h"
#include "SharedSSLState.h" #include "SharedSSLState.h"
#include "cert.h" #include "cert.h"
@ -1793,28 +1792,6 @@ nsresult nsNSSComponent::InitializeNSS() {
Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true); Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true);
} }
// Gather telemetry on any PKCS#11 modules we have loaded. Note that because
// we load the built-in root module asynchronously after this, the telemetry
// will not include it.
{ // Introduce scope for the AutoSECMODListReadLock.
AutoSECMODListReadLock lock;
for (SECMODModuleList* list = SECMOD_GetDefaultModuleList(); list;
list = list->next) {
nsAutoString scalarKey;
GetModuleNameForTelemetry(list->module, scalarKey);
// Scalar keys must be between 0 and 70 characters (exclusive).
// GetModuleNameForTelemetry takes care of keys that are too long. If for
// some reason it couldn't come up with an appropriate name and returned
// an empty result, however, we need to not attempt to record this (it
// wouldn't give us anything useful anyway).
if (scalarKey.Length() > 0) {
Telemetry::ScalarSet(
Telemetry::ScalarID::SECURITY_PKCS11_MODULES_LOADED, scalarKey,
true);
}
}
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NSS Initialization done\n")); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NSS Initialization done\n"));
{ {

Просмотреть файл

@ -50,40 +50,13 @@ function checkTestModuleExists() {
return testModule; return testModule;
} }
function checkModuleTelemetry(additionalExpectedModule = undefined) {
let expectedModules = [
"NSS Internal PKCS #11 Module",
];
if (additionalExpectedModule) {
expectedModules.push(additionalExpectedModule);
}
expectedModules.sort();
let telemetry = Services.telemetry.getSnapshotForKeyedScalars("main", false).parent;
let moduleTelemetry = telemetry["security.pkcs11_modules_loaded"];
let actualModules = [];
Object.keys(moduleTelemetry).forEach((key) => {
ok(moduleTelemetry[key], "each keyed scalar should be true");
actualModules.push(key);
});
actualModules.sort();
equal(actualModules.length, expectedModules.length,
"the number of actual and expected loaded modules should be the same");
for (let i in actualModules) {
equal(actualModules[i], expectedModules[i],
"actual and expected module names should match");
}
}
function run_test() { function run_test() {
// Check that if we have never added the test module, that we don't find it // Check that if we have never added the test module, that we don't find it
// in the module list. // in the module list.
checkTestModuleNotPresent(); checkTestModuleNotPresent();
checkModuleTelemetry();
// Check that adding the test module makes it appear in the module list. // Check that adding the test module makes it appear in the module list.
loadPKCS11TestModule(true); loadPKCS11TestModule(true);
checkModuleTelemetry(
`${AppConstants.DLL_PREFIX}pkcs11testmodule${AppConstants.DLL_SUFFIX}`);
let testModule = checkTestModuleExists(); let testModule = checkTestModuleExists();
// Check that listing the slots for the test module works. // Check that listing the slots for the test module works.

Просмотреть файл

@ -8,7 +8,7 @@ for (let [key, val] of Object.entries({
// Don't manually modify this line, as it is automatically replaced on merge day // Don't manually modify this line, as it is automatically replaced on merge day
// by the gecko_migration.py script. // by the gecko_migration.py script.
WEAVE_VERSION: "1.70.0", WEAVE_VERSION: "1.71.0",
// Sync Server API version that the client supports. // Sync Server API version that the client supports.
SYNC_API_VERSION: "1.5", SYNC_API_VERSION: "1.5",

Просмотреть файл

@ -5,12 +5,22 @@
//! FFI implementations for types listed in ServoBoxedTypeList.h. //! FFI implementations for types listed in ServoBoxedTypeList.h.
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
use crate::properties::animated_properties::AnimationValueMap;
use to_shmem::SharedMemoryBuilder; use to_shmem::SharedMemoryBuilder;
// TODO(heycam): The FFI impls for most of the types in ServoBoxedTypeList.h are spread across // TODO(heycam): The FFI impls for most of the types in ServoBoxedTypeList.h are spread across
// various files at the moment, but should probably all move here, and use macros to define // various files at the moment, but should probably all move here, and use macros to define
// them more succinctly, like we do in arc_types.rs. // them more succinctly, like we do in arc_types.rs.
#[cfg(feature = "gecko")]
unsafe impl HasFFI for AnimationValueMap {
type FFIType = crate::gecko_bindings::bindings::RawServoAnimationValueMap;
}
#[cfg(feature = "gecko")]
unsafe impl HasSimpleFFI for AnimationValueMap {}
#[cfg(feature = "gecko")]
unsafe impl HasBoxFFI for AnimationValueMap {}
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
unsafe impl HasFFI for SharedMemoryBuilder { unsafe impl HasFFI for SharedMemoryBuilder {
type FFIType = crate::gecko_bindings::bindings::RawServoSharedMemoryBuilder; type FFIType = crate::gecko_bindings::bindings::RawServoSharedMemoryBuilder;

Просмотреть файл

@ -9,9 +9,7 @@
from itertools import groupby from itertools import groupby
%> %>
#[cfg(feature = "gecko")] use crate::gecko_bindings::structs::RawServoAnimationValueMap;
#[cfg(feature = "gecko")] use crate::gecko_bindings::structs::nsCSSPropertyID; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::nsCSSPropertyID;
#[cfg(feature = "gecko")] use crate::gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
use itertools::{EitherOrBoth, Itertools}; use itertools::{EitherOrBoth, Itertools};
use crate::properties::{CSSWideKeyword, PropertyDeclaration}; use crate::properties::{CSSWideKeyword, PropertyDeclaration};
use crate::properties::longhands; use crate::properties::longhands;
@ -190,13 +188,6 @@ impl AnimatedProperty {
/// composed for each TransitionProperty. /// composed for each TransitionProperty.
pub type AnimationValueMap = FxHashMap<LonghandId, AnimationValue>; pub type AnimationValueMap = FxHashMap<LonghandId, AnimationValue>;
#[cfg(feature = "gecko")]
unsafe impl HasFFI for AnimationValueMap {
type FFIType = RawServoAnimationValueMap;
}
#[cfg(feature = "gecko")]
unsafe impl HasSimpleFFI for AnimationValueMap {}
/// An enum to represent a single computed value belonging to an animated /// An enum to represent a single computed value belonging to an animated
/// property in order to be interpolated with another one. When interpolating, /// property in order to be interpolated with another one. When interpolating,
/// both values need to belong to the same property. /// both values need to belong to the same property.

Просмотреть файл

@ -2337,6 +2337,14 @@ impl SourcePropertyDeclaration {
} }
} }
/// Create one with a single PropertyDeclaration.
#[inline]
pub fn with_one(decl: PropertyDeclaration) -> Self {
let mut result = Self::new();
result.declarations.push(decl);
result
}
/// Similar to Vec::drain: leaves this empty when the return value is dropped. /// Similar to Vec::drain: leaves this empty when the return value is dropped.
pub fn drain(&mut self) -> SourcePropertyDeclarationDrain { pub fn drain(&mut self) -> SourcePropertyDeclarationDrain {
SourcePropertyDeclarationDrain { SourcePropertyDeclarationDrain {

Просмотреть файл

@ -101,7 +101,7 @@ use style::global_style_data::{GlobalStyleData, GLOBAL_STYLE_DATA, STYLE_THREAD_
use style::invalidation::element::restyle_hints::RestyleHint; use style::invalidation::element::restyle_hints::RestyleHint;
use style::media_queries::MediaList; use style::media_queries::MediaList;
use style::parser::{self, Parse, ParserContext}; use style::parser::{self, Parse, ParserContext};
use style::properties::animated_properties::AnimationValue; use style::properties::animated_properties::{AnimationValue, AnimationValueMap};
use style::properties::{parse_one_declaration_into, parse_style_attribute}; use style::properties::{parse_one_declaration_into, parse_style_attribute};
use style::properties::{ComputedValues, Importance, NonCustomPropertyId}; use style::properties::{ComputedValues, Importance, NonCustomPropertyId};
use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId}; use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
@ -913,6 +913,36 @@ fn resolve_rules_for_element_with_context<'a>(
.0 .0
} }
#[no_mangle]
pub extern "C" fn Servo_AnimationValueMap_Create() -> Owned<structs::RawServoAnimationValueMap> {
Box::<AnimationValueMap>::default().into_ffi()
}
#[no_mangle]
pub unsafe extern "C" fn Servo_AnimationValueMap_Drop(value_map: *mut structs::RawServoAnimationValueMap) {
AnimationValueMap::drop_ffi(value_map)
}
#[no_mangle]
pub extern "C" fn Servo_AnimationValueMap_GetValue(
raw_value_map: &mut structs::RawServoAnimationValueMap,
property_id: nsCSSPropertyID,
) -> Strong<RawServoAnimationValue> {
use style::properties::animated_properties::AnimationValueMap;
let property = match LonghandId::from_nscsspropertyid(property_id) {
Ok(longhand) => longhand,
Err(()) => return Strong::null(),
};
let value_map = AnimationValueMap::from_ffi_mut(raw_value_map);
value_map
.get(&property)
.map_or(Strong::null(), |value| {
Arc::new(value.clone()).into_strong()
})
}
#[no_mangle] #[no_mangle]
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement( pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
raw_style_set: &RawServoStyleSet, raw_style_set: &RawServoStyleSet,
@ -4091,6 +4121,28 @@ pub unsafe extern "C" fn Servo_DeclarationBlock_GetPropertyIsImportant(
}) })
} }
#[inline(always)]
fn set_property_to_declarations(
block: &RawServoDeclarationBlock,
parsed_declarations: &mut SourcePropertyDeclaration,
before_change_closure: DeclarationBlockMutationClosure,
importance: Importance,
) -> bool {
let mut updates = Default::default();
let will_change = read_locked_arc(block, |decls: &PropertyDeclarationBlock| {
decls.prepare_for_update(&parsed_declarations, importance, &mut updates)
});
if !will_change {
return false;
}
before_change_closure.invoke();
write_locked_arc(block, |decls: &mut PropertyDeclarationBlock| {
decls.update(parsed_declarations.drain(), importance, &mut updates)
});
true
}
fn set_property( fn set_property(
declarations: &RawServoDeclarationBlock, declarations: &RawServoDeclarationBlock,
property_id: PropertyId, property_id: PropertyId,
@ -4123,19 +4175,13 @@ fn set_property(
} else { } else {
Importance::Normal Importance::Normal
}; };
let mut updates = Default::default();
let will_change = read_locked_arc(declarations, |decls: &PropertyDeclarationBlock| {
decls.prepare_for_update(&source_declarations, importance, &mut updates)
});
if !will_change {
return false;
}
before_change_closure.invoke(); set_property_to_declarations(
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| { declarations,
decls.update(source_declarations.drain(), importance, &mut updates) &mut source_declarations,
}); before_change_closure,
true importance,
)
} }
#[no_mangle] #[no_mangle]
@ -4167,13 +4213,17 @@ pub unsafe extern "C" fn Servo_DeclarationBlock_SetProperty(
pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue( pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue(
declarations: &RawServoDeclarationBlock, declarations: &RawServoDeclarationBlock,
animation_value: &RawServoAnimationValue, animation_value: &RawServoAnimationValue,
before_change_closure: DeclarationBlockMutationClosure,
) -> bool { ) -> bool {
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| { let mut source_declarations =
decls.push( SourcePropertyDeclaration::with_one(AnimationValue::as_arc(&animation_value).uncompute());
AnimationValue::as_arc(&animation_value).uncompute(),
set_property_to_declarations(
declarations,
&mut source_declarations,
before_change_closure,
Importance::Normal, Importance::Normal,
) )
})
} }
#[no_mangle] #[no_mangle]

Просмотреть файл

@ -21,16 +21,12 @@ job-template:
attributes: attributes:
artifact_prefix: public artifact_prefix: public
artifact_map: artifact_map:
by-project: by-release-type:
beta|release.*|esr.*:
by-platform:
android.*: taskcluster/taskgraph/manifests/fennec_candidates_checksums.yml
default: taskcluster/taskgraph/manifests/firefox_candidates_checksums.yml
default: default:
by-platform: by-platform:
android.*: taskcluster/taskgraph/manifests/fennec_nightly_checksums.yml android.*: taskcluster/taskgraph/manifests/fennec_nightly_checksums.yml
default: taskcluster/taskgraph/manifests/firefox_nightly_checksums.yml default: taskcluster/taskgraph/manifests/firefox_nightly_checksums.yml
mozilla-beta:
by-platform:
android.*: taskcluster/taskgraph/manifests/fennec_candidates_checksums.yml
default: taskcluster/taskgraph/manifests/firefox_candidates_checksums.yml
mozilla-release:
by-platform:
android.*: taskcluster/taskgraph/manifests/fennec_candidates_checksums.yml
default: taskcluster/taskgraph/manifests/firefox_candidates_checksums.yml

Просмотреть файл

@ -57,7 +57,6 @@ job-template:
shipping-phase: promote shipping-phase: promote
attributes: attributes:
artifact_map: artifact_map:
by-project: by-release-type:
beta|release.*|esr.*: taskcluster/taskgraph/manifests/firefox_candidates.yml
default: taskcluster/taskgraph/manifests/firefox_nightly.yml default: taskcluster/taskgraph/manifests/firefox_nightly.yml
mozilla-beta: taskcluster/taskgraph/manifests/firefox_candidates.yml
mozilla-release: taskcluster/taskgraph/manifests/firefox_candidates.yml

Просмотреть файл

@ -41,7 +41,6 @@ job-template:
shipping-phase: promote shipping-phase: promote
attributes: attributes:
artifact_map: artifact_map:
by-project: by-release-type:
mozilla-release: taskcluster/taskgraph/manifests/fennec_candidates.yml beta|release.*|esr.*: taskcluster/taskgraph/manifests/fennec_candidates.yml
mozilla-beta: taskcluster/taskgraph/manifests/fennec_candidates.yml
default: taskcluster/taskgraph/manifests/fennec_nightly.yml default: taskcluster/taskgraph/manifests/fennec_nightly.yml

Просмотреть файл

@ -269,7 +269,7 @@ win32-shippable/opt:
by-release-type: by-release-type:
nightly: true nightly: true
beta: true beta: true
release: true release.*: true
esr.*: false esr.*: false
default: default:
by-project: by-project:
@ -832,7 +832,7 @@ win32-devedition-nightly/opt:
by-release-type: by-release-type:
nightly: true nightly: true
beta: true beta: true
release: true release.*: true
default: default:
by-project: by-project:
# browser/confvars.sh looks for nightly-try # browser/confvars.sh looks for nightly-try

Просмотреть файл

@ -42,7 +42,7 @@ jobs:
- releases/bouncer_firefox_beta.py - releases/bouncer_firefox_beta.py
release: release:
- releases/bouncer_firefox_release.py - releases/bouncer_firefox_release.py
esr60: esr.*:
- releases/bouncer_firefox_esr.py - releases/bouncer_firefox_esr.py
default: default:
- releases/bouncer_firefox_beta.py - releases/bouncer_firefox_beta.py
@ -51,6 +51,7 @@ jobs:
mozilla-beta: LATEST_FIREFOX_RELEASED_DEVEL_VERSION mozilla-beta: LATEST_FIREFOX_RELEASED_DEVEL_VERSION
mozilla-release: LATEST_FIREFOX_VERSION mozilla-release: LATEST_FIREFOX_VERSION
mozilla-esr60: FIREFOX_ESR mozilla-esr60: FIREFOX_ESR
mozilla-esr68: FIREFOX_ESR_NEXT
default: LATEST_FIREFOX_DEVEL_VERSION default: LATEST_FIREFOX_DEVEL_VERSION
products-url: https://product-details.mozilla.org/1.0/firefox_versions.json products-url: https://product-details.mozilla.org/1.0/firefox_versions.json
treeherder: treeherder:

Просмотреть файл

@ -34,12 +34,14 @@ jobs:
beta: [32] beta: [32]
release: [145] release: [145]
esr60: [806] esr60: [806]
esr68: [882]
default: [] default: []
staging: staging:
by-release-type: by-release-type:
beta: [32] beta: [32]
release: [145] release: [145]
esr60: [806] esr60: [806]
esr68: [875]
default: [] default: []
treeherder: treeherder:
platform: firefox-release/opt platform: firefox-release/opt
@ -50,10 +52,13 @@ jobs:
description: Schedule Firefox publishing in balrog (bz2) description: Schedule Firefox publishing in balrog (bz2)
name: release-firefox_schedule_publishing_in_balrog-bz2 name: release-firefox_schedule_publishing_in_balrog-bz2
shipping-product: firefox shipping-product: firefox
run-on-releases: [esr60] run-on-releases: [esr60, esr68]
worker: worker:
product: firefox product: firefox
publish-rules: [521] publish-rules:
by-release-type:
esr60: [521]
default: []
blob-suffix: -bz2 blob-suffix: -bz2
treeherder: treeherder:
platform: firefox-release/opt platform: firefox-release/opt

Просмотреть файл

@ -39,12 +39,13 @@ jobs:
by-release-type: by-release-type:
beta: ["beta", "beta-localtest", "beta-cdntest"] beta: ["beta", "beta-localtest", "beta-cdntest"]
release(-rc)?: ["release", "release-localtest", "release-cdntest"] release(-rc)?: ["release", "release-localtest", "release-cdntest"]
esr60: ["esr", "esr-localtest", "esr-cdntest"] esr.*: ["esr", "esr-localtest", "esr-cdntest", "esr-localtest-next", "esr-cdntest-next"]
default: [] default: []
rules-to-update: rules-to-update:
by-release-type: by-release-type:
beta: ["firefox-beta-cdntest", "firefox-beta-localtest"] beta: ["firefox-beta-cdntest", "firefox-beta-localtest"]
release(-rc)?: ["firefox-release-cdntest", "firefox-release-localtest"] release(-rc)?: ["firefox-release-cdntest", "firefox-release-localtest"]
esr68: ["firefox-esr68-cdntest", "firefox-esr68-localtest"]
esr60: ["firefox-esr60-cdntest", "firefox-esr60-localtest"] esr60: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
default: [] default: []
platforms: ["linux", "linux64", "macosx64", "win32", "win64", "win64-aarch64"] platforms: ["linux", "linux64", "macosx64", "win32", "win64", "win64-aarch64"]
@ -58,11 +59,14 @@ jobs:
name: submit-toplevel-firefox-release-to-balrog-bz2 name: submit-toplevel-firefox-release-to-balrog-bz2
description: Submit toplevel Firefox release to balrog description: Submit toplevel Firefox release to balrog
shipping-product: firefox shipping-product: firefox
run-on-releases: [esr60] run-on-releases: [esr60, esr68]
worker: worker:
product: firefox product: firefox
channel-names: ["esr", "esr-localtest", "esr-cdntest"] channel-names: ["esr", "esr-localtest", "esr-cdntest", "esr-localtest-next", "esr-cdntest-next"]
rules-to-update: ["esr52-cdntest", "esr52-localtest"] rules-to-update:
by-release-type:
esr68: ["esr52-cdntest-next", "esr52-localtest-next"]
esr60: ["esr52-cdntest", "esr52-localtest"]
platforms: ["linux", "linux64", "macosx64", "win32", "win64"] platforms: ["linux", "linux64", "macosx64", "win32", "win64"]
blob-suffix: -bz2 blob-suffix: -bz2
complete-mar-filename-pattern: '%s-%s.bz2.complete.mar' complete-mar-filename-pattern: '%s-%s.bz2.complete.mar'

Просмотреть файл

@ -79,6 +79,10 @@ jobs:
firefox-esr-latest-ssl: installer-ssl firefox-esr-latest-ssl: installer-ssl
firefox-esr-latest: installer firefox-esr-latest: installer
firefox-esr-msi-latest-ssl: msi firefox-esr-msi-latest-ssl: msi
mozilla-esr68:
firefox-esr-next-latest-ssl: installer-ssl
firefox-esr-next-latest: installer
firefox-esr-next-msi-latest-ssl: msi
birch: birch:
firefox-latest-ssl: installer-ssl firefox-latest-ssl: installer-ssl
firefox-latest: installer firefox-latest: installer

Просмотреть файл

@ -57,7 +57,7 @@ jobs:
- releases/bouncer_firefox_beta.py - releases/bouncer_firefox_beta.py
release: release:
- releases/bouncer_firefox_release.py - releases/bouncer_firefox_release.py
esr60: esr.*:
- releases/bouncer_firefox_esr.py - releases/bouncer_firefox_esr.py
default: default:
- releases/bouncer_firefox_beta.py - releases/bouncer_firefox_beta.py

Просмотреть файл

@ -52,11 +52,11 @@ jobs:
firefox: firefox:
bouncer-platforms: ['linux', 'linux64', 'osx', 'win', 'win64', 'win64-aarch64'] bouncer-platforms: ['linux', 'linux64', 'osx', 'win', 'win64', 'win64-aarch64']
bouncer-products: bouncer-products:
by-project: by-release-type:
default: ['complete-mar', 'installer', 'installer-ssl', 'partial-mar', 'stub-installer', 'msi'] default: ['complete-mar', 'installer', 'installer-ssl', 'partial-mar', 'stub-installer', 'msi']
esr68: ['complete-mar', 'complete-mar-bz2', 'installer', 'installer-ssl', 'partial-mar', 'msi']
# No stub installer in esr60 # No stub installer in esr60
mozilla-esr60: ['complete-mar', 'complete-mar-bz2', 'installer', 'installer-ssl', 'partial-mar'] esr60: ['complete-mar', 'complete-mar-bz2', 'installer', 'installer-ssl', 'partial-mar']
jamun: ['complete-mar', 'complete-mar-bz2', 'installer', 'installer-ssl', 'partial-mar']
shipping-product: firefox shipping-product: firefox
treeherder: treeherder:
platform: firefox-release/opt platform: firefox-release/opt

Просмотреть файл

@ -59,7 +59,7 @@ jobs:
firefox: firefox:
shipping-product: firefox shipping-product: firefox
attributes: attributes:
build_platform: linux64-snap-shippable build_platform: linux64-shippable
build_type: opt build_type: opt
treeherder: treeherder:
symbol: Snap(r) symbol: Snap(r)

Просмотреть файл

@ -0,0 +1,98 @@
# 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/.
---
loader: taskgraph.loader.transform:loader
transforms:
- taskgraph.transforms.release:run_on_releases
- taskgraph.transforms.update_verify_config:transforms
- taskgraph.transforms.job:transforms
- taskgraph.transforms.task:transforms
job-defaults:
name: update-verify-config-next
run-on-projects: [] # to make sure this never runs as part of CI
run-on-releases: [esr68]
shipping-phase: promote
worker-type: b-linux
worker:
docker-image:
in-tree: "update-verify"
max-run-time: 3600
artifacts:
- name: public/build/update-verify.cfg
path: /builds/worker/checkouts/gecko/update-verify.cfg
type: file
run:
sparse-profile: mozharness
treeherder:
symbol: UVCnext
kind: test
tier: 1
extra:
app-name: browser
branch-prefix: mozilla
archive-prefix:
by-release-level:
staging: "http://ftp.stage.mozaws.net/pub"
production: "https://archive.mozilla.org/pub"
previous-archive-prefix:
by-release-level:
staging: "https://archive.mozilla.org/pub"
production: null
aus-server:
by-release-level:
staging: "https://stage.balrog.nonprod.cloudops.mozgcp.net"
production: "https://aus5.mozilla.org"
override-certs:
by-release-level:
staging: dep
production: null
updater-platform: linux-x86_64
product: firefox
channel: "esr-localtest-next"
include-version: esr68-next
last-watershed: "52.0esr"
jobs:
firefox-next-linux:
shipping-product: firefox
treeherder:
platform: linux32-shippable/opt
attributes:
build_platform: linux-shippable
extra:
platform: linux-i686
firefox-next-linux64:
shipping-product: firefox
treeherder:
platform: linux64-shippable/opt
attributes:
build_platform: linux64-shippable
extra:
platform: linux-x86_64
firefox-next-macosx64:
shipping-product: firefox
treeherder:
platform: osx-shippable/opt
attributes:
build_platform: macosx64-shippable
extra:
platform: mac
firefox-next-win32:
shipping-product: firefox
treeherder:
platform: windows2012-32-shippable/opt
attributes:
build_platform: win32-shippable
extra:
platform: win32
firefox-next-win64:
shipping-product: firefox
treeherder:
platform: windows2012-64-shippable/opt
attributes:
build_platform: win64-shippable
extra:
platform: win64

Просмотреть файл

@ -70,6 +70,7 @@ job-defaults:
win64-aarch64.*: "67.0" win64-aarch64.*: "67.0"
default: null default: null
esr60: "52.0esr" esr60: "52.0esr"
esr68: "68.0esr"
default: "default" default: "default"
jobs: jobs:

Просмотреть файл

@ -0,0 +1,73 @@
# 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/.
---
loader: taskgraph.loader.transform:loader
kind-dependencies:
- post-balrog-dummy
- post-beetmover-dummy
- release-balrog-submit-toplevel
- release-update-verify-config-next
transforms:
- taskgraph.transforms.release:run_on_releases
- taskgraph.transforms.release_deps:transforms
- taskgraph.transforms.update_verify:transforms
- taskgraph.transforms.job:transforms
- taskgraph.transforms.task:transforms
job-defaults:
name: update-verify-next
run-on-projects: [] # to make sure this never runs as part of CI
run-on-releases: [esr68]
shipping-phase: promote
worker-type: b-linux
worker:
artifacts:
- name: 'public/build/diff-summary.log'
path: '/builds/worker/tools/release/updates/diff-summary.log'
type: file
docker-image:
in-tree: "update-verify"
max-run-time: 7200
retry-exit-status:
- 255
env:
CHANNEL: "esr-localtest-next"
treeherder:
symbol: UV(UVnext)
kind: test
extra:
chunks: 12
jobs:
firefox-next-linux64:
description: linux64 esr-next update verify
shipping-product: firefox
attributes:
build_platform: linux64-shippable
firefox-next-linux:
description: linux esr-next update verify
shipping-product: firefox
attributes:
build_platform: linux-shippable
firefox-next-win64:
description: win64 esr-next update verify
shipping-product: firefox
attributes:
build_platform: win64-shippable
firefox-next-win32:
description: win32 esr-next update verify
shipping-product: firefox
attributes:
build_platform: win32-shippable
firefox-next-macosx64:
description: macosx64 esr-next update verify
shipping-product: firefox
attributes:
build_platform: macosx64-shippable

Просмотреть файл

@ -63,7 +63,7 @@ job-template:
- repackage/win64_signed.py - repackage/win64_signed.py
package-formats: package-formats:
by-release-type: by-release-type:
esr60: esr(60|68):
by-build-platform: by-build-platform:
linux.*: [mar, mar-bz2] linux.*: [mar, mar-bz2]
linux4\b.*: [mar, mar-bz2] linux4\b.*: [mar, mar-bz2]

Просмотреть файл

@ -73,7 +73,7 @@ job-template:
- repackage/win64_signed.py - repackage/win64_signed.py
package-formats: package-formats:
by-release-type: by-release-type:
esr60: esr(60|68):
by-build-platform: by-build-platform:
linux.*: [mar, mar-bz2] linux.*: [mar, mar-bz2]
linux4\b.*: [mar, mar-bz2] linux4\b.*: [mar, mar-bz2]

Просмотреть файл

@ -46,10 +46,10 @@
}, },
"arrow": { "arrow": {
"hashes": [ "hashes": [
"sha256:3397e5448952e18e1295bf047014659effa5ae8da6a5371d37ff0ddc46fa6872", "sha256:002f2315cf4c8404de737c42860441732d339bbc57fee584e2027520e055ecc1",
"sha256:6f54d9f016c0b7811fac9fb8c2c7fa7421d80c54dbdd75ffb12913c55db60b8a" "sha256:82dd5e13b733787d4eb0fef42d1ee1a99136dc1d65178f70373b3678b3181bfc"
], ],
"version": "==0.13.1" "version": "==0.13.2"
}, },
"asn1crypto": { "asn1crypto": {
"hashes": [ "hashes": [
@ -74,11 +74,11 @@
}, },
"awscli": { "awscli": {
"hashes": [ "hashes": [
"sha256:b7a6e758a7d2e7230e4e21acab9f80db2fd31248333ca8575b4538a5c43ebd2c", "sha256:34e7ee2bd912e6613ac064099c13e2114722d508fc35e01fd0dfc3be41ddd92c",
"sha256:fae8839c4ddf6e7fb49543beec8d9659afd60e2fa23481ee723390f0a3a7d0f7" "sha256:f73c11e6726a5ca25df3399762fae7f6882c71e097dc622d0e4743c9f8e84526"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.16.156" "version": "==1.16.161"
}, },
"backports.lzma": { "backports.lzma": {
"hashes": [ "hashes": [
@ -88,10 +88,10 @@
}, },
"botocore": { "botocore": {
"hashes": [ "hashes": [
"sha256:1517c52eaa3056d0e81f9a81b580d7f28440e7e1523d10a8acc8160c56be7113", "sha256:5e4774c106bb02f8e4639818c2f8157b8ec114a76e481e17cd3fe6955206e088",
"sha256:19d9d56fcf4f16ffea8a929bbf3c72db3458b6c1f306c04031f3166759cd62ac" "sha256:cfc667e7888aad09ead8f7e32129ea90aa5c7f602531094954bf6305db74aac4"
], ],
"version": "==1.12.146" "version": "==1.12.151"
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
@ -370,11 +370,11 @@
}, },
"requests": { "requests": {
"hashes": [ "hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e", "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b" "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.21.0" "version": "==2.22.0"
}, },
"rsa": { "rsa": {
"hashes": [ "hashes": [
@ -392,11 +392,11 @@
}, },
"scriptworker": { "scriptworker": {
"hashes": [ "hashes": [
"sha256:44b19ef0ddfe14309ddb035e4f6e82da8d9eb3d7c3de8ed82ee74a75beefb767", "sha256:836181e36befcd74bb6b9457fd9336d8efa1350e77c285f0dc32bdb0ef6e4270",
"sha256:4b7bd567c8b511f1a87c68ac541c94b730d6f307ad86bb0af279ac30ef5867e9" "sha256:d858c4e0dae3305dec3683458d2e879752b0a3645e9f95278b717d53d45c2809"
], ],
"index": "pypi", "index": "pypi",
"version": "==23.0.4" "version": "==23.0.5"
}, },
"sh": { "sh": {
"hashes": [ "hashes": [

Просмотреть файл

@ -367,11 +367,14 @@ Publishes signed langpacks to archive.mozilla.org
release-update-verify release-update-verify
--------------------- ---------------------
Verifies the contents and package of release update MARs. Verifies the contents and package of release update MARs.
release-secondary-update-verify release-secondary-update-verify
------------------------------- -------------------------------
Verifies the contents and package of release update MARs. Verifies the contents and package of release update MARs.
release-update-verify-next
--------------------------
Verifies the contents and package of release and updare MARs from the previous ESR release.
release-update-verify-config release-update-verify-config
---------------------------- ----------------------------
Creates configs for release-update-verify tasks Creates configs for release-update-verify tasks
@ -380,6 +383,10 @@ release-secondary-update-verify-config
-------------------------------------- --------------------------------------
Creates configs for release-secondary-update-verify tasks Creates configs for release-secondary-update-verify tasks
release-update-verify-config-next
---------------------------------
Creates configs for release-update-verify-next tasks
release-updates-builder release-updates-builder
----------------------- -----------------------
Top level Balrog blob submission & patcher/update verify config updates. Top level Balrog blob submission & patcher/update verify config updates.

Просмотреть файл

@ -332,7 +332,7 @@ mapping:
checksums_path: ${path_platform}/${locale}/Firefox Setup ${version}.msi checksums_path: ${path_platform}/${locale}/Firefox Setup ${version}.msi
target.complete.mar: target.complete.mar:
<<: *default <<: *default
description: "The main installer we ship our mobile products baked within" description: "Complete MAR to serve as updates"
all_locales: true all_locales: true
from: from:
- mar-signing - mar-signing
@ -341,6 +341,18 @@ mapping:
update_balrog_manifest: true update_balrog_manifest: true
destinations: destinations:
- ${version}-candidates/build${build_number}/update/${path_platform} - ${version}-candidates/build${build_number}/update/${path_platform}
target.bz2.complete.mar:
<<: *default
description: "Complete MAR with bz2 compression and SHA1 signing to serve as updates"
all_locales: true
from:
- mar-signing
pretty_name: firefox-${version}.bz2.complete.mar
checksums_path: update/${path_platform}/${locale}/firefox-${version}.bz2.complete.mar
update_balrog_manifest: true
balrog_format: bz2
destinations:
- ${version}-candidates/build${build_number}/update/${path_platform}
${partial}: ${partial}:
<<: *default <<: *default
description: "Partials MAR files to serve as updates" description: "Partials MAR files to serve as updates"

Просмотреть файл

@ -315,7 +315,7 @@ def make_task_worker(config, jobs):
signing_task_ref = "<" + str(signing_task) + ">" signing_task_ref = "<" + str(signing_task) + ">"
build_task_ref = "<" + str(build_task) + ">" build_task_ref = "<" + str(build_task) + ">"
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
upstream_artifacts = generate_beetmover_upstream_artifacts( upstream_artifacts = generate_beetmover_upstream_artifacts(
config, job, platform, locale config, job, platform, locale
) )
@ -329,7 +329,7 @@ def make_task_worker(config, jobs):
'upstream-artifacts': upstream_artifacts, 'upstream-artifacts': upstream_artifacts,
} }
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
worker['artifact-map'] = generate_beetmover_artifact_map( worker['artifact-map'] = generate_beetmover_artifact_map(
config, job, platform=platform, locale=locale) config, job, platform=platform, locale=locale)

Просмотреть файл

@ -145,7 +145,7 @@ def make_beetmover_checksums_worker(config, jobs):
'release-properties': craft_release_properties(config, job), 'release-properties': craft_release_properties(config, job),
} }
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
upstream_artifacts = generate_beetmover_upstream_artifacts( upstream_artifacts = generate_beetmover_upstream_artifacts(
config, job, platform, locale config, job, platform, locale
) )

Просмотреть файл

@ -138,7 +138,7 @@ def make_beetmover_checksums_worker(config, jobs):
'release-properties': craft_release_properties(config, job), 'release-properties': craft_release_properties(config, job),
} }
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
upstream_artifacts = generate_beetmover_upstream_artifacts( upstream_artifacts = generate_beetmover_upstream_artifacts(
config, job, platform, locales config, job, platform, locales
) )

Просмотреть файл

@ -358,7 +358,7 @@ def make_task_worker(config, jobs):
locale = job["attributes"].get("locale") locale = job["attributes"].get("locale")
platform = job["attributes"]["build_platform"] platform = job["attributes"]["build_platform"]
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
upstream_artifacts = generate_beetmover_upstream_artifacts( upstream_artifacts = generate_beetmover_upstream_artifacts(
config, job, platform, locale) config, job, platform, locale)
else: else:
@ -373,7 +373,7 @@ def make_task_worker(config, jobs):
'upstream-artifacts': upstream_artifacts, 'upstream-artifacts': upstream_artifacts,
} }
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
worker['artifact-map'] = generate_beetmover_artifact_map( worker['artifact-map'] = generate_beetmover_artifact_map(
config, job, platform=platform, locale=locale) config, job, platform=platform, locale=locale)
@ -412,7 +412,7 @@ def make_partials_artifacts(config, jobs):
partials_info = get_partials_info_from_params( partials_info = get_partials_info_from_params(
config.params.get('release_history'), balrog_platform, locale) config.params.get('release_history'), balrog_platform, locale)
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
job['worker']['artifact-map'].extend( job['worker']['artifact-map'].extend(
generate_beetmover_partials_artifact_map( generate_beetmover_partials_artifact_map(
config, job, partials_info, platform=platform, locale=locale)) config, job, partials_info, platform=platform, locale=locale))

Просмотреть файл

@ -137,7 +137,7 @@ def make_beetmover_checksums_worker(config, jobs):
raise NotImplementedError( raise NotImplementedError(
"Beetmover checksums must have a beetmover and signing dependency!") "Beetmover checksums must have a beetmover and signing dependency!")
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
upstream_artifacts = generate_beetmover_upstream_artifacts(config, upstream_artifacts = generate_beetmover_upstream_artifacts(config,
job, platform, locale) job, platform, locale)
else: else:
@ -149,7 +149,7 @@ def make_beetmover_checksums_worker(config, jobs):
'upstream-artifacts': upstream_artifacts, 'upstream-artifacts': upstream_artifacts,
} }
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
worker['artifact-map'] = generate_beetmover_artifact_map( worker['artifact-map'] = generate_beetmover_artifact_map(
config, job, platform=platform) config, job, platform=platform)

Просмотреть файл

@ -131,7 +131,8 @@ def make_task_worker(config, jobs):
**{'release-level': config.params.release_level()} **{'release-level': config.params.release_level()}
) )
resolve_keyed_by( resolve_keyed_by(
job, 'bouncer-products', item_name=job['name'], project=config.params['project'] job, 'bouncer-products', item_name=job['name'],
**{'release-type': config.params['release_type']}
) )
# No need to filter out ja-JP-mac, we need to upload both; but we do # No need to filter out ja-JP-mac, we need to upload both; but we do

Просмотреть файл

@ -125,7 +125,7 @@ def make_task_worker(config, jobs):
platform = job["attributes"]["build_platform"] platform = job["attributes"]["build_platform"]
locale = job["attributes"]["chunk_locales"] locale = job["attributes"]["chunk_locales"]
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
upstream_artifacts = generate_beetmover_upstream_artifacts( upstream_artifacts = generate_beetmover_upstream_artifacts(
config, job, platform, locale, config, job, platform, locale,
) )
@ -139,7 +139,7 @@ def make_task_worker(config, jobs):
'upstream-artifacts': upstream_artifacts, 'upstream-artifacts': upstream_artifacts,
} }
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
job['worker']['artifact-map'] = generate_beetmover_artifact_map( job['worker']['artifact-map'] = generate_beetmover_artifact_map(
config, job, platform=platform, locale=locale) config, job, platform=platform, locale=locale)
@ -232,7 +232,7 @@ def _change_platform_data(config, platform_job, platform):
platform_job['worker']['release-properties']['platform'] = platform platform_job['worker']['release-properties']['platform'] = platform
# amend artifactMap entries as well # amend artifactMap entries as well
if should_use_artifact_map(backup_platform, config.params['project']): if should_use_artifact_map(backup_platform):
platform_mapping = { platform_mapping = {
'linux64': 'linux-x86_64', 'linux64': 'linux-x86_64',
'linux': 'linux-i686', 'linux': 'linux-i686',

Просмотреть файл

@ -156,7 +156,7 @@ def make_task_worker(config, jobs):
platform = job["attributes"]["build_platform"] platform = job["attributes"]["build_platform"]
# Works with Firefox/Devedition. Commented for migration. # Works with Firefox/Devedition. Commented for migration.
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
upstream_artifacts = generate_beetmover_upstream_artifacts( upstream_artifacts = generate_beetmover_upstream_artifacts(
config, job, platform=None, locale=None config, job, platform=None, locale=None
) )
@ -168,7 +168,7 @@ def make_task_worker(config, jobs):
worker['upstream-artifacts'] = upstream_artifacts worker['upstream-artifacts'] = upstream_artifacts
# Works with Firefox/Devedition. Commented for migration. # Works with Firefox/Devedition. Commented for migration.
if should_use_artifact_map(platform, config.params['project']): if should_use_artifact_map(platform):
worker['artifact-map'] = generate_beetmover_artifact_map( worker['artifact-map'] = generate_beetmover_artifact_map(
config, job, platform=platform) config, job, platform=platform)

Просмотреть файл

@ -19,7 +19,7 @@ transforms = TransformSequence()
def add_command(config, tasks): def add_command(config, tasks):
config_tasks = {} config_tasks = {}
for dep in config.kind_dependencies_tasks: for dep in config.kind_dependencies_tasks:
if 'update-verify-config' in dep.kind: if 'update-verify-config' in dep.kind or 'update-verify-next-config' in dep.kind:
config_tasks[dep.name] = dep config_tasks[dep.name] = dep
for task in tasks: for task in tasks:

Просмотреть файл

@ -33,6 +33,8 @@ INCLUDE_VERSION_REGEXES = {
"devedition_hack": r"'^((?!58\.0b1$)\d+\.\d+(b\d+)?)$'", "devedition_hack": r"'^((?!58\.0b1$)\d+\.\d+(b\d+)?)$'",
# Same as nonbeta, except for the esr suffix # Same as nonbeta, except for the esr suffix
"esr": r"'^\d+\.\d+(\.\d+)?esr$'", "esr": r"'^\d+\.\d+(\.\d+)?esr$'",
# Previous esr versions, for update testing before we update users to esr68
"esr68-next": r"'^(52|60)+\.\d+(\.\d+)?esr$'",
} }
MAR_CHANNEL_ID_OVERRIDE_REGEXES = { MAR_CHANNEL_ID_OVERRIDE_REGEXES = {

Просмотреть файл

@ -427,8 +427,10 @@ def generate_beetmover_upstream_artifacts(config, job, platform, locale=None, de
resolve_keyed_by( resolve_keyed_by(
job, 'attributes.artifact_map', job, 'attributes.artifact_map',
'artifact map', 'artifact map',
project=config.params['project'], **{
platform=platform 'release-type': config.params['release_type'],
'platform': platform,
}
) )
map_config = deepcopy(cached_load_yaml(job['attributes']['artifact_map'])) map_config = deepcopy(cached_load_yaml(job['attributes']['artifact_map']))
upstream_artifacts = list() upstream_artifacts = list()
@ -469,6 +471,11 @@ def generate_beetmover_upstream_artifacts(config, job, platform, locale=None, de
filename, filename,
)) ))
if getattr(job['dependencies'][dep], 'release_artifacts', None):
paths = [
path for path in paths
if path in job['dependencies'][dep].release_artifacts]
if not paths: if not paths:
continue continue
@ -556,8 +563,10 @@ def generate_beetmover_artifact_map(config, job, **kwargs):
resolve_keyed_by( resolve_keyed_by(
job, 'attributes.artifact_map', job, 'attributes.artifact_map',
'artifact map', 'artifact map',
project=config.params['project'], **{
platform=platform 'release-type': config.params['release_type'],
'platform': platform,
}
) )
map_config = deepcopy(cached_load_yaml(job['attributes']['artifact_map'])) map_config = deepcopy(cached_load_yaml(job['attributes']['artifact_map']))
base_artifact_prefix = map_config.get('base_artifact_prefix', get_artifact_prefix(job)) base_artifact_prefix = map_config.get('base_artifact_prefix', get_artifact_prefix(job))
@ -697,8 +706,10 @@ def generate_beetmover_partials_artifact_map(config, job, partials_info, **kwarg
resolve_keyed_by( resolve_keyed_by(
job, 'attributes.artifact_map', job, 'attributes.artifact_map',
'artifact map', 'artifact map',
project=config.params['project'], **{
platform=platform 'release-type': config.params['release_type'],
'platform': platform,
}
) )
map_config = deepcopy(cached_load_yaml(job['attributes']['artifact_map'])) map_config = deepcopy(cached_load_yaml(job['attributes']['artifact_map']))
base_artifact_prefix = map_config.get('base_artifact_prefix', get_artifact_prefix(job)) base_artifact_prefix = map_config.get('base_artifact_prefix', get_artifact_prefix(job))
@ -811,48 +822,10 @@ def generate_beetmover_partials_artifact_map(config, job, partials_info, **kwarg
return artifacts return artifacts
# should_use_artifact_map {{{ def should_use_artifact_map(platform):
def should_use_artifact_map(platform, project):
"""Return True if this task uses the beetmover artifact map. """Return True if this task uses the beetmover artifact map.
This function exists solely for the beetmover artifact map This function exists solely for the beetmover artifact map
migration. migration.
""" """
if 'linux64-snap-shippable' in platform: return 'devedition' not in platform
# Snap has never been implemented outside of declarative artifacts. We need to use
# declarative artifacts no matter the branch we're on
return True
# FIXME: once we're ready to switch fully to declarative artifacts on other
# branches, we can expand this; for now, Fennec is rolled-out to all
# release branches, while Firefox only to mozilla-central
platforms = [
'android',
'fennec'
]
projects = ['mozilla-central', 'mozilla-beta', 'mozilla-release']
if any([pl in platform for pl in platforms]) and any([pj == project for pj in projects]):
return True
platforms = [
'linux', # needed for beetmover-langpacks-checksums
'linux64', # which inherit amended platform from their beetmover counterpart
'win32',
'win64',
'macosx64',
'linux-shippable',
'linux64-shippable',
'macosx64-shippable',
'win32-shippable',
'win64-shippable',
'win64-aarch64-shippable',
'win64-asan-reporter-nightly',
'linux64-asan-reporter-nightly',
'firefox-source',
'firefox-release',
]
projects = ['mozilla-central', 'mozilla-beta', 'mozilla-release']
if any([pl == platform for pl in platforms]) and any([pj == project for pj in projects]):
return True
return False

Просмотреть файл

@ -91,8 +91,8 @@ def cli(runner_class=MarionetteTestRunner, parser_class=MarionetteArguments,
failed = harness_instance.run() failed = harness_instance.run()
if failed > 0: if failed > 0:
sys.exit(10) sys.exit(10)
except Exception: except Exception as e:
logger.error('Failure during harness execution', exc_info=True) logger.error(e.message, exc_info=True)
sys.exit(1) sys.exit(1)
sys.exit(0) sys.exit(0)

Просмотреть файл

@ -44,6 +44,18 @@ config = {
"win64", "win64",
], ],
}, },
"complete-mar-bz2": {
"product-name": "Firefox-%(version)s-Complete-bz2",
"check_uptake": True,
"platforms": [
"linux",
"linux64",
"osx",
"win",
"win64",
"win64-aarch64",
],
},
}, },
"partials": { "partials": {
"releases-dir": { "releases-dir": {

Просмотреть файл

@ -1,5 +0,0 @@
[disallow-crossorigin.html]
expected: ERROR
[Promise rejection event should be muted for cross-origin non-CORS script]
expected: FAIL

Просмотреть файл

@ -1 +1 @@
prefs: [dom.animations-api.compositing.enabled:true, dom.animations-api.core.enabled:true, dom.animations-api.getAnimations.enabled:true, dom.animations-api.implicit-keyframes.enabled:true, dom.animations-api.timelines.enabled:true, layout.css.step-position-jump.enabled:true] prefs: [dom.animations-api.autoremove.enabled:true, dom.animations-api.compositing.enabled:true, dom.animations-api.core.enabled:true, dom.animations-api.getAnimations.enabled:true, dom.animations-api.implicit-keyframes.enabled:true, dom.animations-api.timelines.enabled:true, layout.css.step-position-jump.enabled:true]

Просмотреть файл

@ -162,6 +162,24 @@ async_test(function(t) {
p = Promise.all([Promise.reject(e)]); p = Promise.all([Promise.reject(e)]);
}, 'unhandledrejection: from Promise.reject, indirected through Promise.all'); }, 'unhandledrejection: from Promise.reject, indirected through Promise.all');
async_test(function(t) {
var p;
var unhandled = function(ev) {
if (ev.promise === p) {
t.step(function() {
assert_equals(ev.reason.name, 'InvalidStateError');
assert_equals(ev.promise, p);
});
t.done();
}
};
addEventListener('unhandledrejection', unhandled);
ensureCleanup(t, unhandled);
p = createImageBitmap(new Blob());
}, 'unhandledrejection: from createImageBitmap which is UA triggered');
// //
// Negative unhandledrejection/rejectionhandled tests with immediate attachment // Negative unhandledrejection/rejectionhandled tests with immediate attachment
// //
@ -270,6 +288,16 @@ async_test(function(t) {
}, 'no unhandledrejection/rejectionhandled: all inside a queued task, a rejection handler attached synchronously to ' + }, 'no unhandledrejection/rejectionhandled: all inside a queued task, a rejection handler attached synchronously to ' +
'a promise created from returning a Promise.reject-created promise in a fulfillment handler'); 'a promise created from returning a Promise.reject-created promise in a fulfillment handler');
async_test(function(t) {
var p;
onUnhandledFail(t, function() { return p; });
var unreached = t.unreached_func('promise should not be fulfilled');
p = createImageBitmap(new Blob()).then(unreached, function() {});
}, 'no unhandledrejection/rejectionhandled: rejection handler attached synchronously to a promise created from ' +
'createImageBitmap');
// //
// Negative unhandledrejection/rejectionhandled tests with microtask-delayed attachment // Negative unhandledrejection/rejectionhandled tests with microtask-delayed attachment
// //
@ -659,6 +687,43 @@ async_test(function(t) {
}, 10); }, 10);
}, 'delayed handling: delaying handling by setTimeout(,10) will cause both events to fire'); }, 'delayed handling: delaying handling by setTimeout(,10) will cause both events to fire');
async_test(function(t) {
var unhandledPromises = [];
var unhandledReasons = [];
var p;
var unhandled = function(ev) {
if (ev.promise === p) {
t.step(function() {
unhandledPromises.push(ev.promise);
unhandledReasons.push(ev.reason.name);
});
}
};
var handled = function(ev) {
if (ev.promise === p) {
t.step(function() {
assert_array_equals(unhandledPromises, [p]);
assert_array_equals(unhandledReasons, ['InvalidStateError']);
assert_equals(ev.promise, p);
assert_equals(ev.reason.name, 'InvalidStateError');
});
}
};
addEventListener('unhandledrejection', unhandled);
addEventListener('rejectionhandled', handled);
ensureCleanup(t, unhandled, handled);
p = createImageBitmap(new Blob());
setTimeout(function() {
var unreached = t.unreached_func('promise should not be fulfilled');
p.then(unreached, function(reason) {
assert_equals(reason.name, 'InvalidStateError');
setTimeout(function() { t.done(); }, 10);
});
}, 10);
}, 'delayed handling: delaying handling rejected promise created from createImageBitmap will cause both events to fire');
// //
// Miscellaneous tests about integration with the rest of the platform // Miscellaneous tests about integration with the rest of the platform
// //

Просмотреть файл

@ -0,0 +1,161 @@
<!doctype html>
<meta charset=utf-8>
<title>The effect value of a keyframe effect: Overlapping keyframes</title>
<link rel="help" href="https://drafts.csswg.org/web-animations/#the-effect-value-of-a-keyframe-animation-effect">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
function assert_opacity_value(opacity, expected, description) {
return assert_approx_equals(
parseFloat(opacity),
expected,
0.0001,
description
);
}
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: 0.3 },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
// Sanity check
assert_equals(animA.replaceState, 'removed');
assert_equals(animB.replaceState, 'active');
// animA is now removed so if we cancel animB, we should go back to the
// underlying value
animB.cancel();
assert_opacity_value(
getComputedStyle(div).opacity,
0.1,
'Opacity should be the un-animated value'
);
}, 'Removed animations do not contribute to animated style');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: 0.3, composite: 'add' },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
// Sanity check
assert_equals(animA.replaceState, 'removed');
assert_equals(animB.replaceState, 'active');
// animA has been removed so the final result should be 0.1 + 0.3 = 0.4.
// (If animA were not removed it would be 0.2 + 0.3 = 0.5.)
assert_opacity_value(
getComputedStyle(div).opacity,
0.4,
'Opacity value should not include the removed animation'
);
}, 'Removed animations do not contribute to the effect stack');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: 0.3 },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
animA.persist();
animB.cancel();
assert_opacity_value(
getComputedStyle(div).opacity,
0.2,
"Opacity should be the persisted animation's value"
);
}, 'Persisted animations contribute to animated style');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: 0.3, composite: 'add' },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
assert_opacity_value(
getComputedStyle(div).opacity,
0.4,
'Opacity value should NOT include the contribution of the removed animation'
);
animA.persist();
assert_opacity_value(
getComputedStyle(div).opacity,
0.5,
'Opacity value should include the contribution of the persisted animation'
);
}, 'Persisted animations contribute to the effect stack');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
// Persist the animation before it finishes (and before it would otherwise be
// removed).
animA.persist();
const animB = div.animate(
{ opacity: 0.3, composite: 'add' },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
assert_opacity_value(
getComputedStyle(div).opacity,
0.5,
'Opacity value should include the contribution of the persisted animation'
);
}, 'Animations persisted before they would be removed contribute to the'
+ ' effect stack');
</script>
</body>

Просмотреть файл

@ -209,6 +209,28 @@ test(t => {
}, 'Returns animations based on dynamic changes to individual' }, 'Returns animations based on dynamic changes to individual'
+ ' animations\' current time'); + ' animations\' current time');
promise_test(async t => {
const div = createDiv(t);
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
await animA.finished;
assert_array_equals(div.getAnimations(), [animB]);
}, 'Does not return an animation that has been removed');
promise_test(async t => {
const div = createDiv(t);
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
await animA.finished;
animA.persist();
assert_array_equals(div.getAnimations(), [animA, animB]);
}, 'Returns an animation that has been persisted');
promise_test(async t => { promise_test(async t => {
const div = createDiv(t); const div = createDiv(t);
const watcher = EventWatcher(t, div, 'transitionrun'); const watcher = EventWatcher(t, div, 'transitionrun');

Просмотреть файл

@ -0,0 +1,389 @@
<!doctype html>
<meta charset=utf-8>
<title>Animation.commitStyles</title>
<link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animation-commitstyles">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
function assert_numeric_style_equals(opacity, expected, description) {
return assert_approx_equals(
parseFloat(opacity),
expected,
0.0001,
description
);
}
test(t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animation = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
animation.finish();
animation.commitStyles();
// Cancel the animation so we can inspect the underlying style
animation.cancel();
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.2);
}, 'Commits styles');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: 0.3 },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
animB.cancel();
animA.commitStyles();
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.2);
}, 'Commits styles for an animation that has been removed');
test(t => {
const div = createDiv(t);
div.style.margin = '10px';
const animation = div.animate(
{ margin: '20px' },
{ duration: 1, fill: 'forwards' }
);
animation.finish();
animation.commitStyles();
animation.cancel();
assert_equals(div.style.marginLeft, '20px');
}, 'Commits shorthand styles');
test(t => {
const div = createDiv(t);
div.style.marginLeft = '10px';
const animation = div.animate(
{ marginInlineStart: '20px' },
{ duration: 1, fill: 'forwards' }
);
animation.finish();
animation.commitStyles();
animation.cancel();
assert_equals(div.style.marginLeft, '20px');
}, 'Commits logical properties');
test(t => {
const div = createDiv(t);
div.style.marginLeft = '10px';
const animation = div.animate({ opacity: [0.2, 0.7] }, 1000);
animation.currentTime = 500;
animation.commitStyles();
animation.cancel();
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.45);
}, 'Commits values calculated mid-interval');
test(t => {
const div = createDiv(t);
div.style.setProperty('--target', '0.5');
const animation = div.animate(
{ opacity: 'var(--target)' },
{ duration: 1, fill: 'forwards' }
);
animation.finish();
animation.commitStyles();
animation.cancel();
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.5);
// Changes to the variable should have no effect
div.style.setProperty('--target', '1');
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.5);
}, 'Commits variables as their computed values');
test(t => {
const div = createDiv(t);
div.style.fontSize = '10px';
const animation = div.animate(
{ width: '10em' },
{ duration: 1, fill: 'forwards' }
);
animation.finish();
animation.commitStyles();
animation.cancel();
assert_numeric_style_equals(getComputedStyle(div).width, 100);
// Changes to the font-size should have no effect
div.style.fontSize = '20px';
assert_numeric_style_equals(getComputedStyle(div).width, 100);
}, 'Commits em units as pixel values');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
const animA = div.animate(
{ opacity: '0.2' },
{ duration: 1, fill: 'forwards' }
);
const animB = div.animate(
{ opacity: '0.2', composite: 'add' },
{ duration: 1, fill: 'forwards' }
);
const animC = div.animate(
{ opacity: '0.3', composite: 'add' },
{ duration: 1, fill: 'forwards' }
);
animA.persist();
animB.persist();
await animB.finished;
// The values above have been chosen such that various error conditions
// produce results that all differ from the desired result:
//
// Expected result:
//
// animA + animB = 0.4
//
// Likely error results:
//
// <underlying> = 0.1
// (Commit didn't work at all)
//
// animB = 0.2
// (Didn't add at all when resolving)
//
// <underlying> + animB = 0.3
// (Added to the underlying value instead of lower-priority animations when
// resolving)
//
// <underlying> + animA + animB = 0.5
// (Didn't respect the composite mode of lower-priority animations)
//
// animA + animB + animC = 0.7
// (Resolved the whole stack, not just up to the target effect)
//
animB.commitStyles();
animA.cancel();
animB.cancel();
animC.cancel();
assert_numeric_style_equals(getComputedStyle(div).opacity, 0.4);
}, 'Commits the intermediate value of an animation in the middle of stack');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.1';
// Setup animation
const animation = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
animation.finish();
// Setup observer
const mutationRecords = [];
const observer = new MutationObserver(mutations => {
mutationRecords.push(...mutations);
});
observer.observe(div, { attributes: true, attributeOldValue: true });
animation.commitStyles();
// Wait for mutation records to be dispatched
await Promise.resolve();
assert_equals(mutationRecords.length, 1, 'Should have one mutation record');
const mutation = mutationRecords[0];
assert_equals(mutation.type, 'attributes');
assert_equals(mutation.oldValue, 'opacity: 0.1;');
observer.disconnect();
}, 'Triggers mutation observers when updating style');
promise_test(async t => {
const div = createDiv(t);
div.style.opacity = '0.2';
// Setup animation
const animation = div.animate(
{ opacity: 0.2 },
{ duration: 1, fill: 'forwards' }
);
animation.finish();
// Setup observer
const mutationRecords = [];
const observer = new MutationObserver(mutations => {
mutationRecords.push(...mutations);
});
observer.observe(div, { attributes: true });
animation.commitStyles();
// Wait for mutation records to be dispatched
await Promise.resolve();
assert_equals(mutationRecords.length, 0, 'Should have no mutation records');
observer.disconnect();
}, 'Does NOT trigger mutation observers when the change to style is redundant');
test(t => {
const pseudo = getPseudoElement(t, 'before');
const animation = pseudo.animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
assert_throws('NoModificationAllowedError', () => {
animation.commitStyles();
});
}, 'Throws if the target element is a pseudo element');
test(t => {
const animation = createDiv(t).animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
const nonStyleElement
= document.createElementNS('http://example.org/test', 'test');
document.body.appendChild(nonStyleElement);
animation.effect.target = nonStyleElement;
assert_throws('NoModificationAllowedError', () => {
animation.commitStyles();
});
nonStyleElement.remove();
}, 'Throws if the target element is not something with a style attribute');
test(t => {
const div = createDiv(t);
const animation = div.animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
div.style.display = 'none';
assert_throws('InvalidStateError', () => {
animation.commitStyles();
});
}, 'Throws if the target effect is display:none');
test(t => {
const container = createDiv(t);
const div = createDiv(t);
container.append(div);
const animation = div.animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
container.style.display = 'none';
assert_throws('InvalidStateError', () => {
animation.commitStyles();
});
}, "Throws if the target effect's ancestor is display:none");
test(t => {
const container = createDiv(t);
const div = createDiv(t);
container.append(div);
const animation = div.animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
container.style.display = 'contents';
// Should NOT throw
animation.commitStyles();
}, 'Treats display:contents as rendered');
test(t => {
const container = createDiv(t);
const div = createDiv(t);
container.append(div);
const animation = div.animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
div.style.display = 'contents';
container.style.display = 'none';
assert_throws('InvalidStateError', () => {
animation.commitStyles();
});
}, 'Treats display:contents in a display:none subtree as not rendered');
test(t => {
const div = createDiv(t);
const animation = div.animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
div.remove();
assert_throws('InvalidStateError', () => {
animation.commitStyles();
});
}, 'Throws if the target effect is disconnected');
test(t => {
const pseudo = getPseudoElement(t, 'before');
const animation = pseudo.animate(
{ opacity: 0 },
{ duration: 1, fill: 'forwards' }
);
pseudo.element.remove();
assert_throws('NoModificationAllowedError', () => {
animation.commitStyles();
});
}, 'Checks the pseudo element condition before the not rendered condition');
</script>
</body>

Просмотреть файл

@ -0,0 +1,40 @@
<!doctype html>
<meta charset=utf-8>
<title>Animation.persist</title>
<link rel="help" href="https://drafts.csswg.org/web-animations/#dom-animation-persist">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
async_test(t => {
const div = createDiv(t);
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
animA.onremove = t.step_func_done(() => {
assert_equals(animA.replaceState, 'removed');
animA.persist();
assert_equals(animA.replaceState, 'persisted');
});
}, 'Allows an animation to be persisted after being removed');
promise_test(async t => {
const div = createDiv(t);
const animA = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
const animB = div.animate({ opacity: 1 }, { duration: 1, fill: 'forwards' });
animA.persist();
await animA.finished;
assert_equals(animA.replaceState, 'persisted');
}, 'Allows an animation to be persisted before being removed');
</script>
</body>

Просмотреть файл

@ -11,8 +11,8 @@
<script> <script>
'use strict'; 'use strict';
// Test that each property defined in the Animation interface does not produce // Test that each property defined in the Animation interface behaves as
// style change events. // expected with regards to whether or not it produces style change events.
// //
// There are two types of tests: // There are two types of tests:
// //
@ -29,8 +29,9 @@
// (b) An object with the following format: // (b) An object with the following format:
// //
// { // {
// setup: elem => { /* return Animation */ } // setup: elem => { /* return Animation */ },
// test: animation => { /* play |animation| */ } // test: animation => { /* play |animation| */ },
// shouldFlush: boolean /* optional, defaults to false */
// } // }
// //
// If the latter form is used, the setup function should return an Animation // If the latter form is used, the setup function should return an Animation
@ -56,15 +57,17 @@
// animation, but simply needs to get/set the property under test. // animation, but simply needs to get/set the property under test.
const PlayAnimationTest = testFuncOrObj => { const PlayAnimationTest = testFuncOrObj => {
let test, setup; let test, setup, shouldFlush;
if (typeof testFuncOrObj === 'function') { if (typeof testFuncOrObj === 'function') {
test = testFuncOrObj; test = testFuncOrObj;
shouldFlush = false;
} else { } else {
test = testFuncOrObj.test; test = testFuncOrObj.test;
if (typeof testFuncOrObj.setup === 'function') { if (typeof testFuncOrObj.setup === 'function') {
setup = testFuncOrObj.setup; setup = testFuncOrObj.setup;
} }
shouldFlush = !!testFuncOrObj.shouldFlush;
} }
if (!setup) { if (!setup) {
@ -74,11 +77,11 @@ const PlayAnimationTest = testFuncOrObj => {
); );
} }
return { test, setup }; return { test, setup, shouldFlush };
}; };
const UsePropertyTest = testFuncOrObj => { const UsePropertyTest = testFuncOrObj => {
const { setup, test } = PlayAnimationTest(testFuncOrObj); const { setup, test, shouldFlush } = PlayAnimationTest(testFuncOrObj);
let coveringAnimation; let coveringAnimation;
return { return {
@ -93,6 +96,7 @@ const UsePropertyTest = testFuncOrObj => {
test(animation); test(animation);
coveringAnimation.play(); coveringAnimation.play();
}, },
shouldFlush,
}; };
}; };
@ -160,6 +164,7 @@ const tests = {
}), }),
playState: UsePropertyTest(animation => animation.playState), playState: UsePropertyTest(animation => animation.playState),
pending: UsePropertyTest(animation => animation.pending), pending: UsePropertyTest(animation => animation.pending),
replaceState: UsePropertyTest(animation => animation.replaceState),
ready: UsePropertyTest(animation => animation.ready), ready: UsePropertyTest(animation => animation.ready),
finished: UsePropertyTest(animation => { finished: UsePropertyTest(animation => {
// Get the finished Promise // Get the finished Promise
@ -172,6 +177,13 @@ const tests = {
// Set the onfinish menber // Set the onfinish menber
animation.onfinish = () => {}; animation.onfinish = () => {};
}), }),
onremove: UsePropertyTest(animation => {
// Get the onremove member
animation.onremove;
// Set the onremove menber
animation.onremove = () => {};
}),
oncancel: UsePropertyTest(animation => { oncancel: UsePropertyTest(animation => {
// Get the oncancel member // Get the oncancel member
animation.oncancel; animation.oncancel;
@ -225,6 +237,51 @@ const tests = {
animation.reverse(); animation.reverse();
}, },
}), }),
persist: PlayAnimationTest({
setup: async elem => {
// Create an animation whose replaceState is 'removed'.
const animA = elem.animate(
{ opacity: 1 },
{ duration: 1, fill: 'forwards' }
);
const animB = elem.animate(
{ opacity: 1 },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
animB.cancel();
return animA;
},
test: animation => {
animation.persist();
},
}),
commitStyles: PlayAnimationTest({
setup: async elem => {
// Create an animation whose replaceState is 'removed'.
const animA = elem.animate(
// It's important to use opacity of '1' here otherwise we'll create a
// transition due to updating the specified style whereas the transition
// we want to detect is the one from flushing due to calling
// commitStyles.
{ opacity: 1 },
{ duration: 1, fill: 'forwards' }
);
const animB = elem.animate(
{ opacity: 1 },
{ duration: 1, fill: 'forwards' }
);
await animA.finished;
animB.cancel();
return animA;
},
test: animation => {
animation.commitStyles();
},
shouldFlush: true,
}),
get ['Animation constructor']() { get ['Animation constructor']() {
let originalElem; let originalElem;
return UsePropertyTest({ return UsePropertyTest({
@ -266,7 +323,7 @@ test(() => {
for (const key of properties) { for (const key of properties) {
promise_test(async t => { promise_test(async t => {
assert_own_property(tests, key, `Should have a test for '${key}' property`); assert_own_property(tests, key, `Should have a test for '${key}' property`);
const { setup, test } = tests[key]; const { setup, test, shouldFlush } = tests[key];
// Setup target element // Setup target element
const div = createDiv(t); const div = createDiv(t);
@ -276,7 +333,7 @@ for (const key of properties) {
}); });
// Setup animation // Setup animation
const animation = setup(div); const animation = await setup(div);
// Setup transition start point // Setup transition start point
div.style.transition = 'opacity 100s'; div.style.transition = 'opacity 100s';
@ -291,17 +348,24 @@ for (const key of properties) {
// If the test function produced a style change event it will have triggered // If the test function produced a style change event it will have triggered
// a transition. // a transition.
// Wait for the animation to start and then for at least one animation // Wait for the animation to start and then for at least two animation
// frame to give the transitionrun event a chance to be dispatched. // frames to give the transitionrun event a chance to be dispatched.
assert_true( assert_true(
typeof animation.ready !== 'undefined', typeof animation.ready !== 'undefined',
'Should have a valid animation to wait on' 'Should have a valid animation to wait on'
); );
await animation.ready; await animation.ready;
await waitForAnimationFrames(1); await waitForAnimationFrames(2);
assert_false(gotTransition, 'A transition should NOT have been triggered'); if (shouldFlush) {
}, `Animation.${key} does NOT trigger a style change event`); assert_true(gotTransition, 'A transition should have been triggered');
} else {
assert_false(
gotTransition,
'A transition should NOT have been triggered'
);
}
}, `Animation.${key} produces expected style change events`);
} }
</script> </script>
</body> </body>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше