зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
ce435076fa
|
@ -753,11 +753,6 @@ class ContentSessionStore {
|
|||
this.restoreHistory(data);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
case "SessionStore:resetRestore":
|
||||
|
|
|
@ -4310,8 +4310,7 @@ var SessionStoreInternal = {
|
|||
browser.messageManager.sendAsyncMessage("SessionStore:restoreTabContent",
|
||||
{loadArguments, isRemotenessUpdate,
|
||||
reason: aOptions.restoreContentReason ||
|
||||
RESTORE_TAB_CONTENT_REASON.SET_STATE,
|
||||
requestTime: Services.telemetry.msSystemNow()});
|
||||
RESTORE_TAB_CONTENT_REASON.SET_STATE});
|
||||
|
||||
// Focus the tab's content area.
|
||||
if (aTab.selected && !window.isBlankPageURL(uri)) {
|
||||
|
|
|
@ -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_CHROME_ENABLED_PREF = "devtools.inspector.chrome.three-pane-enabled";
|
||||
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.
|
||||
|
@ -1299,6 +1300,7 @@ Inspector.prototype = {
|
|||
executeSoon(() => {
|
||||
try {
|
||||
selfUpdate(this.selection.nodeFront);
|
||||
this.telemetry.scalarAdd(TELEMETRY_SCALAR_NODE_SELECTION_COUNT, 1);
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
|
|
|
@ -9,9 +9,6 @@ const ChromeUtils = require("ChromeUtils");
|
|||
const Services = require("Services");
|
||||
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
|
||||
const { E10SUtils } = require("resource://gre/modules/E10SUtils.jsm");
|
||||
const Telemetry = require("devtools/client/shared/telemetry");
|
||||
|
||||
const telemetry = new Telemetry();
|
||||
|
||||
function readInputStreamToString(stream) {
|
||||
return NetUtil.readInputStreamToString(stream, stream.available());
|
||||
|
@ -85,7 +82,6 @@ BrowserElementWebNavigation.prototype = {
|
|||
triggeringPrincipal: E10SUtils.serializePrincipal(
|
||||
triggeringPrincipal ||
|
||||
Services.scriptSecurityManager.createNullPrincipal({})),
|
||||
requestTime: telemetry.msSystemNow(),
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Animation.h"
|
||||
|
||||
#include "AnimationUtils.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "mozilla/dom/AnimationBinding.h"
|
||||
#include "mozilla/dom/AnimationPlaybackEvent.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
|
@ -14,10 +16,13 @@
|
|||
#include "mozilla/AnimationEventDispatcher.h"
|
||||
#include "mozilla/AnimationTarget.h"
|
||||
#include "mozilla/AutoRestore.h"
|
||||
#include "mozilla/Maybe.h" // For Maybe
|
||||
#include "mozilla/TypeTraits.h" // For std::forward<>
|
||||
#include "nsAnimationManager.h" // For CSSAnimation
|
||||
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
|
||||
#include "mozilla/DeclarationBlock.h"
|
||||
#include "mozilla/Maybe.h" // For Maybe
|
||||
#include "mozilla/TypeTraits.h" // For std::forward<>
|
||||
#include "nsAnimationManager.h" // For CSSAnimation
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsDOMMutationObserver.h" // For nsAutoAnimationMutationBatch
|
||||
#include "nsDOMCSSAttrDeclaration.h" // For nsDOMCSSAttributeDeclaration
|
||||
#include "nsThreadUtils.h" // For nsRunnableMethod and nsRevocableEventPtr
|
||||
#include "nsTransitionManager.h" // For CSSTransition
|
||||
#include "PendingAnimationTracker.h" // For PendingAnimationTracker
|
||||
|
@ -168,6 +173,8 @@ void Animation::SetEffectNoUpdate(AnimationEffect* aEffect) {
|
|||
ReschedulePendingTasks();
|
||||
}
|
||||
|
||||
MaybeScheduleReplacementCheck();
|
||||
|
||||
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
|
||||
}
|
||||
|
||||
|
@ -594,6 +601,124 @@ void Animation::Reverse(ErrorResult& aRv) {
|
|||
// 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:
|
||||
|
@ -650,7 +775,14 @@ void Animation::Tick() {
|
|||
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) {
|
||||
return;
|
||||
|
@ -837,7 +969,8 @@ bool Animation::ShouldBeSynchronizedWithMainThread(
|
|||
|
||||
void Animation::UpdateRelevance() {
|
||||
bool wasRelevant = mIsRelevant;
|
||||
mIsRelevant = HasCurrentEffect() || IsInEffect();
|
||||
mIsRelevant = mReplaceState != AnimationReplaceState::Removed &&
|
||||
(HasCurrentEffect() || IsInEffect());
|
||||
|
||||
// Notify animation observers.
|
||||
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 {
|
||||
// 0. Object-equality case
|
||||
if (&aOther == this) {
|
||||
|
@ -989,11 +1234,27 @@ void Animation::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
|
|||
|
||||
void Animation::NotifyEffectTimingUpdated() {
|
||||
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");
|
||||
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() {
|
||||
if (!IsNewlyStarted() || !mEffect) {
|
||||
return;
|
||||
|
@ -1179,7 +1440,7 @@ void Animation::ResumeAt(const TimeDuration& aReadyTime) {
|
|||
|
||||
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
|
||||
// to notify observers.
|
||||
|
@ -1503,7 +1764,7 @@ void Animation::QueuePlaybackEvent(const nsAString& aName,
|
|||
|
||||
AnimationPlaybackEventInit init;
|
||||
|
||||
if (aName.EqualsLiteral("finish")) {
|
||||
if (aName.EqualsLiteral("finish") || aName.EqualsLiteral("remove")) {
|
||||
init.mCurrentTime = GetCurrentTimeAsDouble();
|
||||
}
|
||||
if (mTimeline) {
|
||||
|
|
|
@ -51,15 +51,7 @@ class Animation : public DOMEventTargetHelper,
|
|||
|
||||
public:
|
||||
explicit Animation(nsIGlobalObject* aGlobal)
|
||||
: DOMEventTargetHelper(aGlobal),
|
||||
mPlaybackRate(1.0),
|
||||
mAnimationIndex(sNextAnimationIndex++),
|
||||
mCachedChildIndex(-1),
|
||||
mPendingState(PendingState::NotPending),
|
||||
mFinishedAtLastComposeStyle(false),
|
||||
mIsRelevant(false),
|
||||
mFinishedIsResolved(false),
|
||||
mSyncWithGeometricAnimations(false) {}
|
||||
: DOMEventTargetHelper(aGlobal), mAnimationIndex(sNextAnimationIndex++) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Animation, DOMEventTargetHelper)
|
||||
|
@ -120,12 +112,14 @@ class Animation : public DOMEventTargetHelper,
|
|||
|
||||
bool Pending() const { return mPendingState != PendingState::NotPending; }
|
||||
virtual bool PendingFromJS() const { return Pending(); }
|
||||
AnimationReplaceState ReplaceState() const { return mReplaceState; }
|
||||
|
||||
virtual Promise* GetReady(ErrorResult& aRv);
|
||||
Promise* GetFinished(ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(finish);
|
||||
IMPL_EVENT_HANDLER(cancel);
|
||||
IMPL_EVENT_HANDLER(remove);
|
||||
|
||||
void Cancel(PostRestyleMode aPostRestyle = PostRestyleMode::IfNeeded);
|
||||
|
||||
|
@ -147,6 +141,9 @@ class Animation : public DOMEventTargetHelper,
|
|||
void UpdatePlaybackRate(double aPlaybackRate);
|
||||
void Reverse(ErrorResult& aRv);
|
||||
|
||||
void Persist();
|
||||
void CommitStyles(ErrorResult& aRv);
|
||||
|
||||
bool IsRunningOnCompositor() const;
|
||||
|
||||
virtual void Tick();
|
||||
|
@ -329,6 +326,25 @@ class Animation : public DOMEventTargetHelper,
|
|||
bool IsRelevant() const { return mIsRelevant; }
|
||||
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.
|
||||
*/
|
||||
|
@ -366,6 +382,8 @@ class Animation : public DOMEventTargetHelper,
|
|||
const nsCSSPropertyIDSet& aPropertiesToSkip);
|
||||
|
||||
void NotifyEffectTimingUpdated();
|
||||
void NotifyEffectPropertiesUpdated();
|
||||
void NotifyEffectTargetUpdated();
|
||||
void NotifyGeometricAnimationsStartingThisFrame();
|
||||
|
||||
/**
|
||||
|
@ -481,6 +499,9 @@ class Animation : public DOMEventTargetHelper,
|
|||
return GetCurrentTimeForHoldTime(Nullable<TimeDuration>());
|
||||
}
|
||||
|
||||
void ScheduleReplacementCheck();
|
||||
void MaybeScheduleReplacementCheck();
|
||||
|
||||
// Earlier side of the elapsed time range reported in CSS Animations and CSS
|
||||
// Transitions events.
|
||||
//
|
||||
|
@ -527,7 +548,7 @@ class Animation : public DOMEventTargetHelper,
|
|||
Nullable<TimeDuration> mHoldTime; // Animation timescale
|
||||
Nullable<TimeDuration> mPendingReadyTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mPreviousCurrentTime; // Animation timescale
|
||||
double mPlaybackRate;
|
||||
double mPlaybackRate = 1.0;
|
||||
Maybe<double> mPendingPlaybackRate;
|
||||
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
// (see TriggerOnNextTick for details).
|
||||
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
|
||||
// getAnimations() list.
|
||||
bool mIsRelevant;
|
||||
bool mIsRelevant = false;
|
||||
|
||||
// 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
|
||||
// in that case mFinished is immediately reset to represent a new current
|
||||
// finished promise.
|
||||
bool mFinishedIsResolved;
|
||||
bool mFinishedIsResolved = false;
|
||||
|
||||
// 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
|
||||
// the main thread.
|
||||
bool mSyncWithGeometricAnimations;
|
||||
bool mSyncWithGeometricAnimations = false;
|
||||
|
||||
RefPtr<MicroTaskRunnable> mFinishNotificationTask;
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#ifndef 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/RefPtr.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
|
||||
|
||||
#endif // mozilla_AnimationTarget_h
|
||||
|
|
|
@ -188,13 +188,6 @@ void DocumentTimeline::MostRecentRefreshTimeUpdated() {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -394,6 +394,26 @@ class EffectCompositeOrderComparator {
|
|||
};
|
||||
} // 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(
|
||||
const dom::Element* aElement, PseudoStyleType aPseudoType,
|
||||
CascadeLevel aCascadeLevel, RawServoAnimationValueMap* aAnimationValues) {
|
||||
|
@ -417,16 +437,8 @@ bool EffectCompositor::GetServoAnimationRule(
|
|||
}
|
||||
sortedEffectList.Sort(EffectCompositeOrderComparator());
|
||||
|
||||
// If multiple animations affect the same property, animations with higher
|
||||
// composite order (priority) override or add or animations with lower
|
||||
// priority.
|
||||
const nsCSSPropertyIDSet propertiesToSkip =
|
||||
aCascadeLevel == CascadeLevel::Animations
|
||||
? effectSet->PropertiesForAnimationsLevel().Inverse()
|
||||
: effectSet->PropertiesForAnimationsLevel();
|
||||
for (KeyframeEffect* effect : sortedEffectList) {
|
||||
effect->GetAnimation()->ComposeStyle(*aAnimationValues, propertiesToSkip);
|
||||
}
|
||||
ComposeSortedEffects(sortedEffectList, effectSet, aCascadeLevel,
|
||||
aAnimationValues);
|
||||
|
||||
MOZ_ASSERT(effectSet == EffectSet::GetEffectSet(aElement, aPseudoType),
|
||||
"EffectSet should not change while composing style");
|
||||
|
@ -434,6 +446,59 @@ bool EffectCompositor::GetServoAnimationRule(
|
|||
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(
|
||||
dom::Element* aElement, PseudoStyleType aPseudoType) {
|
||||
if (aPseudoType == PseudoStyleType::NotPseudo) {
|
||||
|
@ -862,4 +927,53 @@ bool EffectCompositor::PreTraverseInSubtree(ServoTraversalFlags aFlags,
|
|||
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
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
#define mozilla_EffectCompositor_h
|
||||
|
||||
#include "mozilla/AnimationPerformanceWarning.h"
|
||||
#include "mozilla/AnimationTarget.h"
|
||||
#include "mozilla/EnumeratedArray.h"
|
||||
#include "mozilla/HashTable.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/OwningNonNull.h"
|
||||
#include "mozilla/PseudoElementHashEntry.h"
|
||||
|
@ -38,6 +40,7 @@ struct NonOwningAnimationTarget;
|
|||
namespace dom {
|
||||
class Animation;
|
||||
class Element;
|
||||
class KeyframeEffect;
|
||||
} // namespace dom
|
||||
|
||||
class EffectCompositor {
|
||||
|
@ -116,8 +119,9 @@ class EffectCompositor {
|
|||
dom::Element* aElement,
|
||||
PseudoStyleType aPseudoType);
|
||||
|
||||
// Get animation rule for stylo. This is an equivalent of GetAnimationRule
|
||||
// and will be called from servo side.
|
||||
// Get the animation rule for the appropriate level of the cascade for
|
||||
// a (pseudo-)element. Called from the Servo side.
|
||||
//
|
||||
// The animation rule is stored in |RawServoAnimationValueMap|.
|
||||
// We need to be careful while doing any modification because it may cause
|
||||
// some thread-safe issues.
|
||||
|
@ -126,6 +130,15 @@ class EffectCompositor {
|
|||
CascadeLevel aCascadeLevel,
|
||||
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;
|
||||
|
||||
static bool HasAnimationsForCompositor(const nsIFrame* aFrame,
|
||||
|
@ -199,6 +212,12 @@ class EffectCompositor {
|
|||
// at aElement.
|
||||
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.
|
||||
//
|
||||
// If |aPseudoType| is ::after, ::before or ::marker, returns the generated
|
||||
|
@ -239,6 +258,8 @@ class EffectCompositor {
|
|||
mElementsToRestyle;
|
||||
|
||||
bool mIsInPreTraverse = false;
|
||||
|
||||
HashSet<OwningAnimationTarget> mElementsToReduce;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -317,7 +317,7 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
|
|||
|
||||
nsCSSPropertyIDSet properties;
|
||||
|
||||
if (!IsInEffect() && !IsCurrent()) {
|
||||
if (!mAnimation || !mAnimation->IsRelevant()) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
@ -342,14 +342,14 @@ nsCSSPropertyIDSet KeyframeEffect::GetPropertiesForCompositor(
|
|||
return properties;
|
||||
}
|
||||
|
||||
bool KeyframeEffect::HasAnimationOfPropertySet(
|
||||
const nsCSSPropertyIDSet& aPropertySet) const {
|
||||
nsCSSPropertyIDSet KeyframeEffect::GetPropertySet() const {
|
||||
nsCSSPropertyIDSet result;
|
||||
|
||||
for (const AnimationProperty& property : mProperties) {
|
||||
if (aPropertySet.HasProperty(property.mProperty)) {
|
||||
return true;
|
||||
}
|
||||
result.AddProperty(property.mProperty);
|
||||
}
|
||||
return false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
@ -418,6 +418,10 @@ void KeyframeEffect::UpdateProperties(const ComputedStyle* aStyle) {
|
|||
|
||||
MarkCascadeNeedsUpdate();
|
||||
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectPropertiesUpdated();
|
||||
}
|
||||
|
||||
RequestRestyle(EffectCompositor::RestyleType::Layer);
|
||||
}
|
||||
|
||||
|
@ -575,13 +579,10 @@ void KeyframeEffect::ComposeStyle(RawServoAnimationValueMap& aComposeResult,
|
|||
if (HasPropertiesThatMightAffectOverflow()) {
|
||||
nsPresContext* presContext =
|
||||
nsContentUtils::GetContextForContent(mTarget->mElement);
|
||||
if (presContext) {
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
if (presContext && effectSet) {
|
||||
TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
|
||||
EffectSet* effectSet =
|
||||
EffectSet::GetEffectSet(mTarget->mElement, mTarget->mPseudoType);
|
||||
MOZ_ASSERT(effectSet,
|
||||
"ComposeStyle should only be called on an effect "
|
||||
"that is part of an effect set");
|
||||
effectSet->UpdateLastOverflowAnimationSyncTime(now);
|
||||
}
|
||||
}
|
||||
|
@ -792,7 +793,9 @@ void KeyframeEffect::UpdateTargetRegistration() {
|
|||
// something calls Animation::UpdateRelevance. Whenever our timing changes,
|
||||
// we should be notifying our Animation before calling this, so
|
||||
// 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");
|
||||
|
||||
if (isRelevant && !mInEffectSet) {
|
||||
|
@ -999,6 +1002,10 @@ void KeyframeEffect::SetTarget(
|
|||
mAnimation->ReschedulePendingTasks();
|
||||
}
|
||||
}
|
||||
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectTargetUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
static void CreatePropertyValue(
|
||||
|
@ -1728,6 +1735,11 @@ bool KeyframeEffect::ContainsAnimatedScale(const nsIFrame* aFrame) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!mAnimation ||
|
||||
mAnimation->ReplaceState() == AnimationReplaceState::Removed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const AnimationProperty& prop : mProperties) {
|
||||
if (prop.mProperty != eCSSProperty_transform &&
|
||||
prop.mProperty != eCSSProperty_scale &&
|
||||
|
|
|
@ -183,9 +183,15 @@ class KeyframeEffect : public AnimationEffect {
|
|||
void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
|
||||
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
|
||||
// of whether any property in the set is overridden by !important rule.
|
||||
bool HasAnimationOfPropertySet(const nsCSSPropertyIDSet& aPropertySet) const;
|
||||
// of whether any property in the set is overridden by an !important rule.
|
||||
bool HasAnimationOfPropertySet(const nsCSSPropertyIDSet& aPropertySet) const {
|
||||
return GetPropertySet().Intersects(aPropertySet);
|
||||
}
|
||||
|
||||
// GetEffectiveAnimationOfProperty returns AnimationProperty corresponding
|
||||
// to a given CSS property if the effect includes the property and the
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
[DEFAULT]
|
||||
prefs =
|
||||
dom.animations-api.autoremove.enabled=true
|
||||
dom.animations-api.compositing.enabled=true
|
||||
gfx.omta.background-color=true
|
||||
layout.css.individual-transform.enabled=true
|
||||
|
|
|
@ -578,5 +578,74 @@ promise_test(t => {
|
|||
});
|
||||
}, "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();
|
||||
</script>
|
||||
|
|
|
@ -14,6 +14,7 @@ support-files =
|
|||
chrome/file_animate_xrays.html
|
||||
mozilla/xhr_doc.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_get_animations.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_deferred_start.html]
|
||||
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_get_animations.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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 {
|
||||
return mAttrs.GetAttr(aAttr);
|
||||
}
|
||||
|
|
|
@ -346,9 +346,9 @@ static nsresult PutToClipboard(
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsCopySupport::HTMLCopy(Selection* aSel, Document* aDoc,
|
||||
int16_t aClipboardID,
|
||||
bool aWithRubyAnnotation) {
|
||||
nsresult nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
|
||||
Selection* aSel, Document* aDoc, int16_t aClipboardID,
|
||||
bool aWithRubyAnnotation) {
|
||||
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
|
||||
|
||||
uint32_t additionalFlags = nsIDocumentEncoder::SkipInvisibleContent;
|
||||
|
@ -915,8 +915,8 @@ bool nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
|
|||
// selection is inside a same ruby container. But we really should
|
||||
// expose the full functionality in browser. See bug 1130891.
|
||||
bool withRubyAnnotation = IsSelectionInsideRuby(sel);
|
||||
// call the copy code
|
||||
nsresult rv = HTMLCopy(sel, doc, aClipboardType, withRubyAnnotation);
|
||||
nsresult rv = EncodeDocumentWithContextAndPutToClipboard(
|
||||
sel, doc, aClipboardType, withRubyAnnotation);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -33,9 +33,9 @@ class nsCopySupport {
|
|||
/**
|
||||
* @param aDoc Needs to be not nullptr.
|
||||
*/
|
||||
static nsresult HTMLCopy(mozilla::dom::Selection* aSel,
|
||||
mozilla::dom::Document* aDoc, int16_t aClipboardID,
|
||||
bool aWithRubyAnnotation);
|
||||
static nsresult EncodeDocumentWithContextAndPutToClipboard(
|
||||
mozilla::dom::Selection* aSel, mozilla::dom::Document* aDoc,
|
||||
int16_t aClipboardID, bool aWithRubyAnnotation);
|
||||
|
||||
// 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
|
||||
|
@ -47,8 +47,7 @@ class nsCopySupport {
|
|||
static nsresult ImageCopy(nsIImageLoadingContent* aImageElement,
|
||||
nsILoadContext* aLoadContext, int32_t aCopyFlags);
|
||||
|
||||
// Get the selection as a transferable. Similar to HTMLCopy except does
|
||||
// not deal with the clipboard.
|
||||
// Get the selection as a transferable.
|
||||
// @param aSelection Can be nullptr.
|
||||
// @param aDocument 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);
|
||||
|
||||
bool IsVisibleNode(nsINode* aNode) {
|
||||
MOZ_ASSERT(aNode, "null node");
|
||||
|
||||
bool IsInvisibleNodeAndShouldBeSkipped(nsINode& aNode) const {
|
||||
if (mFlags & SkipInvisibleContent) {
|
||||
// Treat the visibility of the ShadowRoot as if it were
|
||||
// the host content.
|
||||
|
@ -95,35 +93,38 @@ class nsDocumentEncoder : public nsIDocumentEncoder {
|
|||
// FIXME(emilio): I suspect instead of this a bunch of the GetParent()
|
||||
// calls here should be doing GetFlattenedTreeParent, then this condition
|
||||
// should be unreachable...
|
||||
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode)) {
|
||||
aNode = shadowRoot->GetHost();
|
||||
nsINode* node{&aNode};
|
||||
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(node)) {
|
||||
node = shadowRoot->GetHost();
|
||||
}
|
||||
|
||||
if (aNode->IsContent()) {
|
||||
nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
|
||||
if (node->IsContent()) {
|
||||
nsIFrame* frame = node->AsContent()->GetPrimaryFrame();
|
||||
if (!frame) {
|
||||
if (aNode->IsElement() && aNode->AsElement()->IsDisplayContents()) {
|
||||
return true;
|
||||
if (node->IsElement() && node->AsElement()->IsDisplayContents()) {
|
||||
return false;
|
||||
}
|
||||
if (aNode->IsText()) {
|
||||
if (node->IsText()) {
|
||||
// We have already checked that our parent is visible.
|
||||
//
|
||||
// FIXME(emilio): Text not assigned to a <slot> in Shadow DOM should
|
||||
// probably return false...
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
if (aNode->IsHTMLElement(nsGkAtoms::rp)) {
|
||||
if (node->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;
|
||||
return true;
|
||||
}
|
||||
bool isVisible = frame->StyleVisibility()->IsVisible();
|
||||
if (!isVisible && aNode->IsText()) return false;
|
||||
if (!isVisible && node->IsText()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool IncludeInContext(nsINode* aNode);
|
||||
|
@ -349,7 +350,7 @@ nsresult nsDocumentEncoder::SerializeNodeStart(nsINode& aOriginalNode,
|
|||
}
|
||||
}
|
||||
|
||||
if (!IsVisibleNode(&aOriginalNode)) {
|
||||
if (IsInvisibleNodeAndShouldBeSkipped(aOriginalNode)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -411,7 +412,7 @@ nsresult nsDocumentEncoder::SerializeNodeEnd(nsINode& aNode, nsAString& aStr) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!IsVisibleNode(&aNode)) {
|
||||
if (IsInvisibleNodeAndShouldBeSkipped(aNode)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -429,11 +430,12 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
|
|||
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};
|
||||
nsINode* maybeFixedNode =
|
||||
&fixupNodeDeterminer.GetFixupNodeFallBackToOriginalNode();
|
||||
|
@ -449,6 +451,8 @@ nsresult nsDocumentEncoder::SerializeToStringRecursive(nsINode* aNode,
|
|||
}
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
if (!aDontSerializeRoot) {
|
||||
int32_t endOffset = -1;
|
||||
if (aMaxLength > 0) {
|
||||
|
@ -585,7 +589,9 @@ nsresult nsDocumentEncoder::SerializeRangeNodes(nsRange* const aRange,
|
|||
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
|
||||
NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
|
||||
|
||||
if (!IsVisibleNode(aNode)) return NS_OK;
|
||||
if (IsInvisibleNodeAndShouldBeSkipped(*aNode)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
|
@ -788,11 +794,13 @@ nsresult nsDocumentEncoder::SerializeRangeToString(nsRange* aRange,
|
|||
if (startContainer == endContainer && IsTextNode(startContainer)) {
|
||||
if (mFlags & SkipInvisibleContent) {
|
||||
// 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);
|
||||
if (content && !content->GetPrimaryFrame()) {
|
||||
nsIContent* parent = content->GetParent();
|
||||
if (!parent || !IsVisibleNode(parent)) return NS_OK;
|
||||
if (!parent || IsInvisibleNodeAndShouldBeSkipped(*parent)) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
rv = SerializeNodeStart(*startContainer, startOffset, endOffset,
|
||||
|
@ -847,9 +855,9 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
|||
aOutputString.Truncate();
|
||||
|
||||
nsString output;
|
||||
static const size_t bufferSize = 2048;
|
||||
static const size_t kStringBufferSizeInBytes = 2048;
|
||||
if (!mCachedBuffer) {
|
||||
mCachedBuffer = nsStringBuffer::Alloc(bufferSize).take();
|
||||
mCachedBuffer = nsStringBuffer::Alloc(kStringBufferSizeInBytes).take();
|
||||
if (NS_WARN_IF(!mCachedBuffer)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
@ -975,7 +983,7 @@ nsDocumentEncoder::EncodeToStringWithMaxLength(uint32_t aMaxLength,
|
|||
bool setOutput = false;
|
||||
// Try to cache the buffer.
|
||||
if (mCachedBuffer) {
|
||||
if (mCachedBuffer->StorageSize() == bufferSize &&
|
||||
if ((mCachedBuffer->StorageSize() == kStringBufferSizeInBytes) &&
|
||||
!mCachedBuffer->IsReadonly()) {
|
||||
mCachedBuffer->AddRef();
|
||||
} else {
|
||||
|
|
|
@ -466,7 +466,7 @@ class nsINode : public mozilla::dom::EventTarget {
|
|||
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); }
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ interface JSWindowActorChild {
|
|||
[Throws]
|
||||
readonly attribute BrowsingContext? browsingContext;
|
||||
|
||||
[Throws]
|
||||
readonly attribute nsIDocShell? docShell;
|
||||
|
||||
// NOTE: As this returns a window proxy, it may not be currently referencing
|
||||
// the document associated with this JSWindowActor. Generally prefer using
|
||||
// `document`.
|
||||
|
|
|
@ -76,7 +76,7 @@ skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
|
|||
[test_bug563329.html]
|
||||
skip-if = true # Disabled due to timeouts.
|
||||
[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_bug593959.html]
|
||||
[test_bug603008.html]
|
||||
|
|
|
@ -63,7 +63,6 @@ function forceScrollAndWait(scrollbox, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
var kExtraEvents = 5;
|
||||
var kDelta = 3;
|
||||
|
||||
function sendTouchpadScrollMotion(scrollbox, direction, ctrl, momentum, callback) {
|
||||
|
@ -91,11 +90,9 @@ function sendTouchpadScrollMotion(scrollbox, direction, ctrl, momentum, callback
|
|||
scrollbox.addEventListener("wheel", onwheel);
|
||||
|
||||
synthesizeWheel(scrollbox, 10, 10, event, win);
|
||||
// then 5 additional pixel scrolls
|
||||
// then additional pixel scroll
|
||||
event.lineOrPageDeltaY = 0;
|
||||
for (let i = 1; i <= kExtraEvents; ++i) {
|
||||
synthesizeWheel(scrollbox, 10, 10, event, win);
|
||||
}
|
||||
synthesizeWheel(scrollbox, 10, 10, event, win);
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
@ -147,7 +144,7 @@ function runTest() {
|
|||
let postfix = isMomentum ? ", even after releasing the touchpad" : "";
|
||||
// Normal scroll: scroll
|
||||
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);
|
||||
} else {
|
||||
if (!isMomentum) {
|
||||
|
@ -155,7 +152,7 @@ function runTest() {
|
|||
is(scrollbox.scrollTop, scrollTopBefore, "Ctrl-scrolling shouldn't scroll while the user is touching the touchpad");
|
||||
} else {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2653,14 +2653,6 @@ nsresult nsGenericHTMLElement::NewURIFromString(const nsAString& aURISpec,
|
|||
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,
|
||||
mozilla::ErrorResult& aError) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
if (!IsRendered(*this)) {
|
||||
if (!IsRendered()) {
|
||||
GetTextContentInternal(aValue, aError);
|
||||
} else {
|
||||
nsRange::GetInnerTextNoFlush(aValue, aError, this);
|
||||
|
|
|
@ -106,6 +106,14 @@ BrowsingContext* JSWindowActorChild::GetBrowsingContext(ErrorResult& aRv) {
|
|||
return mManager->BrowsingContext();
|
||||
}
|
||||
|
||||
nsIDocShell* JSWindowActorChild::GetDocShell(ErrorResult& aRv) {
|
||||
if (BrowsingContext* bc = GetBrowsingContext(aRv)) {
|
||||
return bc->GetDocShell();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow(
|
||||
ErrorResult& aRv) {
|
||||
if (BrowsingContext* bc = GetBrowsingContext(aRv)) {
|
||||
|
|
|
@ -51,6 +51,7 @@ class JSWindowActorChild final : public JSWindowActor {
|
|||
void AfterDestroy();
|
||||
Document* GetDocument(ErrorResult& aRv);
|
||||
BrowsingContext* GetBrowsingContext(ErrorResult& aRv);
|
||||
nsIDocShell* GetDocShell(ErrorResult& aRv);
|
||||
Nullable<WindowProxyHolder> GetContentWindow(ErrorResult& aRv);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -7,7 +7,5 @@ support-files =
|
|||
[browser_domainPolicy.js]
|
||||
[browser_memory_distribution_telemetry.js]
|
||||
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]
|
||||
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) {
|
||||
#if defined(XP_LINUX)
|
||||
return "pulse";
|
||||
#elif defined(XP_MACOSX)
|
||||
#if defined(XP_MACOSX)
|
||||
return "audiounit";
|
||||
#elif defined(XP_WIN)
|
||||
return "wasapi";
|
||||
#elif defined(ANDROID)
|
||||
return "opensl";
|
||||
#elif defined(__OpenBSD__)
|
||||
return "sndio";
|
||||
#else
|
||||
return "pulse";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -516,8 +516,8 @@ bool SMILCSSValueType::SetPropertyValues(const SMILValue& aValue,
|
|||
|
||||
bool changed = false;
|
||||
for (const auto& value : wrapper->mServoValues) {
|
||||
changed |=
|
||||
Servo_DeclarationBlock_SetPropertyToAnimationValue(aDecl.Raw(), value);
|
||||
changed |= Servo_DeclarationBlock_SetPropertyToAnimationValue(aDecl.Raw(),
|
||||
value, {});
|
||||
}
|
||||
|
||||
return changed;
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
|
||||
enum AnimationPlayState { "idle", "running", "paused", "finished" };
|
||||
|
||||
[Constructor (optional AnimationEffect? effect = null,
|
||||
optional AnimationTimeline? timeline)]
|
||||
enum AnimationReplaceState { "active", "removed", "persisted" };
|
||||
|
||||
[Constructor(optional AnimationEffect? effect = null,
|
||||
optional AnimationTimeline? timeline)]
|
||||
interface Animation : EventTarget {
|
||||
attribute DOMString id;
|
||||
[Func="Document::IsWebAnimationsEnabled", Pure]
|
||||
|
@ -30,22 +32,30 @@ interface Animation : EventTarget {
|
|||
readonly attribute AnimationPlayState playState;
|
||||
[BinaryName="pendingFromJS"]
|
||||
readonly attribute boolean pending;
|
||||
[Pref="dom.animations-api.autoremove.enabled"]
|
||||
readonly attribute AnimationReplaceState replaceState;
|
||||
[Func="Document::IsWebAnimationsEnabled", Throws]
|
||||
readonly attribute Promise<Animation> ready;
|
||||
[Func="Document::IsWebAnimationsEnabled", Throws]
|
||||
readonly attribute Promise<Animation> finished;
|
||||
attribute EventHandler onfinish;
|
||||
attribute EventHandler oncancel;
|
||||
void cancel ();
|
||||
[Pref="dom.animations-api.autoremove.enabled"]
|
||||
attribute EventHandler onremove;
|
||||
void cancel();
|
||||
[Throws]
|
||||
void finish ();
|
||||
void finish();
|
||||
[Throws, BinaryName="playFromJS"]
|
||||
void play ();
|
||||
void play();
|
||||
[Throws, BinaryName="pauseFromJS"]
|
||||
void pause ();
|
||||
void pause();
|
||||
void updatePlaybackRate (double playbackRate);
|
||||
[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
|
||||
|
|
|
@ -15,6 +15,6 @@ dictionary DocumentTimelineOptions {
|
|||
};
|
||||
|
||||
[Func="Document::AreWebAnimationsTimelinesEnabled",
|
||||
Constructor (optional DocumentTimelineOptions options)]
|
||||
Constructor(optional DocumentTimelineOptions options)]
|
||||
interface DocumentTimeline : AnimationTimeline {
|
||||
};
|
||||
|
|
|
@ -24,18 +24,18 @@ dictionary KeyframeEffectOptions : EffectTiming {
|
|||
// processing on the `keyframes` object.
|
||||
[Func="Document::IsWebAnimationsEnabled",
|
||||
RunConstructorInCallerCompartment,
|
||||
Constructor ((Element or CSSPseudoElement)? target,
|
||||
object? keyframes,
|
||||
optional (unrestricted double or KeyframeEffectOptions) options),
|
||||
Constructor (KeyframeEffect source)]
|
||||
Constructor((Element or CSSPseudoElement)? target,
|
||||
object? keyframes,
|
||||
optional (unrestricted double or KeyframeEffectOptions) options),
|
||||
Constructor(KeyframeEffect source)]
|
||||
interface KeyframeEffect : AnimationEffect {
|
||||
attribute (Element or CSSPseudoElement)? target;
|
||||
[Pref="dom.animations-api.compositing.enabled"]
|
||||
attribute IterationCompositeOperation iterationComposite;
|
||||
[Pref="dom.animations-api.compositing.enabled"]
|
||||
attribute CompositeOperation composite;
|
||||
[Throws] sequence<object> getKeyframes ();
|
||||
[Throws] void setKeyframes (object? keyframes);
|
||||
[Throws] sequence<object> getKeyframes();
|
||||
[Throws] void setKeyframes(object? keyframes);
|
||||
};
|
||||
|
||||
// Non-standard extensions
|
||||
|
|
|
@ -260,7 +260,7 @@ class MOZ_RAII JS_PUBLIC_API AutoDebuggerJobQueueInterruption {
|
|||
enum class PromiseRejectionHandlingState { Unhandled, Handled };
|
||||
|
||||
typedef void (*PromiseRejectionTrackerCallback)(
|
||||
JSContext* cx, JS::HandleObject promise,
|
||||
JSContext* cx, bool mutedErrors, JS::HandleObject promise,
|
||||
JS::PromiseRejectionHandlingState state, void* data);
|
||||
|
||||
/**
|
||||
|
|
|
@ -1137,7 +1137,7 @@ static bool TrackUnhandledRejections(JSContext* cx, JS::HandleObject promise,
|
|||
}
|
||||
|
||||
static void ForwardingPromiseRejectionTrackerCallback(
|
||||
JSContext* cx, JS::HandleObject promise,
|
||||
JSContext* cx, bool mutedErrors, JS::HandleObject promise,
|
||||
JS::PromiseRejectionHandlingState state, void* data) {
|
||||
AutoReportException are(cx);
|
||||
|
||||
|
|
|
@ -637,9 +637,15 @@ void JSRuntime::addUnhandledRejectedPromise(JSContext* cx,
|
|||
return;
|
||||
}
|
||||
|
||||
bool mutedErrors = false;
|
||||
if (JSScript* script = cx->currentScript()) {
|
||||
mutedErrors = script->mutedErrors();
|
||||
}
|
||||
|
||||
void* data = cx->promiseRejectionTrackerCallbackData;
|
||||
cx->promiseRejectionTrackerCallback(
|
||||
cx, promise, JS::PromiseRejectionHandlingState::Unhandled, data);
|
||||
cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Unhandled,
|
||||
data);
|
||||
}
|
||||
|
||||
void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx,
|
||||
|
@ -649,9 +655,15 @@ void JSRuntime::removeUnhandledRejectedPromise(JSContext* cx,
|
|||
return;
|
||||
}
|
||||
|
||||
bool mutedErrors = false;
|
||||
if (JSScript* script = cx->currentScript()) {
|
||||
mutedErrors = script->mutedErrors();
|
||||
}
|
||||
|
||||
void* data = cx->promiseRejectionTrackerCallbackData;
|
||||
cx->promiseRejectionTrackerCallback(
|
||||
cx, promise, JS::PromiseRejectionHandlingState::Handled, data);
|
||||
cx, mutedErrors, promise, JS::PromiseRejectionHandlingState::Handled,
|
||||
data);
|
||||
}
|
||||
|
||||
mozilla::non_crypto::XorShift128PlusRNG& JSRuntime::randomKeyGenerator() {
|
||||
|
|
|
@ -225,6 +225,10 @@ static ContentMap& GetContentMap() {
|
|||
template <typename TestType>
|
||||
static bool HasMatchingAnimations(EffectSet& aEffects, TestType&& aTest) {
|
||||
for (KeyframeEffect* effect : aEffects) {
|
||||
if (!effect->GetAnimation() || !effect->GetAnimation()->IsRelevant()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (aTest(*effect, aEffects)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -263,8 +267,7 @@ bool nsLayoutUtils::HasAnimationOfPropertySet(
|
|||
return HasMatchingAnimations(
|
||||
aFrame, aPropertySet,
|
||||
[&aPropertySet](KeyframeEffect& aEffect, const EffectSet&) {
|
||||
return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
|
||||
aEffect.HasAnimationOfPropertySet(aPropertySet);
|
||||
return aEffect.HasAnimationOfPropertySet(aPropertySet);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -294,8 +297,7 @@ bool nsLayoutUtils::HasAnimationOfPropertySet(
|
|||
return HasMatchingAnimations(
|
||||
*aEffectSet,
|
||||
[&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
|
||||
return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
|
||||
aEffect.HasAnimationOfPropertySet(aPropertySet);
|
||||
return aEffect.HasAnimationOfPropertySet(aPropertySet);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -305,8 +307,7 @@ bool nsLayoutUtils::HasEffectiveAnimation(
|
|||
return HasMatchingAnimations(
|
||||
aFrame, aPropertySet,
|
||||
[&aPropertySet](KeyframeEffect& aEffect, const EffectSet& aEffectSet) {
|
||||
return (aEffect.IsInEffect() || aEffect.IsCurrent()) &&
|
||||
aEffect.HasEffectiveAnimationOfPropertySet(aPropertySet,
|
||||
return aEffect.HasEffectiveAnimationOfPropertySet(aPropertySet,
|
||||
aEffectSet);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#include "nsComponentManagerUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/DocumentInlines.h"
|
||||
#include "nsIXULRuntime.h"
|
||||
#include "jsapi.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) {
|
||||
MOZ_ASSERT(!nsContentUtils::GetCurrentJSContext(),
|
||||
"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) {
|
||||
// This is the FlushType::Style case.
|
||||
|
||||
|
|
|
@ -2747,8 +2747,8 @@ nsresult nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(
|
|||
nsCOMPtr<Document> aDoc = presShell->GetDocument();
|
||||
|
||||
if (aDoc && aSel && !aSel->IsCollapsed()) {
|
||||
return nsCopySupport::HTMLCopy(aSel, aDoc, nsIClipboard::kSelectionCache,
|
||||
false);
|
||||
return nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
|
||||
aSel, aDoc, nsIClipboard::kSelectionCache, false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
@ -2762,8 +2762,9 @@ int16_t AutoCopyListener::sClipboardID = -1;
|
|||
* What we do now:
|
||||
* On every selection change, we copy to the clipboard anew, creating a
|
||||
* HTML buffer, a transferable, an nsISupportsString and
|
||||
* a huge mess every time. This is basically what nsCopySupport::HTMLCopy()
|
||||
* does to move the selection into the clipboard for Edit->Copy.
|
||||
* a huge mess every time. This is basically what
|
||||
* 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:
|
||||
* Create a singleton transferable with our own magic converter. When selection
|
||||
|
@ -2830,8 +2831,10 @@ void AutoCopyListener::OnSelectionChange(Document* aDocument,
|
|||
return;
|
||||
}
|
||||
|
||||
// Call the copy code.
|
||||
DebugOnly<nsresult> rv =
|
||||
nsCopySupport::HTMLCopy(&aSelection, aDocument, sClipboardID, false);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "nsCopySupport::HTMLCopy() failed");
|
||||
nsCopySupport::EncodeDocumentWithContextAndPutToClipboard(
|
||||
&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.
|
||||
|
||||
// TODO(heycam): Handle these elsewhere.
|
||||
struct RawServoAnimationValueTable;
|
||||
struct RawServoAnimationValueMap;
|
||||
|
||||
#endif // mozilla_ServoBindingTypes_h
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
// to just generate the forward declaration.
|
||||
|
||||
SERVO_BOXED_TYPE(StyleSet, RawServoStyleSet)
|
||||
SERVO_BOXED_TYPE(AnimationValueMap, RawServoAnimationValueMap)
|
||||
SERVO_BOXED_TYPE(AuthorStyles, RawServoAuthorStyles)
|
||||
SERVO_BOXED_TYPE(SelectorList, RawServoSelectorList)
|
||||
SERVO_BOXED_TYPE(SharedMemoryBuilder, RawServoSharedMemoryBuilder)
|
||||
|
|
|
@ -127,6 +127,19 @@ VARCACHE_PREF(
|
|||
// 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?
|
||||
#ifdef RELEASE_OR_BETA
|
||||
# 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.javascript", 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.res", false);
|
||||
pref("network.protocol-handler.external.shell", false);
|
||||
|
|
|
@ -7,8 +7,6 @@
|
|||
#include "PKCS11ModuleDB.h"
|
||||
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsCRTGlue.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsNSSCertHelper.h"
|
||||
#include "nsNSSComponent.h"
|
||||
|
@ -63,32 +61,6 @@ PKCS11ModuleDB::DeleteModule(const nsAString& aModuleName) {
|
|||
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.
|
||||
NS_IMETHODIMP
|
||||
PKCS11ModuleDB::AddModule(const nsAString& aModuleName,
|
||||
|
@ -131,23 +103,6 @@ PKCS11ModuleDB::AddModule(const nsAString& aModuleName,
|
|||
if (srv != SECSuccess) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
|
||||
#include "nsIPKCS11ModuleDB.h"
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
|
@ -31,9 +29,6 @@ class PKCS11ModuleDB : public nsIPKCS11ModuleDB {
|
|||
virtual ~PKCS11ModuleDB() {}
|
||||
};
|
||||
|
||||
void GetModuleNameForTelemetry(/*in*/ const SECMODModule* module,
|
||||
/*out*/ nsString& result);
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "EnterpriseRoots.h"
|
||||
#include "ExtendedValidation.h"
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "PKCS11ModuleDB.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "SharedSSLState.h"
|
||||
#include "cert.h"
|
||||
|
@ -1793,28 +1792,6 @@ nsresult nsNSSComponent::InitializeNSS() {
|
|||
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"));
|
||||
|
||||
{
|
||||
|
|
|
@ -50,40 +50,13 @@ function checkTestModuleExists() {
|
|||
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() {
|
||||
// Check that if we have never added the test module, that we don't find it
|
||||
// in the module list.
|
||||
checkTestModuleNotPresent();
|
||||
checkModuleTelemetry();
|
||||
|
||||
// Check that adding the test module makes it appear in the module list.
|
||||
loadPKCS11TestModule(true);
|
||||
checkModuleTelemetry(
|
||||
`${AppConstants.DLL_PREFIX}pkcs11testmodule${AppConstants.DLL_SUFFIX}`);
|
||||
let testModule = checkTestModuleExists();
|
||||
|
||||
// Check that listing the slots for the test module works.
|
||||
|
|
|
@ -5,12 +5,22 @@
|
|||
//! FFI implementations for types listed in ServoBoxedTypeList.h.
|
||||
|
||||
use crate::gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI};
|
||||
use crate::properties::animated_properties::AnimationValueMap;
|
||||
use to_shmem::SharedMemoryBuilder;
|
||||
|
||||
// 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
|
||||
// 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")]
|
||||
unsafe impl HasFFI for SharedMemoryBuilder {
|
||||
type FFIType = crate::gecko_bindings::bindings::RawServoSharedMemoryBuilder;
|
||||
|
|
|
@ -9,9 +9,7 @@
|
|||
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::sugar::ownership::{HasFFI, HasSimpleFFI};
|
||||
use itertools::{EitherOrBoth, Itertools};
|
||||
use crate::properties::{CSSWideKeyword, PropertyDeclaration};
|
||||
use crate::properties::longhands;
|
||||
|
@ -190,13 +188,6 @@ impl AnimatedProperty {
|
|||
/// composed for each TransitionProperty.
|
||||
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
|
||||
/// property in order to be interpolated with another one. When interpolating,
|
||||
/// 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.
|
||||
pub fn drain(&mut self) -> 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::media_queries::MediaList;
|
||||
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::{ComputedValues, Importance, NonCustomPropertyId};
|
||||
use style::properties::{LonghandId, LonghandIdSet, PropertyDeclarationBlock, PropertyId};
|
||||
|
@ -913,6 +913,36 @@ fn resolve_rules_for_element_with_context<'a>(
|
|||
.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]
|
||||
pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(
|
||||
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(
|
||||
declarations: &RawServoDeclarationBlock,
|
||||
property_id: PropertyId,
|
||||
|
@ -4123,19 +4175,13 @@ fn set_property(
|
|||
} else {
|
||||
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();
|
||||
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
|
||||
decls.update(source_declarations.drain(), importance, &mut updates)
|
||||
});
|
||||
true
|
||||
set_property_to_declarations(
|
||||
declarations,
|
||||
&mut source_declarations,
|
||||
before_change_closure,
|
||||
importance,
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
@ -4167,13 +4213,17 @@ pub unsafe extern "C" fn Servo_DeclarationBlock_SetProperty(
|
|||
pub unsafe extern "C" fn Servo_DeclarationBlock_SetPropertyToAnimationValue(
|
||||
declarations: &RawServoDeclarationBlock,
|
||||
animation_value: &RawServoAnimationValue,
|
||||
before_change_closure: DeclarationBlockMutationClosure,
|
||||
) -> bool {
|
||||
write_locked_arc(declarations, |decls: &mut PropertyDeclarationBlock| {
|
||||
decls.push(
|
||||
AnimationValue::as_arc(&animation_value).uncompute(),
|
||||
Importance::Normal,
|
||||
)
|
||||
})
|
||||
let mut source_declarations =
|
||||
SourcePropertyDeclaration::with_one(AnimationValue::as_arc(&animation_value).uncompute());
|
||||
|
||||
set_property_to_declarations(
|
||||
declarations,
|
||||
&mut source_declarations,
|
||||
before_change_closure,
|
||||
Importance::Normal,
|
||||
)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
|
|
|
@ -21,16 +21,12 @@ job-template:
|
|||
attributes:
|
||||
artifact_prefix: public
|
||||
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:
|
||||
by-platform:
|
||||
android.*: taskcluster/taskgraph/manifests/fennec_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
|
||||
attributes:
|
||||
artifact_map:
|
||||
by-project:
|
||||
by-release-type:
|
||||
beta|release.*|esr.*: taskcluster/taskgraph/manifests/firefox_candidates.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
|
||||
attributes:
|
||||
artifact_map:
|
||||
by-project:
|
||||
mozilla-release: taskcluster/taskgraph/manifests/fennec_candidates.yml
|
||||
mozilla-beta: taskcluster/taskgraph/manifests/fennec_candidates.yml
|
||||
by-release-type:
|
||||
beta|release.*|esr.*: taskcluster/taskgraph/manifests/fennec_candidates.yml
|
||||
default: taskcluster/taskgraph/manifests/fennec_nightly.yml
|
||||
|
|
|
@ -269,7 +269,7 @@ win32-shippable/opt:
|
|||
by-release-type:
|
||||
nightly: true
|
||||
beta: true
|
||||
release: true
|
||||
release.*: true
|
||||
esr.*: false
|
||||
default:
|
||||
by-project:
|
||||
|
@ -832,7 +832,7 @@ win32-devedition-nightly/opt:
|
|||
by-release-type:
|
||||
nightly: true
|
||||
beta: true
|
||||
release: true
|
||||
release.*: true
|
||||
default:
|
||||
by-project:
|
||||
# browser/confvars.sh looks for nightly-try
|
||||
|
|
|
@ -42,7 +42,7 @@ jobs:
|
|||
- releases/bouncer_firefox_beta.py
|
||||
release:
|
||||
- releases/bouncer_firefox_release.py
|
||||
esr60:
|
||||
esr.*:
|
||||
- releases/bouncer_firefox_esr.py
|
||||
default:
|
||||
- releases/bouncer_firefox_beta.py
|
||||
|
@ -51,6 +51,7 @@ jobs:
|
|||
mozilla-beta: LATEST_FIREFOX_RELEASED_DEVEL_VERSION
|
||||
mozilla-release: LATEST_FIREFOX_VERSION
|
||||
mozilla-esr60: FIREFOX_ESR
|
||||
mozilla-esr68: FIREFOX_ESR_NEXT
|
||||
default: LATEST_FIREFOX_DEVEL_VERSION
|
||||
products-url: https://product-details.mozilla.org/1.0/firefox_versions.json
|
||||
treeherder:
|
||||
|
|
|
@ -34,12 +34,14 @@ jobs:
|
|||
beta: [32]
|
||||
release: [145]
|
||||
esr60: [806]
|
||||
esr68: [882]
|
||||
default: []
|
||||
staging:
|
||||
by-release-type:
|
||||
beta: [32]
|
||||
release: [145]
|
||||
esr60: [806]
|
||||
esr68: [875]
|
||||
default: []
|
||||
treeherder:
|
||||
platform: firefox-release/opt
|
||||
|
@ -50,10 +52,13 @@ jobs:
|
|||
description: Schedule Firefox publishing in balrog (bz2)
|
||||
name: release-firefox_schedule_publishing_in_balrog-bz2
|
||||
shipping-product: firefox
|
||||
run-on-releases: [esr60]
|
||||
run-on-releases: [esr60, esr68]
|
||||
worker:
|
||||
product: firefox
|
||||
publish-rules: [521]
|
||||
publish-rules:
|
||||
by-release-type:
|
||||
esr60: [521]
|
||||
default: []
|
||||
blob-suffix: -bz2
|
||||
treeherder:
|
||||
platform: firefox-release/opt
|
||||
|
|
|
@ -39,12 +39,13 @@ jobs:
|
|||
by-release-type:
|
||||
beta: ["beta", "beta-localtest", "beta-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: []
|
||||
rules-to-update:
|
||||
by-release-type:
|
||||
beta: ["firefox-beta-cdntest", "firefox-beta-localtest"]
|
||||
release(-rc)?: ["firefox-release-cdntest", "firefox-release-localtest"]
|
||||
esr68: ["firefox-esr68-cdntest", "firefox-esr68-localtest"]
|
||||
esr60: ["firefox-esr60-cdntest", "firefox-esr60-localtest"]
|
||||
default: []
|
||||
platforms: ["linux", "linux64", "macosx64", "win32", "win64", "win64-aarch64"]
|
||||
|
@ -58,11 +59,14 @@ jobs:
|
|||
name: submit-toplevel-firefox-release-to-balrog-bz2
|
||||
description: Submit toplevel Firefox release to balrog
|
||||
shipping-product: firefox
|
||||
run-on-releases: [esr60]
|
||||
run-on-releases: [esr60, esr68]
|
||||
worker:
|
||||
product: firefox
|
||||
channel-names: ["esr", "esr-localtest", "esr-cdntest"]
|
||||
rules-to-update: ["esr52-cdntest", "esr52-localtest"]
|
||||
channel-names: ["esr", "esr-localtest", "esr-cdntest", "esr-localtest-next", "esr-cdntest-next"]
|
||||
rules-to-update:
|
||||
by-release-type:
|
||||
esr68: ["esr52-cdntest-next", "esr52-localtest-next"]
|
||||
esr60: ["esr52-cdntest", "esr52-localtest"]
|
||||
platforms: ["linux", "linux64", "macosx64", "win32", "win64"]
|
||||
blob-suffix: -bz2
|
||||
complete-mar-filename-pattern: '%s-%s.bz2.complete.mar'
|
||||
|
|
|
@ -79,6 +79,10 @@ jobs:
|
|||
firefox-esr-latest-ssl: installer-ssl
|
||||
firefox-esr-latest: installer
|
||||
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:
|
||||
firefox-latest-ssl: installer-ssl
|
||||
firefox-latest: installer
|
||||
|
|
|
@ -57,7 +57,7 @@ jobs:
|
|||
- releases/bouncer_firefox_beta.py
|
||||
release:
|
||||
- releases/bouncer_firefox_release.py
|
||||
esr60:
|
||||
esr.*:
|
||||
- releases/bouncer_firefox_esr.py
|
||||
default:
|
||||
- releases/bouncer_firefox_beta.py
|
||||
|
|
|
@ -52,11 +52,11 @@ jobs:
|
|||
firefox:
|
||||
bouncer-platforms: ['linux', 'linux64', 'osx', 'win', 'win64', 'win64-aarch64']
|
||||
bouncer-products:
|
||||
by-project:
|
||||
by-release-type:
|
||||
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
|
||||
mozilla-esr60: ['complete-mar', 'complete-mar-bz2', 'installer', 'installer-ssl', 'partial-mar']
|
||||
jamun: ['complete-mar', 'complete-mar-bz2', 'installer', 'installer-ssl', 'partial-mar']
|
||||
esr60: ['complete-mar', 'complete-mar-bz2', 'installer', 'installer-ssl', 'partial-mar']
|
||||
shipping-product: firefox
|
||||
treeherder:
|
||||
platform: firefox-release/opt
|
||||
|
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
firefox:
|
||||
shipping-product: firefox
|
||||
attributes:
|
||||
build_platform: linux64-snap-shippable
|
||||
build_platform: linux64-shippable
|
||||
build_type: opt
|
||||
treeherder:
|
||||
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"
|
||||
default: null
|
||||
esr60: "52.0esr"
|
||||
esr68: "68.0esr"
|
||||
default: "default"
|
||||
|
||||
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
|
||||
package-formats:
|
||||
by-release-type:
|
||||
esr60:
|
||||
esr(60|68):
|
||||
by-build-platform:
|
||||
linux.*: [mar, mar-bz2]
|
||||
linux4\b.*: [mar, mar-bz2]
|
||||
|
|
|
@ -73,7 +73,7 @@ job-template:
|
|||
- repackage/win64_signed.py
|
||||
package-formats:
|
||||
by-release-type:
|
||||
esr60:
|
||||
esr(60|68):
|
||||
by-build-platform:
|
||||
linux.*: [mar, mar-bz2]
|
||||
linux4\b.*: [mar, mar-bz2]
|
||||
|
|
|
@ -46,10 +46,10 @@
|
|||
},
|
||||
"arrow": {
|
||||
"hashes": [
|
||||
"sha256:3397e5448952e18e1295bf047014659effa5ae8da6a5371d37ff0ddc46fa6872",
|
||||
"sha256:6f54d9f016c0b7811fac9fb8c2c7fa7421d80c54dbdd75ffb12913c55db60b8a"
|
||||
"sha256:002f2315cf4c8404de737c42860441732d339bbc57fee584e2027520e055ecc1",
|
||||
"sha256:82dd5e13b733787d4eb0fef42d1ee1a99136dc1d65178f70373b3678b3181bfc"
|
||||
],
|
||||
"version": "==0.13.1"
|
||||
"version": "==0.13.2"
|
||||
},
|
||||
"asn1crypto": {
|
||||
"hashes": [
|
||||
|
@ -74,11 +74,11 @@
|
|||
},
|
||||
"awscli": {
|
||||
"hashes": [
|
||||
"sha256:b7a6e758a7d2e7230e4e21acab9f80db2fd31248333ca8575b4538a5c43ebd2c",
|
||||
"sha256:fae8839c4ddf6e7fb49543beec8d9659afd60e2fa23481ee723390f0a3a7d0f7"
|
||||
"sha256:34e7ee2bd912e6613ac064099c13e2114722d508fc35e01fd0dfc3be41ddd92c",
|
||||
"sha256:f73c11e6726a5ca25df3399762fae7f6882c71e097dc622d0e4743c9f8e84526"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.16.156"
|
||||
"version": "==1.16.161"
|
||||
},
|
||||
"backports.lzma": {
|
||||
"hashes": [
|
||||
|
@ -88,10 +88,10 @@
|
|||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:1517c52eaa3056d0e81f9a81b580d7f28440e7e1523d10a8acc8160c56be7113",
|
||||
"sha256:19d9d56fcf4f16ffea8a929bbf3c72db3458b6c1f306c04031f3166759cd62ac"
|
||||
"sha256:5e4774c106bb02f8e4639818c2f8157b8ec114a76e481e17cd3fe6955206e088",
|
||||
"sha256:cfc667e7888aad09ead8f7e32129ea90aa5c7f602531094954bf6305db74aac4"
|
||||
],
|
||||
"version": "==1.12.146"
|
||||
"version": "==1.12.151"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
|
@ -370,11 +370,11 @@
|
|||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
|
||||
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.21.0"
|
||||
"version": "==2.22.0"
|
||||
},
|
||||
"rsa": {
|
||||
"hashes": [
|
||||
|
@ -392,11 +392,11 @@
|
|||
},
|
||||
"scriptworker": {
|
||||
"hashes": [
|
||||
"sha256:44b19ef0ddfe14309ddb035e4f6e82da8d9eb3d7c3de8ed82ee74a75beefb767",
|
||||
"sha256:4b7bd567c8b511f1a87c68ac541c94b730d6f307ad86bb0af279ac30ef5867e9"
|
||||
"sha256:836181e36befcd74bb6b9457fd9336d8efa1350e77c285f0dc32bdb0ef6e4270",
|
||||
"sha256:d858c4e0dae3305dec3683458d2e879752b0a3645e9f95278b717d53d45c2809"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==23.0.4"
|
||||
"version": "==23.0.5"
|
||||
},
|
||||
"sh": {
|
||||
"hashes": [
|
||||
|
|
|
@ -367,11 +367,14 @@ Publishes signed langpacks to archive.mozilla.org
|
|||
release-update-verify
|
||||
---------------------
|
||||
Verifies the contents and package of release update MARs.
|
||||
|
||||
release-secondary-update-verify
|
||||
-------------------------------
|
||||
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
|
||||
----------------------------
|
||||
Creates configs for release-update-verify tasks
|
||||
|
@ -380,6 +383,10 @@ release-secondary-update-verify-config
|
|||
--------------------------------------
|
||||
Creates configs for release-secondary-update-verify tasks
|
||||
|
||||
release-update-verify-config-next
|
||||
---------------------------------
|
||||
Creates configs for release-update-verify-next tasks
|
||||
|
||||
release-updates-builder
|
||||
-----------------------
|
||||
Top level Balrog blob submission & patcher/update verify config updates.
|
||||
|
|
|
@ -332,7 +332,7 @@ mapping:
|
|||
checksums_path: ${path_platform}/${locale}/Firefox Setup ${version}.msi
|
||||
target.complete.mar:
|
||||
<<: *default
|
||||
description: "The main installer we ship our mobile products baked within"
|
||||
description: "Complete MAR to serve as updates"
|
||||
all_locales: true
|
||||
from:
|
||||
- mar-signing
|
||||
|
@ -341,6 +341,18 @@ mapping:
|
|||
update_balrog_manifest: true
|
||||
destinations:
|
||||
- ${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}:
|
||||
<<: *default
|
||||
description: "Partials MAR files to serve as updates"
|
||||
|
|
|
@ -315,7 +315,7 @@ def make_task_worker(config, jobs):
|
|||
signing_task_ref = "<" + str(signing_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(
|
||||
config, job, platform, locale
|
||||
)
|
||||
|
@ -329,7 +329,7 @@ def make_task_worker(config, jobs):
|
|||
'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(
|
||||
config, job, platform=platform, locale=locale)
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ def make_beetmover_checksums_worker(config, jobs):
|
|||
'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(
|
||||
config, job, platform, locale
|
||||
)
|
||||
|
|
|
@ -138,7 +138,7 @@ def make_beetmover_checksums_worker(config, jobs):
|
|||
'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(
|
||||
config, job, platform, locales
|
||||
)
|
||||
|
|
|
@ -358,7 +358,7 @@ def make_task_worker(config, jobs):
|
|||
locale = job["attributes"].get("locale")
|
||||
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(
|
||||
config, job, platform, locale)
|
||||
else:
|
||||
|
@ -373,7 +373,7 @@ def make_task_worker(config, jobs):
|
|||
'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(
|
||||
config, job, platform=platform, locale=locale)
|
||||
|
||||
|
@ -412,7 +412,7 @@ def make_partials_artifacts(config, jobs):
|
|||
partials_info = get_partials_info_from_params(
|
||||
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(
|
||||
generate_beetmover_partials_artifact_map(
|
||||
config, job, partials_info, platform=platform, locale=locale))
|
||||
|
|
|
@ -137,7 +137,7 @@ def make_beetmover_checksums_worker(config, jobs):
|
|||
raise NotImplementedError(
|
||||
"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,
|
||||
job, platform, locale)
|
||||
else:
|
||||
|
@ -149,7 +149,7 @@ def make_beetmover_checksums_worker(config, jobs):
|
|||
'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(
|
||||
config, job, platform=platform)
|
||||
|
||||
|
|
|
@ -131,7 +131,8 @@ def make_task_worker(config, jobs):
|
|||
**{'release-level': config.params.release_level()}
|
||||
)
|
||||
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
|
||||
|
|
|
@ -125,7 +125,7 @@ def make_task_worker(config, jobs):
|
|||
|
||||
platform = job["attributes"]["build_platform"]
|
||||
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(
|
||||
config, job, platform, locale,
|
||||
)
|
||||
|
@ -139,7 +139,7 @@ def make_task_worker(config, jobs):
|
|||
'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(
|
||||
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
|
||||
|
||||
# amend artifactMap entries as well
|
||||
if should_use_artifact_map(backup_platform, config.params['project']):
|
||||
if should_use_artifact_map(backup_platform):
|
||||
platform_mapping = {
|
||||
'linux64': 'linux-x86_64',
|
||||
'linux': 'linux-i686',
|
||||
|
|
|
@ -156,7 +156,7 @@ def make_task_worker(config, jobs):
|
|||
|
||||
platform = job["attributes"]["build_platform"]
|
||||
# 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(
|
||||
config, job, platform=None, locale=None
|
||||
)
|
||||
|
@ -168,7 +168,7 @@ def make_task_worker(config, jobs):
|
|||
worker['upstream-artifacts'] = upstream_artifacts
|
||||
|
||||
# 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(
|
||||
config, job, platform=platform)
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ transforms = TransformSequence()
|
|||
def add_command(config, tasks):
|
||||
config_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
|
||||
|
||||
for task in tasks:
|
||||
|
|
|
@ -33,6 +33,8 @@ INCLUDE_VERSION_REGEXES = {
|
|||
"devedition_hack": r"'^((?!58\.0b1$)\d+\.\d+(b\d+)?)$'",
|
||||
# Same as nonbeta, except for the esr suffix
|
||||
"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 = {
|
||||
|
|
|
@ -427,8 +427,10 @@ def generate_beetmover_upstream_artifacts(config, job, platform, locale=None, de
|
|||
resolve_keyed_by(
|
||||
job, 'attributes.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']))
|
||||
upstream_artifacts = list()
|
||||
|
@ -469,6 +471,11 @@ def generate_beetmover_upstream_artifacts(config, job, platform, locale=None, de
|
|||
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:
|
||||
continue
|
||||
|
||||
|
@ -556,8 +563,10 @@ def generate_beetmover_artifact_map(config, job, **kwargs):
|
|||
resolve_keyed_by(
|
||||
job, 'attributes.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']))
|
||||
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(
|
||||
job, 'attributes.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']))
|
||||
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
|
||||
|
||||
|
||||
# should_use_artifact_map {{{
|
||||
def should_use_artifact_map(platform, project):
|
||||
def should_use_artifact_map(platform):
|
||||
"""Return True if this task uses the beetmover artifact map.
|
||||
|
||||
This function exists solely for the beetmover artifact map
|
||||
migration.
|
||||
"""
|
||||
if 'linux64-snap-shippable' 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
|
||||
return 'devedition' not in platform
|
||||
|
|
|
@ -91,8 +91,8 @@ def cli(runner_class=MarionetteTestRunner, parser_class=MarionetteArguments,
|
|||
failed = harness_instance.run()
|
||||
if failed > 0:
|
||||
sys.exit(10)
|
||||
except Exception:
|
||||
logger.error('Failure during harness execution', exc_info=True)
|
||||
except Exception as e:
|
||||
logger.error(e.message, exc_info=True)
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
|
||||
|
|
|
@ -44,6 +44,18 @@ config = {
|
|||
"win64",
|
||||
],
|
||||
},
|
||||
"complete-mar-bz2": {
|
||||
"product-name": "Firefox-%(version)s-Complete-bz2",
|
||||
"check_uptake": True,
|
||||
"platforms": [
|
||||
"linux",
|
||||
"linux64",
|
||||
"osx",
|
||||
"win",
|
||||
"win64",
|
||||
"win64-aarch64",
|
||||
],
|
||||
},
|
||||
},
|
||||
"partials": {
|
||||
"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)]);
|
||||
}, '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
|
||||
//
|
||||
|
@ -270,6 +288,16 @@ async_test(function(t) {
|
|||
}, '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');
|
||||
|
||||
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
|
||||
//
|
||||
|
@ -659,6 +687,43 @@ async_test(function(t) {
|
|||
}, 10);
|
||||
}, '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
|
||||
//
|
||||
|
|
|
@ -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'
|
||||
+ ' 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 => {
|
||||
const div = createDiv(t);
|
||||
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>
|
||||
'use strict';
|
||||
|
||||
// Test that each property defined in the Animation interface does not produce
|
||||
// style change events.
|
||||
// Test that each property defined in the Animation interface behaves as
|
||||
// expected with regards to whether or not it produces style change events.
|
||||
//
|
||||
// There are two types of tests:
|
||||
//
|
||||
|
@ -29,8 +29,9 @@
|
|||
// (b) An object with the following format:
|
||||
//
|
||||
// {
|
||||
// setup: elem => { /* return Animation */ }
|
||||
// test: animation => { /* play |animation| */ }
|
||||
// setup: elem => { /* return Animation */ },
|
||||
// test: animation => { /* play |animation| */ },
|
||||
// shouldFlush: boolean /* optional, defaults to false */
|
||||
// }
|
||||
//
|
||||
// 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.
|
||||
|
||||
const PlayAnimationTest = testFuncOrObj => {
|
||||
let test, setup;
|
||||
let test, setup, shouldFlush;
|
||||
|
||||
if (typeof testFuncOrObj === 'function') {
|
||||
test = testFuncOrObj;
|
||||
shouldFlush = false;
|
||||
} else {
|
||||
test = testFuncOrObj.test;
|
||||
if (typeof testFuncOrObj.setup === 'function') {
|
||||
setup = testFuncOrObj.setup;
|
||||
}
|
||||
shouldFlush = !!testFuncOrObj.shouldFlush;
|
||||
}
|
||||
|
||||
if (!setup) {
|
||||
|
@ -74,11 +77,11 @@ const PlayAnimationTest = testFuncOrObj => {
|
|||
);
|
||||
}
|
||||
|
||||
return { test, setup };
|
||||
return { test, setup, shouldFlush };
|
||||
};
|
||||
|
||||
const UsePropertyTest = testFuncOrObj => {
|
||||
const { setup, test } = PlayAnimationTest(testFuncOrObj);
|
||||
const { setup, test, shouldFlush } = PlayAnimationTest(testFuncOrObj);
|
||||
|
||||
let coveringAnimation;
|
||||
return {
|
||||
|
@ -93,6 +96,7 @@ const UsePropertyTest = testFuncOrObj => {
|
|||
test(animation);
|
||||
coveringAnimation.play();
|
||||
},
|
||||
shouldFlush,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -160,6 +164,7 @@ const tests = {
|
|||
}),
|
||||
playState: UsePropertyTest(animation => animation.playState),
|
||||
pending: UsePropertyTest(animation => animation.pending),
|
||||
replaceState: UsePropertyTest(animation => animation.replaceState),
|
||||
ready: UsePropertyTest(animation => animation.ready),
|
||||
finished: UsePropertyTest(animation => {
|
||||
// Get the finished Promise
|
||||
|
@ -172,6 +177,13 @@ const tests = {
|
|||
// Set the onfinish menber
|
||||
animation.onfinish = () => {};
|
||||
}),
|
||||
onremove: UsePropertyTest(animation => {
|
||||
// Get the onremove member
|
||||
animation.onremove;
|
||||
|
||||
// Set the onremove menber
|
||||
animation.onremove = () => {};
|
||||
}),
|
||||
oncancel: UsePropertyTest(animation => {
|
||||
// Get the oncancel member
|
||||
animation.oncancel;
|
||||
|
@ -225,6 +237,51 @@ const tests = {
|
|||
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']() {
|
||||
let originalElem;
|
||||
return UsePropertyTest({
|
||||
|
@ -266,7 +323,7 @@ test(() => {
|
|||
for (const key of properties) {
|
||||
promise_test(async t => {
|
||||
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
|
||||
const div = createDiv(t);
|
||||
|
@ -276,7 +333,7 @@ for (const key of properties) {
|
|||
});
|
||||
|
||||
// Setup animation
|
||||
const animation = setup(div);
|
||||
const animation = await setup(div);
|
||||
|
||||
// Setup transition start point
|
||||
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
|
||||
// a transition.
|
||||
|
||||
// Wait for the animation to start and then for at least one animation
|
||||
// frame to give the transitionrun event a chance to be dispatched.
|
||||
// Wait for the animation to start and then for at least two animation
|
||||
// frames to give the transitionrun event a chance to be dispatched.
|
||||
assert_true(
|
||||
typeof animation.ready !== 'undefined',
|
||||
'Should have a valid animation to wait on'
|
||||
);
|
||||
await animation.ready;
|
||||
await waitForAnimationFrames(1);
|
||||
await waitForAnimationFrames(2);
|
||||
|
||||
assert_false(gotTransition, 'A transition should NOT have been triggered');
|
||||
}, `Animation.${key} does NOT trigger a style change event`);
|
||||
if (shouldFlush) {
|
||||
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>
|
||||
</body>
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -223,4 +223,35 @@ promise_test(async t => {
|
|||
}, 'Playback events with the same timeline retain the order in which they are' +
|
||||
'queued');
|
||||
|
||||
promise_test(async t => {
|
||||
const div = createDiv(t);
|
||||
|
||||
// Create two animations with separate timelines
|
||||
|
||||
const timelineA = document.timeline;
|
||||
const animA = div.animate(null, 100 * MS_PER_SEC);
|
||||
|
||||
const timelineB = new DocumentTimeline();
|
||||
const animB = new Animation(
|
||||
new KeyframeEffect(div, null, 100 * MS_PER_SEC),
|
||||
timelineB
|
||||
);
|
||||
animB.play();
|
||||
|
||||
animA.currentTime = 99.9 * MS_PER_SEC;
|
||||
animB.currentTime = 99.9 * MS_PER_SEC;
|
||||
|
||||
// When the next tick happens both animations should be updated, and we will
|
||||
// notice that they are now finished. As a result their finished promise
|
||||
// callbacks should be queued. All of that should happen before we run the
|
||||
// next microtask checkpoint and actually run the promise callbacks and
|
||||
// hence the calls to cancel should not stop the existing callbacks from
|
||||
// being run.
|
||||
|
||||
animA.finished.then(() => { animB.cancel() });
|
||||
animB.finished.then(() => { animA.cancel() });
|
||||
|
||||
await Promise.all([animA.finished, animB.finished]);
|
||||
}, 'All timelines are updated before running microtasks');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -36,12 +36,7 @@ class WebNavigationChild extends ActorChild {
|
|||
this.gotoIndex(message.data);
|
||||
break;
|
||||
case "WebNavigation:LoadURI":
|
||||
let histogram = Services.telemetry.getKeyedHistogramById("FX_TAB_REMOTE_NAVIGATION_DELAY_MS");
|
||||
histogram.add("WebNavigation:LoadURI",
|
||||
Services.telemetry.msSystemNow() - message.data.requestTime);
|
||||
|
||||
this.loadURI(message.data);
|
||||
|
||||
break;
|
||||
case "WebNavigation:SetOriginAttributes":
|
||||
this.setOriginAttributes(message.data.originAttributes);
|
||||
|
|
|
@ -64,6 +64,9 @@ var EnableDelayHelper = function({enableDialog, disableDialog, focusTarget}) {
|
|||
|
||||
this.focusTarget.addEventListener("blur", this);
|
||||
this.focusTarget.addEventListener("focus", this);
|
||||
// While the user key-repeats, we want to renew the timer until keyup:
|
||||
this.focusTarget.addEventListener("keyup", this, true);
|
||||
this.focusTarget.addEventListener("keydown", this, true);
|
||||
this.focusTarget.document.addEventListener("unload", this);
|
||||
|
||||
this.startOnFocusDelay();
|
||||
|
@ -75,11 +78,29 @@ this.EnableDelayHelper.prototype = {
|
|||
},
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.target != this.focusTarget &&
|
||||
if (!event.type.startsWith("key") &&
|
||||
event.target != this.focusTarget &&
|
||||
event.target != this.focusTarget.document)
|
||||
return;
|
||||
|
||||
switch (event.type) {
|
||||
case "keyup":
|
||||
// As soon as any key goes up, we can stop treating keypresses
|
||||
// as indicative of key-repeating that should prolong the timer.
|
||||
this.focusTarget.removeEventListener("keyup", this, true);
|
||||
this.focusTarget.removeEventListener("keydown", this, true);
|
||||
break;
|
||||
|
||||
case "keydown":
|
||||
// Renew timer for repeating keydowns:
|
||||
if (this._focusTimer) {
|
||||
this._focusTimer.cancel();
|
||||
this._focusTimer = null;
|
||||
this.startOnFocusDelay();
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
|
||||
case "blur":
|
||||
this.onBlur();
|
||||
break;
|
||||
|
@ -111,6 +132,8 @@ this.EnableDelayHelper.prototype = {
|
|||
onUnload() {
|
||||
this.focusTarget.removeEventListener("blur", this);
|
||||
this.focusTarget.removeEventListener("focus", this);
|
||||
this.focusTarget.removeEventListener("keyup", this, true);
|
||||
this.focusTarget.removeEventListener("keydown", this, true);
|
||||
this.focusTarget.document.removeEventListener("unload", this);
|
||||
|
||||
if (this._focusTimer) {
|
||||
|
|
|
@ -116,7 +116,6 @@ RemoteWebNavigation.prototype = {
|
|||
triggeringPrincipal: E10SUtils.serializePrincipal(
|
||||
aLoadURIOptions.triggeringPrincipal || Services.scriptSecurityManager.createNullPrincipal({})),
|
||||
csp: aLoadURIOptions.csp ? E10SUtils.serializeCSP(aLoadURIOptions.csp) : null,
|
||||
requestTime: Services.telemetry.msSystemNow(),
|
||||
cancelContentJSEpoch,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -6282,17 +6282,6 @@
|
|||
"n_buckets": 20,
|
||||
"description": "Firefox: Time in ms spent on switching tabs in response to a tab click"
|
||||
},
|
||||
"FX_TAB_REMOTE_NAVIGATION_DELAY_MS": {
|
||||
"record_in_processes": ["content"],
|
||||
"alert_emails": ["mconley@mozilla.com"],
|
||||
"bug_numbers": [1352961, 1501295],
|
||||
"expires_in_version": "69",
|
||||
"kind": "exponential",
|
||||
"high": 4000,
|
||||
"n_buckets": 100,
|
||||
"keyed": true,
|
||||
"description": "Time taken (in ms) from the point of the parent sending the naviagion triggering message to the content and the content receiving it. This message can be either SessionStore:restoreTabContent or WebNavigation:LoadURI and these names are used as keys for this histogram. This is e10s only and recorded in the content process."
|
||||
},
|
||||
"FX_BOOKMARKS_TOOLBAR_INIT_MS": {
|
||||
"record_in_processes": ["main", "content"],
|
||||
"expires_in_version": "never",
|
||||
|
|
|
@ -449,22 +449,6 @@ identity.fxaccounts:
|
|||
- main
|
||||
|
||||
security:
|
||||
pkcs11_modules_loaded:
|
||||
bug_numbers:
|
||||
- 1369911
|
||||
- 1445961
|
||||
description: >
|
||||
A keyed boolean indicating the library names of the PKCS#11 modules that
|
||||
have been loaded by the browser.
|
||||
expires: "69"
|
||||
kind: boolean
|
||||
keyed: true
|
||||
notification_emails:
|
||||
- seceng-telemetry@mozilla.com
|
||||
- dkeeler@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- main
|
||||
webauthn_used:
|
||||
bug_numbers:
|
||||
- 1265472
|
||||
|
@ -1449,6 +1433,21 @@ devtools.inspector:
|
|||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
node_selection_count:
|
||||
bug_numbers:
|
||||
- 1550794
|
||||
description: >
|
||||
Number of times a different node is marked as selected in the Inspector regardless
|
||||
of the cause: context menu, manual selection in markup view, etc.
|
||||
expires: "never"
|
||||
kind: uint
|
||||
notification_emails:
|
||||
- dev-developer-tools@lists.mozilla.org
|
||||
- mbalfanz@mozilla.com
|
||||
release_channel_collection: opt-out
|
||||
record_in_processes:
|
||||
- 'main'
|
||||
|
||||
devtools.shadowdom:
|
||||
shadow_root_displayed:
|
||||
bug_numbers:
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче