diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 3d8689226c..c255f60614 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -339,7 +339,6 @@ pref("image.mem.surfacecache.max_size_kb", 131072); // 128MB pref("image.mem.surfacecache.size_factor", 8); // 1/8 of main memory pref("image.mem.surfacecache.discard_factor", 2); // Discard 1/2 of the surface cache at a time. pref("image.mem.surfacecache.min_expiration_ms", 86400000); // 24h, we rely on the out of memory hook -pref("image.onload.decode.limit", 24); /* don't decode more than 24 images eagerly */ // XXX this isn't a good check for "are touch events supported", but // we don't really have a better one at the moment. diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp index 204b4f54f7..672d67ccd2 100644 --- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -51,15 +51,17 @@ Animation::WrapObject(JSContext* aCx, JS::Handle aGivenProto) void Animation::SetEffect(KeyframeEffectReadOnly* aEffect) { + RefPtr kungFuDeathGrip(this); + if (mEffect == aEffect) { return; } if (mEffect) { - mEffect->SetParentTime(Nullable()); + mEffect->SetAnimation(nullptr); } mEffect = aEffect; if (mEffect) { - mEffect->SetParentTime(GetCurrentTime()); + mEffect->SetAnimation(this); } UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); @@ -934,7 +936,6 @@ void Animation::UpdateEffect() { if (mEffect) { - mEffect->SetParentTime(GetCurrentTime()); UpdateRelevance(); } } @@ -1045,6 +1046,46 @@ Animation::EffectEnd() const + mEffect->GetComputedTiming().mActiveDuration; } +TimeStamp +Animation::AnimationTimeToTimeStamp(const StickyTimeDuration& aTime) const +{ + // Initializes to null. Return the same object every time to benefit from + // return-value-optimization. + TimeStamp result; + + // We *don't* check for mTimeline->TracksWallclockTime() here because that + // method only tells us if the timeline times can be converted to + // TimeStamps that can be compared to TimeStamp::Now() or not, *not* + // whether the timelines can be converted to TimeStamp values at all. + // + // Since we never compare the result of this method with TimeStamp::Now() + // it is ok to return values even if mTimeline->TracksWallclockTime() is + // false. Furthermore, we want to be able to use this method when the + // refresh driver is under test control (in which case TracksWallclockTime() + // will return false). + // + // Once we introduce timelines that are not time-based we will need to + // differentiate between them here and determine how to sort their events. + if (!mTimeline) { + return result; + } + + // Check the time is convertible to a timestamp + if (aTime == TimeDuration::Forever() || + mPlaybackRate == 0.0 || + mStartTime.IsNull()) { + return result; + } + + // Invert the standard relation: + // animation time = (timeline time - start time) * playback rate + TimeDuration timelineTime = + TimeDuration(aTime).MultDouble(1.0 / mPlaybackRate) + mStartTime.Value(); + + result = mTimeline->ToTimeStamp(timelineTime); + return result; +} + nsIDocument* Animation::GetRenderedDocument() const { diff --git a/dom/animation/Animation.h b/dom/animation/Animation.h index 66aa395bb8..f0bf1929a4 100644 --- a/dom/animation/Animation.h +++ b/dom/animation/Animation.h @@ -232,11 +232,11 @@ public: bool HasInPlayEffect() const { - return GetEffect() && GetEffect()->IsInPlay(*this); + return GetEffect() && GetEffect()->IsInPlay(); } bool HasCurrentEffect() const { - return GetEffect() && GetEffect()->IsCurrent(*this); + return GetEffect() && GetEffect()->IsCurrent(); } bool IsInEffect() const { @@ -357,6 +357,7 @@ protected: bool IsPossiblyOrphanedPendingAnimation() const; StickyTimeDuration EffectEnd() const; + TimeStamp AnimationTimeToTimeStamp(const StickyTimeDuration& aTime) const; nsIDocument* GetRenderedDocument() const; nsPresContext* GetPresContext() const; diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 15882164e8..3e2dce93bb 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -6,10 +6,14 @@ #include "mozilla/dom/KeyframeEffect.h" #include "mozilla/dom/KeyframeEffectBinding.h" +#include "mozilla/dom/PropertyIndexedKeyframesBinding.h" #include "mozilla/FloatingPoint.h" +#include "mozilla/StyleAnimationValue.h" #include "AnimationCommon.h" +#include "nsCSSParser.h" #include "nsCSSPropertySet.h" #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags +#include "nsCSSValue.h" #include "nsStyleUtil.h" namespace mozilla { @@ -109,7 +113,8 @@ namespace dom { NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly, - mTarget) + mTarget, + mAnimation) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly) @@ -143,20 +148,27 @@ KeyframeEffectReadOnly::WrapObject(JSContext* aCx, } void -KeyframeEffectReadOnly::SetParentTime(Nullable aParentTime) -{ - mParentTime = aParentTime; -} - -void -KeyframeEffectReadOnly::SetTiming(const AnimationTiming& aTiming, - Animation& aOwningAnimation) +KeyframeEffectReadOnly::SetTiming(const AnimationTiming& aTiming) { if (mTiming == aTiming) { return; } mTiming = aTiming; - aOwningAnimation.NotifyEffectTimingUpdated(); + if (mAnimation) { + mAnimation->NotifyEffectTimingUpdated(); + } +} + +Nullable +KeyframeEffectReadOnly::GetLocalTime() const +{ + // Since the *animation* start time is currently always zero, the local + // time is equal to the parent time. + Nullable result; + if (mAnimation) { + result = mAnimation->GetCurrentTime(); + } + return result; } ComputedTiming @@ -306,9 +318,9 @@ KeyframeEffectReadOnly::ActiveDuration(const AnimationTiming& aTiming) // https://w3c.github.io/web-animations/#in-play bool -KeyframeEffectReadOnly::IsInPlay(const Animation& aAnimation) const +KeyframeEffectReadOnly::IsInPlay() const { - if (aAnimation.PlayState() == AnimationPlayState::Finished) { + if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) { return false; } @@ -317,9 +329,9 @@ KeyframeEffectReadOnly::IsInPlay(const Animation& aAnimation) const // https://w3c.github.io/web-animations/#current bool -KeyframeEffectReadOnly::IsCurrent(const Animation& aAnimation) const +KeyframeEffectReadOnly::IsCurrent() const { - if (aAnimation.PlayState() == AnimationPlayState::Finished) { + if (!mAnimation || mAnimation->PlayState() == AnimationPlayState::Finished) { return false; } @@ -336,6 +348,12 @@ KeyframeEffectReadOnly::IsInEffect() const return computedTiming.mProgress != ComputedTiming::kNullProgress; } +void +KeyframeEffectReadOnly::SetAnimation(Animation* aAnimation) +{ + mAnimation = aAnimation; +} + const AnimationProperty* KeyframeEffectReadOnly::GetAnimationOfProperty(nsCSSProperty aProperty) const { @@ -495,6 +513,12 @@ KeyframeEffectReadOnly::SetIsRunningOnCompositor(nsCSSProperty aProperty, } } +// We need to define this here since Animation is an incomplete type +// (forward-declared) in the header. +KeyframeEffectReadOnly::~KeyframeEffectReadOnly() +{ +} + void KeyframeEffectReadOnly::ResetIsRunningOnCompositor() { @@ -503,112 +527,1193 @@ KeyframeEffectReadOnly::ResetIsRunningOnCompositor() } } -struct KeyframeValueEntry +#ifdef DEBUG +void +DumpAnimationProperties(nsTArray& aAnimationProperties) +{ + for (auto& p : aAnimationProperties) { + printf("%s\n", nsCSSProps::GetStringValue(p.mProperty).get()); + for (auto& s : p.mSegments) { + nsString fromValue, toValue; + StyleAnimationValue::UncomputeValue(p.mProperty, + s.mFromValue, + fromValue); + StyleAnimationValue::UncomputeValue(p.mProperty, + s.mToValue, + toValue); + printf(" %f..%f: %s..%s\n", s.mFromKey, s.mToKey, + NS_ConvertUTF16toUTF8(fromValue).get(), + NS_ConvertUTF16toUTF8(toValue).get()); + } + } +} +#endif + +/* static */ AnimationTiming +KeyframeEffectReadOnly::ConvertKeyframeEffectOptions( + const Optional& aOptions) +{ + AnimationTiming animationTiming; + + // The spec says to treat auto durations as 0 until a later version of + // the spec says otherwise. Bug 1215406 is for handling a + // KeyframeEffectOptions object and not just an offset. + if (aOptions.WasPassed()) { + animationTiming.mIterationDuration = + TimeDuration::FromMilliseconds(aOptions.Value()); + } else { + animationTiming.mIterationDuration = TimeDuration(0); + } + animationTiming.mIterationCount = 1.0f; + animationTiming.mDirection = NS_STYLE_ANIMATION_DIRECTION_NORMAL; + animationTiming.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_NONE; + + return animationTiming; +} + +/** + * A property and StyleAnimationValue pair. + */ +struct KeyframeValue +{ + nsCSSProperty mProperty; + StyleAnimationValue mValue; +}; + +/** + * Represents a relative position for a value in a keyframe animation. + */ +enum class ValuePosition +{ + First, // value at 0 used for reverse filling + Left, // value coming in to a given offset + Right, // value coming out from a given offset + Last // value at 1 used for forward filling +}; + +/** + * A single value in a keyframe animation, used by GetFrames to produce a + * minimal set of Keyframe objects. + */ +struct OrderedKeyframeValueEntry : KeyframeValue { float mOffset; - nsCSSProperty mProperty; - nsString mValue; const ComputedTimingFunction* mTimingFunction; + ValuePosition mPosition; - bool operator==(const KeyframeValueEntry& aRhs) const + bool SameKeyframe(const OrderedKeyframeValueEntry& aOther) const { - NS_ASSERTION(mOffset != aRhs.mOffset || mProperty != aRhs.mProperty, - "shouldn't have duplicate (offset, property) pairs"); + return mOffset == aOther.mOffset && + !!mTimingFunction == !!aOther.mTimingFunction && + (!mTimingFunction || *mTimingFunction == *aOther.mTimingFunction) && + mPosition == aOther.mPosition; + } + + struct ForKeyframeGenerationComparator + { + static bool Equals(const OrderedKeyframeValueEntry& aLhs, + const OrderedKeyframeValueEntry& aRhs) + { + return aLhs.SameKeyframe(aRhs) && + aLhs.mProperty == aRhs.mProperty; + } + static bool LessThan(const OrderedKeyframeValueEntry& aLhs, + const OrderedKeyframeValueEntry& aRhs) + { + // First, sort by offset. + if (aLhs.mOffset != aRhs.mOffset) { + return aLhs.mOffset < aRhs.mOffset; + } + + // Second, by position. + if (aLhs.mPosition != aRhs.mPosition) { + return aLhs.mPosition < aRhs.mPosition; + } + + // Third, by easing. + if (aLhs.mTimingFunction) { + if (aRhs.mTimingFunction) { + int32_t order = aLhs.mTimingFunction->Compare(*aRhs.mTimingFunction); + if (order != 0) { + return order < 0; + } + } else { + return true; + } + } else { + if (aRhs.mTimingFunction) { + return false; + } + } + + // Last, by property IDL name. + return nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty) < + nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty); + } + }; +}; + +/** + * Data for a segment in a keyframe animation of a given property + * whose value is a StyleAnimationValue. + * + * KeyframeValueEntry is used in BuildAnimationPropertyListFromKeyframeSequence + * to gather data for each individual segment described by an author-supplied + * an IDL sequence value so that they can be parsed into mProperties. + */ +struct KeyframeValueEntry : KeyframeValue +{ + float mOffset; + ComputedTimingFunction mTimingFunction; + + struct PropertyOffsetComparator + { + static bool Equals(const KeyframeValueEntry& aLhs, + const KeyframeValueEntry& aRhs) + { + return aLhs.mProperty == aRhs.mProperty && + aLhs.mOffset == aRhs.mOffset; + } + static bool LessThan(const KeyframeValueEntry& aLhs, + const KeyframeValueEntry& aRhs) + { + // First, sort by property IDL name. + int32_t order = nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty) - + nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty); + if (order != 0) { + return order < 0; + } + + // Then, by offset. + return aLhs.mOffset < aRhs.mOffset; + } + }; +}; + +/** + * A property-values pair obtained from the open-ended properties + * discovered on a Keyframe or PropertyIndexedKeyframes object. + * + * Single values (as required by Keyframe, and as also supported + * on PropertyIndexedKeyframes) are stored as the only element in + * mValues. + */ +struct PropertyValuesPair +{ + nsCSSProperty mProperty; + nsTArray mValues; + + class PropertyPriorityComparator + { + public: + PropertyPriorityComparator() + : mSubpropertyCountInitialized(false) {} + + bool Equals(const PropertyValuesPair& aLhs, + const PropertyValuesPair& aRhs) const + { + return aLhs.mProperty == aRhs.mProperty; + } + + bool LessThan(const PropertyValuesPair& aLhs, + const PropertyValuesPair& aRhs) const + { + bool isShorthandLhs = nsCSSProps::IsShorthand(aLhs.mProperty); + bool isShorthandRhs = nsCSSProps::IsShorthand(aRhs.mProperty); + + if (isShorthandLhs) { + if (isShorthandRhs) { + // First, sort shorthands by the number of longhands they have. + uint32_t subpropCountLhs = SubpropertyCount(aLhs.mProperty); + uint32_t subpropCountRhs = SubpropertyCount(aRhs.mProperty); + if (subpropCountLhs != subpropCountRhs) { + return subpropCountLhs < subpropCountRhs; + } + // Otherwise, sort by IDL name below. + } else { + // Put longhands before shorthands. + return false; + } + } else { + if (isShorthandRhs) { + // Put longhands before shorthands. + return true; + } + } + // For two longhand properties, or two shorthand with the same number + // of longhand components, sort by IDL name. + return nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty) < + nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty); + } + + uint32_t SubpropertyCount(nsCSSProperty aProperty) const + { + if (!mSubpropertyCountInitialized) { + PodZero(&mSubpropertyCount); + mSubpropertyCountInitialized = true; + } + if (mSubpropertyCount[aProperty] == 0) { + uint32_t count = 0; + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES( + p, aProperty, nsCSSProps::eEnabledForAllContent) { + ++count; + } + mSubpropertyCount[aProperty] = count; + } + return mSubpropertyCount[aProperty]; + } + + private: + // Cache of shorthand subproperty counts. + mutable RangedArray< + uint32_t, + eCSSProperty_COUNT_no_shorthands, + eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands> mSubpropertyCount; + mutable bool mSubpropertyCountInitialized; + }; +}; + +/** + * The result of parsing a JS object as a Keyframe dictionary + * and getting its property-value pairs from its open-ended + * properties. + */ +struct OffsetIndexedKeyframe +{ + binding_detail::FastKeyframe mKeyframeDict; + nsTArray mPropertyValuePairs; +}; + +/** + * Parses a CSS value from + * aEasing into a ComputedTimingFunction. If parsing fails, aResult will + * be set to 'linear'. + */ +static void +ParseEasing(Element* aTarget, + const nsAString& aEasing, + ComputedTimingFunction& aResult) +{ + nsIDocument* doc = aTarget->OwnerDoc(); + + nsCSSValue value; + nsCSSParser parser; + parser.ParseLonghandProperty(eCSSProperty_animation_timing_function, + aEasing, + doc->GetDocumentURI(), + doc->GetDocumentURI(), + doc->NodePrincipal(), + value); + + switch (value.GetUnit()) { + case eCSSUnit_List: { + const nsCSSValueList* list = value.GetListValue(); + if (list->mNext) { + // don't support a list of timing functions + break; + } + switch (list->mValue.GetUnit()) { + case eCSSUnit_Enumerated: + case eCSSUnit_Cubic_Bezier: + case eCSSUnit_Steps: { + nsTimingFunction timingFunction; + nsRuleNode::ComputeTimingFunction(list->mValue, timingFunction); + aResult.Init(timingFunction); + return; + } + default: + MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function list " + "item unit"); + break; + } + break; + } + case eCSSUnit_Null: + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + case eCSSUnit_Unset: + case eCSSUnit_TokenStream: + break; + default: + MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function unit"); + break; + } + + aResult.Init(nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR)); +} + +/** + * An additional property (for a property-values pair) found on a Keyframe + * or PropertyIndexedKeyframes object. + */ +struct AdditionalProperty +{ + nsCSSProperty mProperty; + size_t mJsidIndex; // Index into |ids| in GetPropertyValuesPairs. + + struct PropertyComparator + { + bool Equals(const AdditionalProperty& aLhs, + const AdditionalProperty& aRhs) const + { + return aLhs.mProperty == aRhs.mProperty; + } + bool LessThan(const AdditionalProperty& aLhs, + const AdditionalProperty& aRhs) const + { + return nsCSSProps::PropertyIDLNameSortPosition(aLhs.mProperty) < + nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty); + } + }; +}; + +/** + * Converts aValue to DOMString and appends it to aValues. + */ +static bool +AppendValueAsString(JSContext* aCx, + nsTArray& aValues, + JS::Handle aValue) +{ + return ConvertJSValueToString(aCx, aValue, eStringify, eStringify, + *aValues.AppendElement()); +} + +// For the aAllowList parameter of AppendStringOrStringSequence and +// GetPropertyValuesPairs. +enum class ListAllowance { eDisallow, eAllow }; + +/** + * Converts aValue to DOMString, if aAllowLists is eDisallow, or + * to (DOMString or sequence) if aAllowLists is aAllow. + * The resulting strings are appended to aValues. + */ +static bool +AppendStringOrStringSequenceToArray(JSContext* aCx, + JS::Handle aValue, + ListAllowance aAllowLists, + nsTArray& aValues) +{ + if (aAllowLists == ListAllowance::eAllow && aValue.isObject()) { + // The value is an object, and we want to allow lists; convert + // aValue to (DOMString or sequence). + JS::ForOfIterator iter(aCx); + if (!iter.init(aValue, JS::ForOfIterator::AllowNonIterable)) { + return false; + } + if (iter.valueIsIterable()) { + // If the object is iterable, convert it to sequence. + JS::Rooted element(aCx); + for (;;) { + bool done; + if (!iter.next(&element, &done)) { + return false; + } + if (done) { + break; + } + if (!AppendValueAsString(aCx, aValues, element)) { + return false; + } + } + return true; + } + } + + // Either the object is not iterable, or aAllowLists doesn't want + // a list; convert it to DOMString. + if (!AppendValueAsString(aCx, aValues, aValue)) { return false; } - bool operator<(const KeyframeValueEntry& aRhs) const - { - NS_ASSERTION(mOffset != aRhs.mOffset || mProperty != aRhs.mProperty, - "shouldn't have duplicate (offset, property) pairs"); + return true; +} - // First, sort by offset. - if (mOffset != aRhs.mOffset) { - return mOffset < aRhs.mOffset; - } +/** + * Reads the property-values pairs from the specified JS object. + * + * @param aObject The JS object to look at. + * @param aAllowLists If eAllow, values will be converted to + * (DOMString or sequence aObject, + ListAllowance aAllowLists, + nsTArray& aResult) +{ + nsTArray properties; - // Second, by timing function. - int32_t order = mTimingFunction->Compare(*aRhs.mTimingFunction); - if (order != 0) { - return order < 0; - } - - // Last, by property IDL name. - return nsCSSProps::PropertyIDLNameSortPosition(mProperty) < - nsCSSProps::PropertyIDLNameSortPosition(aRhs.mProperty); + // Iterate over all the properties on aObject and append an + // entry to properties for them. + // + // We don't compare the jsids that we encounter with those for + // the explicit dictionary members, since we know that none + // of the CSS property IDL names clash with them. + JS::Rooted ids(aCx, JS::IdVector(aCx)); + if (!JS_Enumerate(aCx, aObject, &ids)) { + return false; } -}; + for (size_t i = 0, n = ids.length(); i < n; i++) { + nsAutoJSString propName; + if (!propName.init(aCx, ids[i])) { + return false; + } + nsCSSProperty property = + nsCSSProps::LookupPropertyByIDLName(propName, + nsCSSProps::eEnabledForAllContent); + if (property != eCSSProperty_UNKNOWN && + nsCSSProps::kAnimTypeTable[property] != eStyleAnimType_None) { + AdditionalProperty* p = properties.AppendElement(); + p->mProperty = property; + p->mJsidIndex = i; + } + } + + // Sort the entries by IDL name and then get each value and + // convert it either to a DOMString or to a + // (DOMString or sequence), depending on aAllowLists, + // and build up aResult. + properties.Sort(AdditionalProperty::PropertyComparator()); + + for (AdditionalProperty& p : properties) { + JS::Rooted value(aCx); + if (!JS_GetPropertyById(aCx, aObject, ids[p.mJsidIndex], &value)) { + return false; + } + PropertyValuesPair* pair = aResult.AppendElement(); + pair->mProperty = p.mProperty; + if (!AppendStringOrStringSequenceToArray(aCx, value, aAllowLists, + pair->mValues)) { + return false; + } + } + + return true; +} + +/** + * Converts a JS object wrapped by the given JS::ForIfIterator to an + * IDL sequence and stores the resulting OffsetIndexedKeyframe + * objects in aResult. + */ +static bool +ConvertKeyframeSequence(JSContext* aCx, + JS::ForOfIterator& aIterator, + nsTArray& aResult) +{ + JS::Rooted value(aCx); + for (;;) { + bool done; + if (!aIterator.next(&value, &done)) { + return false; + } + if (done) { + break; + } + // Each value found when iterating the object must be an object + // or null/undefined (which gets treated as a default {} dictionary + // value). + if (!value.isObject() && !value.isNullOrUndefined()) { + ThrowErrorMessage(aCx, MSG_NOT_OBJECT, + "Element of sequence argument"); + return false; + } + // Convert the JS value into a Keyframe dictionary value. + OffsetIndexedKeyframe* keyframe = aResult.AppendElement(); + if (!keyframe->mKeyframeDict.Init( + aCx, value, "Element of sequence argument")) { + return false; + } + // Look for additional property-values pairs on the object. + if (value.isObject()) { + JS::Rooted object(aCx, &value.toObject()); + if (!GetPropertyValuesPairs(aCx, object, + ListAllowance::eDisallow, + keyframe->mPropertyValuePairs)) { + return false; + } + } + } + return true; +} + +/** + * Checks that the given keyframes are loosely ordered (each keyframe's + * offset that is not null is greater than or equal to the previous + * non-null offset) and that all values are within the range [0.0, 1.0]. + * + * @return true if the keyframes' offsets are correctly ordered and + * within range; false otherwise. + */ +static bool +HasValidOffsets(const nsTArray& aKeyframes) +{ + double offset = 0.0; + for (const OffsetIndexedKeyframe& keyframe : aKeyframes) { + if (!keyframe.mKeyframeDict.mOffset.IsNull()) { + double thisOffset = keyframe.mKeyframeDict.mOffset.Value(); + if (thisOffset < offset || thisOffset > 1.0f) { + return false; + } + offset = thisOffset; + } + } + return true; +} + +/** + * Fills in any null offsets for the given keyframes by applying the + * "distribute" spacing algorithm. + * + * http://w3c.github.io/web-animations/#distribute-keyframe-spacing-mode + */ +static void +ApplyDistributeSpacing(nsTArray& aKeyframes) +{ + // If the first or last keyframes have an unspecified offset, + // fill them in with 0% and 100%. If there is only a single keyframe, + // then it gets 100%. + if (aKeyframes.LastElement().mKeyframeDict.mOffset.IsNull()) { + aKeyframes.LastElement().mKeyframeDict.mOffset.SetValue(1.0); + } + if (aKeyframes[0].mKeyframeDict.mOffset.IsNull()) { + aKeyframes[0].mKeyframeDict.mOffset.SetValue(0.0); + } + + // Fill in remaining missing offsets. + size_t i = 0; + while (i < aKeyframes.Length() - 1) { + MOZ_ASSERT(!aKeyframes[i].mKeyframeDict.mOffset.IsNull()); + double start = aKeyframes[i].mKeyframeDict.mOffset.Value(); + size_t j = i + 1; + while (aKeyframes[j].mKeyframeDict.mOffset.IsNull()) { + ++j; + } + double end = aKeyframes[j].mKeyframeDict.mOffset.Value(); + size_t n = j - i; + for (size_t k = 1; k < n; ++k) { + double offset = start + double(k) / n * (end - start); + aKeyframes[i + k].mKeyframeDict.mOffset.SetValue(offset); + } + i = j; + } +} + +/** + * Splits out each property's keyframe animation segment information + * from the OffsetIndexedKeyframe objects into an array of KeyframeValueEntry. + * + * The easing string value in OffsetIndexedKeyframe objects is parsed + * into a ComputedTimingFunction value in the corresponding KeyframeValueEntry + * objects. + * + * @param aTarget The target of the animation. + * @param aKeyframes The keyframes to read. + * @param aResult The array to append the resulting KeyframeValueEntry + * objects to. + */ +static void +GenerateValueEntries(Element* aTarget, + nsTArray& aKeyframes, + nsTArray& aResult, + ErrorResult& aRv) +{ + nsCSSPropertySet properties; // All properties encountered. + nsCSSPropertySet propertiesWithFromValue; // Those with a defined 0% value. + nsCSSPropertySet propertiesWithToValue; // Those with a defined 100% value. + + for (OffsetIndexedKeyframe& keyframe : aKeyframes) { + float offset = float(keyframe.mKeyframeDict.mOffset.Value()); + ComputedTimingFunction easing; + ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing, easing); + // We ignore keyframe.mKeyframeDict.mComposite since we don't support + // composite modes on keyframes yet. + + // keyframe.mPropertyValuePairs is currently sorted by CSS property IDL + // name, since that was the order we read the properties from the JS + // object. Re-sort the list so that longhand properties appear before + // shorthands, and with shorthands all appearing in increasing order of + // number of components. For two longhand properties, or two shorthands + // with the same number of components, sort by IDL name. + // + // Example orderings that result from this: + // + // margin-left, margin + // + // and: + // + // border-top-color, border-color, border-top, border + // + // This allows us to prioritize values specified by longhands (or smaller + // shorthand subsets) when longhands and shorthands are both specified + // on the one keyframe. + keyframe.mPropertyValuePairs.Sort( + PropertyValuesPair::PropertyPriorityComparator()); + + nsCSSPropertySet propertiesOnThisKeyframe; + for (const PropertyValuesPair& pair : keyframe.mPropertyValuePairs) { + MOZ_ASSERT(pair.mValues.Length() == 1, + "ConvertKeyframeSequence should have parsed single " + "DOMString values from the property-values pairs"); + // Parse the property's string value and produce a KeyframeValueEntry (or + // more than one, for shorthands) for it. + nsTArray values; + if (StyleAnimationValue::ComputeValues(pair.mProperty, + nsCSSProps::eEnabledForAllContent, + aTarget, + pair.mValues[0], + /* aUseSVGMode */ false, + values)) { + for (auto& value : values) { + // If we already got a value for this property on the keyframe, + // skip this one. + if (propertiesOnThisKeyframe.HasProperty(value.mProperty)) { + continue; + } + + KeyframeValueEntry* entry = aResult.AppendElement(); + entry->mOffset = offset; + entry->mProperty = value.mProperty; + entry->mValue = value.mValue; + entry->mTimingFunction = easing; + + if (offset == 0.0) { + propertiesWithFromValue.AddProperty(value.mProperty); + } else if (offset == 1.0) { + propertiesWithToValue.AddProperty(value.mProperty); + } + propertiesOnThisKeyframe.AddProperty(value.mProperty); + properties.AddProperty(value.mProperty); + } + } + } + } + + // We don't support additive segments and so can't support missing properties + // using their underlying value in 0% and 100% keyframes. Throw an exception + // until we do support this. + if (!propertiesWithFromValue.Equals(properties) || + !propertiesWithToValue.Equals(properties)) { + aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); + return; + } +} + +/** + * Builds an array of AnimationProperty objects to represent the keyframe + * animation segments in aEntries. + */ +static void +BuildSegmentsFromValueEntries(nsTArray& aEntries, + nsTArray& aResult) +{ + if (aEntries.IsEmpty()) { + return; + } + + // Sort the KeyframeValueEntry objects so that all entries for a given + // property are together, and the entries are sorted by offset otherwise. + std::stable_sort(aEntries.begin(), aEntries.end(), + &KeyframeValueEntry::PropertyOffsetComparator::LessThan); + + MOZ_ASSERT(aEntries[0].mOffset == 0.0f); + MOZ_ASSERT(aEntries.LastElement().mOffset == 1.0f); + + // For a given index i, we want to generate a segment from aEntries[i] + // to aEntries[j], if: + // + // * j > i, + // * aEntries[i + 1]'s offset/property is different from aEntries[i]'s, and + // * aEntries[j - 1]'s offset/property is different from aEntries[j]'s. + // + // That will eliminate runs of same offset/property values where there's no + // point generating zero length segments in the middle of the animation. + // + // Additionally we need to generate a zero length segment at offset 0 and at + // offset 1, if we have multiple values for a given property at that offset, + // since we need to retain the very first and very last value so they can + // be used for reverse and forward filling. + + nsCSSProperty lastProperty = eCSSProperty_UNKNOWN; + AnimationProperty* animationProperty = nullptr; + + size_t i = 0, n = aEntries.Length(); + + while (i + 1 < n) { + // Starting from i, determine the next [i, j] interval from which to + // generate a segment. + size_t j; + if (aEntries[i].mOffset == 0.0f && aEntries[i + 1].mOffset == 0.0f) { + // We need to generate an initial zero-length segment. + MOZ_ASSERT(aEntries[i].mProperty == aEntries[i + 1].mProperty); + j = i + 1; + while (aEntries[j + 1].mOffset == 0.0f) { + MOZ_ASSERT(aEntries[j].mProperty == aEntries[j + 1].mProperty); + ++j; + } + } else if (aEntries[i].mOffset == 1.0f) { + if (aEntries[i + 1].mOffset == 1.0f) { + // We need to generate a final zero-length segment. + MOZ_ASSERT(aEntries[i].mProperty == aEntries[i].mProperty); + j = i + 1; + while (j + 1 < n && aEntries[j + 1].mOffset == 1.0f) { + MOZ_ASSERT(aEntries[j].mProperty == aEntries[j + 1].mProperty); + ++j; + } + } else { + // New property. + MOZ_ASSERT(aEntries[i + 1].mOffset == 0.0f); + MOZ_ASSERT(aEntries[i].mProperty != aEntries[i + 1].mProperty); + ++i; + continue; + } + } else { + while (aEntries[i].mOffset == aEntries[i + 1].mOffset && + aEntries[i].mProperty == aEntries[i + 1].mProperty) { + ++i; + } + j = i + 1; + } + + // If we've moved on to a new property, create a new AnimationProperty + // to insert segments into. + if (aEntries[i].mProperty != lastProperty) { + MOZ_ASSERT(aEntries[i].mOffset == 0.0f); + animationProperty = aResult.AppendElement(); + animationProperty->mProperty = aEntries[i].mProperty; + animationProperty->mWinsInCascade = true; + lastProperty = aEntries[i].mProperty; + } + + // Now generate the segment. + AnimationPropertySegment* segment = + animationProperty->mSegments.AppendElement(); + segment->mFromKey = aEntries[i].mOffset; + segment->mToKey = aEntries[j].mOffset; + segment->mFromValue = aEntries[i].mValue; + segment->mToValue = aEntries[j].mValue; + segment->mTimingFunction = aEntries[i].mTimingFunction; + + i = j; + } +} + +/** + * Converts a JS object to an IDL sequence and builds an + * array of AnimationProperty objects for the keyframe animation + * that it specifies. + * + * @param aTarget The target of the animation. + * @param aIterator An already-initialized ForOfIterator for the JS + * object to iterate over as a sequence. + * @param aResult The array into which the resulting AnimationProperty + * objects will be appended. + */ +static void +BuildAnimationPropertyListFromKeyframeSequence( + JSContext* aCx, + Element* aTarget, + JS::ForOfIterator& aIterator, + nsTArray& aResult, + ErrorResult& aRv) +{ + // Convert the object in aIterator to sequence, producing + // an array of OffsetIndexedKeyframe objects. + nsAutoTArray keyframes; + if (!ConvertKeyframeSequence(aCx, aIterator, keyframes)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + // If the sequence<> had zero elements, we won't generate any + // keyframes. + if (keyframes.IsEmpty()) { + return; + } + + // Check that the keyframes are loosely sorted and with values all + // between 0% and 100%. + if (!HasValidOffsets(keyframes)) { + aRv.ThrowTypeError(); + return; + } + + // Fill in 0%/100% values if the first/element keyframes don't have + // a specified offset, and evenly space those that have a missing + // offset. (We don't support paced spacing yet.) + ApplyDistributeSpacing(keyframes); + + // Convert the OffsetIndexedKeyframes into a list of KeyframeValueEntry + // objects. + nsTArray entries; + GenerateValueEntries(aTarget, keyframes, entries, aRv); + if (aRv.Failed()) { + return; + } + + // Finally, build an array of AnimationProperty objects in aResult + // corresponding to the entries. + BuildSegmentsFromValueEntries(entries, aResult); +} + +/** + * Converts a JS object to an IDL PropertyIndexedKeyframes and builds an + * array of AnimationProperty objects for the keyframe animation + * that it specifies. + * + * @param aTarget The target of the animation. + * @param aValue The JS object. + * @param aResult The array into which the resulting AnimationProperty + * objects will be appended. + */ +static void +BuildAnimationPropertyListFromPropertyIndexedKeyframes( + JSContext* aCx, + Element* aTarget, + JS::Handle aValue, + InfallibleTArray& aResult, + ErrorResult& aRv) +{ + MOZ_ASSERT(aValue.isObject()); + + // Convert the object to a PropertyIndexedKeyframes dictionary to + // get its explicit dictionary members. + binding_detail::FastPropertyIndexedKeyframes keyframes; + if (!keyframes.Init(aCx, aValue, "PropertyIndexedKeyframes argument", + false)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + ComputedTimingFunction easing; + ParseEasing(aTarget, keyframes.mEasing, easing); + + // We ignore easing.mComposite since we don't support composite modes on + // keyframes yet. + + // Get all the property--value-list pairs off the object. + JS::Rooted object(aCx, &aValue.toObject()); + nsTArray propertyValuesPairs; + if (!GetPropertyValuesPairs(aCx, object, ListAllowance::eAllow, + propertyValuesPairs)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + // We must keep track of which properties we've already generated + // an AnimationProperty since the author could have specified both a + // shorthand and one of its component longhands on the + // PropertyIndexedKeyframes. + nsCSSPropertySet properties; + + // Create AnimationProperty objects for each PropertyValuesPair, applying + // the "distribute" spacing algorithm to the segments. + for (const PropertyValuesPair& pair : propertyValuesPairs) { + size_t count = pair.mValues.Length(); + if (count == 0) { + // No animation values for this property. + continue; + } + if (count == 1) { + // We don't support additive segments and so can't support an + // animation that goes from the underlying value to this + // specified value. Throw an exception until we do support this. + aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); + return; + } + + // If we find an invalid value, we don't create a segment for it, but + // we adjust the surrounding segments so that the timing of the segments + // is the same as if we did support it. For example, animating with + // values ["red", "green", "yellow", "invalid", "blue"] will generate + // segments with this timing: + // + // 0.00 -> 0.25 : red -> green + // 0.25 -> 0.50 : green -> yellow + // 0.50 -> 1.00 : yellow -> blue + // + // With future spec clarifications we might decide to preserve the invalid + // value on the segment and make the animation code deal with the invalid + // value instead. + nsTArray fromValues; + float fromKey = 0.0f; + if (!StyleAnimationValue::ComputeValues(pair.mProperty, + nsCSSProps::eEnabledForAllContent, + aTarget, + pair.mValues[0], + /* aUseSVGMode */ false, + fromValues)) { + // We need to throw for an invalid first value, since that would imply an + // additive animation, which we don't support yet. + aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); + return; + } + + if (fromValues.IsEmpty()) { + // All longhand components of a shorthand pair.mProperty must be disabled. + continue; + } + + // Create AnimationProperty objects for each property that had a + // value computed. When pair.mProperty is a longhand, it is just + // that property. When pair.mProperty is a shorthand, we'll have + // one property per longhand component. + nsTArray animationPropertyIndexes; + animationPropertyIndexes.SetLength(fromValues.Length()); + for (size_t i = 0, n = fromValues.Length(); i < n; ++i) { + nsCSSProperty p = fromValues[i].mProperty; + bool found = false; + if (properties.HasProperty(p)) { + // We have already dealt with this property. Look up and + // overwrite the old AnimationProperty object. + for (size_t j = 0, m = aResult.Length(); j < m; ++j) { + if (aResult[j].mProperty == p) { + aResult[j].mSegments.Clear(); + animationPropertyIndexes[i] = j; + found = true; + break; + } + } + MOZ_ASSERT(found, "properties is inconsistent with aResult"); + } + if (!found) { + // This is the first time we've encountered this property. + animationPropertyIndexes[i] = aResult.Length(); + AnimationProperty* animationProperty = aResult.AppendElement(); + animationProperty->mProperty = p; + animationProperty->mWinsInCascade = true; + properties.AddProperty(p); + } + } + + double portion = 1.0 / (count - 1); + for (size_t i = 0; i < count - 1; ++i) { + nsTArray toValues; + float toKey = (i + 1) * portion; + if (!StyleAnimationValue::ComputeValues(pair.mProperty, + nsCSSProps::eEnabledForAllContent, + aTarget, + pair.mValues[i + 1], + /* aUseSVGMode */ false, + toValues)) { + if (i + 1 == count - 1) { + // We need to throw for an invalid last value, since that would + // imply an additive animation, which we don't support yet. + aRv.Throw(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR); + return; + } + // Otherwise, skip the segment. + continue; + } + MOZ_ASSERT(toValues.Length() == fromValues.Length(), + "should get the same number of properties as the last time " + "we called ComputeValues for pair.mProperty"); + for (size_t j = 0, n = toValues.Length(); j < n; ++j) { + size_t index = animationPropertyIndexes[j]; + AnimationPropertySegment* segment = + aResult[index].mSegments.AppendElement(); + segment->mFromKey = fromKey; + segment->mFromValue = fromValues[j].mValue; + segment->mToKey = toKey; + segment->mToValue = toValues[j].mValue; + segment->mTimingFunction = easing; + } + fromValues = Move(toValues); + fromKey = toKey; + } + } +} + +/** + * Converts a JS value to an IDL + * (PropertyIndexedKeyframes or sequence) value and builds an + * array of AnimationProperty objects for the keyframe animation + * that it specifies. + * + * @param aTarget The target of the animation, used to resolve style + * for a property's underlying value if needed. + * @param aFrames The JS value, provided as an optional IDL |object?| value, + * that is the keyframe list specification. + * @param aResult The array into which the resulting AnimationProperty + * objects will be appended. + */ +/* static */ void +KeyframeEffectReadOnly::BuildAnimationPropertyList( + JSContext* aCx, + Element* aTarget, + const Optional>& aFrames, + InfallibleTArray& aResult, + ErrorResult& aRv) +{ + MOZ_ASSERT(aResult.IsEmpty()); + + // A frame list specification in the IDL is: + // + // (PropertyIndexedKeyframes or sequence or SharedKeyframeList) + // + // We don't support SharedKeyframeList yet, but we do the other two. We + // manually implement the parts of JS-to-IDL union conversion algorithm + // from the Web IDL spec, since we have to represent this an object? so + // we can look at the open-ended set of properties on a + // PropertyIndexedKeyframes or Keyframe. + + if (!aFrames.WasPassed() || !aFrames.Value().get()) { + // The argument was omitted, or was explicitly null. In both cases, + // the default dictionary value for PropertyIndexedKeyframes would + // result in no keyframes. + return; + } + + // At this point we know we have an object. We try to convert it to a + // sequence first, and if that fails due to not being iterable, + // we try to convert it to PropertyIndexedKeyframes. + JS::Rooted objectValue(aCx, JS::ObjectValue(*aFrames.Value())); + JS::ForOfIterator iter(aCx); + if (!iter.init(objectValue, JS::ForOfIterator::AllowNonIterable)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + if (iter.valueIsIterable()) { + BuildAnimationPropertyListFromKeyframeSequence(aCx, aTarget, iter, + aResult, aRv); + } else { + BuildAnimationPropertyListFromPropertyIndexedKeyframes(aCx, aTarget, + objectValue, aResult, + aRv); + } +} + +/* static */ already_AddRefed +KeyframeEffectReadOnly::Constructor( + const GlobalObject& aGlobal, + Element* aTarget, + const Optional>& aFrames, + const Optional& aOptions, + ErrorResult& aRv) +{ + if (!aTarget) { + // We don't support null targets yet. + aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR); + return nullptr; + } + + AnimationTiming timing = ConvertKeyframeEffectOptions(aOptions); + + InfallibleTArray animationProperties; + BuildAnimationPropertyList(aGlobal.Context(), aTarget, aFrames, + animationProperties, aRv); + + if (aRv.Failed()) { + return nullptr; + } + + RefPtr effect = + new KeyframeEffectReadOnly(aTarget->OwnerDoc(), aTarget, + nsCSSPseudoElements::ePseudo_NotPseudoElement, + timing); + effect->mProperties = Move(animationProperties); + return effect.forget(); +} void KeyframeEffectReadOnly::GetFrames(JSContext*& aCx, nsTArray& aResult, ErrorResult& aRv) { - // Collect tuples of the form (offset, property, value, easing) from - // mProperties, then sort them so we can generate one ComputedKeyframe per - // offset/easing pair. We sort secondarily by property IDL name so that we - // have a uniform order that we set properties on the ComputedKeyframe - // object. - nsAutoTArray entries; + nsTArray entries; + for (const AnimationProperty& property : mProperties) { - if (property.mSegments.IsEmpty()) { - continue; - } for (size_t i = 0, n = property.mSegments.Length(); i < n; i++) { const AnimationPropertySegment& segment = property.mSegments[i]; - KeyframeValueEntry* entry = entries.AppendElement(); - entry->mOffset = segment.mFromKey; + + // We append the mFromValue for each segment. If the mToValue + // differs from the following segment's mFromValue, or if we're on + // the last segment, then we append the mToValue as well. + // + // Each value is annotated with whether it is a "first", "left", "right", + // or "last" value. "left" and "right" values represent the value coming + // in to and out of a given offset, in the middle of an animation. For + // most segments, the mToValue is the "left" and the following segment's + // mFromValue is the "right". The "first" and "last" values are the + // additional values assigned to offset 0 or 1 for reverse and forward + // filling. These annotations are used to ensure multiple values for a + // given property are sorted correctly and that we do not merge Keyframes + // with different values for the same offset. + + OrderedKeyframeValueEntry* entry = entries.AppendElement(); entry->mProperty = property.mProperty; + entry->mValue = segment.mFromValue; + entry->mOffset = segment.mFromKey; entry->mTimingFunction = &segment.mTimingFunction; - StyleAnimationValue::UncomputeValue(property.mProperty, - segment.mFromValue, - entry->mValue); + entry->mPosition = + segment.mFromKey == segment.mToKey && segment.mFromKey == 0.0f ? + ValuePosition::First : + ValuePosition::Right; + + if (i == n - 1 || + segment.mToValue != property.mSegments[i + 1].mFromValue) { + entry = entries.AppendElement(); + entry->mProperty = property.mProperty; + entry->mValue = segment.mToValue; + entry->mOffset = segment.mToKey; + entry->mTimingFunction = &segment.mTimingFunction; + entry->mPosition = + segment.mFromKey == segment.mToKey && segment.mToKey == 1.0f ? + ValuePosition::Last : + ValuePosition::Left; + } } - const AnimationPropertySegment& segment = property.mSegments.LastElement(); - KeyframeValueEntry* entry = entries.AppendElement(); - entry->mOffset = segment.mToKey; - entry->mProperty = property.mProperty; - // We don't have the an appropriate animation-timing-function value to use, - // either from the element or from the 100% keyframe, so we just set it to - // the animation-timing-value value used on the previous segment. - entry->mTimingFunction = &segment.mTimingFunction; - StyleAnimationValue::UncomputeValue(property.mProperty, - segment.mToValue, - entry->mValue); } - entries.Sort(); + + entries.Sort(OrderedKeyframeValueEntry::ForKeyframeGenerationComparator()); for (size_t i = 0, n = entries.Length(); i < n; ) { + OrderedKeyframeValueEntry* entry = &entries[i]; + OrderedKeyframeValueEntry* previousEntry = nullptr; + // Create a JS object with the explicit ComputedKeyframe dictionary members. ComputedKeyframe keyframeDict; - keyframeDict.mOffset.SetValue(entries[i].mOffset); - keyframeDict.mComputedOffset.Construct(entries[i].mOffset); - keyframeDict.mEasing.Truncate(); - entries[i].mTimingFunction->AppendToString(keyframeDict.mEasing); + keyframeDict.mOffset.SetValue(entry->mOffset); + keyframeDict.mComputedOffset.Construct(entry->mOffset); + if (entry->mTimingFunction) { + // If null, leave easing as its default "linear". + keyframeDict.mEasing.Truncate(); + entry->mTimingFunction->AppendToString(keyframeDict.mEasing); + } keyframeDict.mComposite.SetValue(CompositeOperation::Replace); - JS::Rooted keyframeValue(aCx); - if (!ToJSValue(aCx, keyframeDict, &keyframeValue)) { + JS::Rooted keyframeJSValue(aCx); + if (!ToJSValue(aCx, keyframeDict, &keyframeJSValue)) { aRv.Throw(NS_ERROR_FAILURE); return; } - JS::Rooted keyframe(aCx, &keyframeValue.toObject()); - - // Set the property name/value pairs on the JS object. + JS::Rooted keyframe(aCx, &keyframeJSValue.toObject()); do { - const KeyframeValueEntry& entry = entries[i]; - const char* name = nsCSSProps::PropertyIDLName(entry.mProperty); + const char* name = nsCSSProps::PropertyIDLName(entry->mProperty); + nsString stringValue; + StyleAnimationValue::UncomputeValue(entry->mProperty, + entry->mValue, + stringValue); JS::Rooted value(aCx); - if (!ToJSValue(aCx, entry.mValue, &value) || + if (!ToJSValue(aCx, stringValue, &value) || !JS_DefineProperty(aCx, keyframe, name, value, JSPROP_ENUMERATE)) { aRv.Throw(NS_ERROR_FAILURE); return; } - ++i; - } while (i < n && - entries[i].mOffset == entries[i - 1].mOffset && - *entries[i].mTimingFunction == *entries[i - 1].mTimingFunction); + if (++i == n) { + break; + } + previousEntry = entry; + entry = &entries[i]; + } while (entry->SameKeyframe(*previousEntry)); aResult.AppendElement(keyframe); } diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index f14277c11a..41e83411f8 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -195,6 +195,8 @@ struct ElementPropertyTransition; namespace dom { +class Animation; + class KeyframeEffectReadOnly : public AnimationEffectReadOnly { public: @@ -217,6 +219,12 @@ public: } // KeyframeEffectReadOnly interface + static already_AddRefed + Constructor(const GlobalObject& aGlobal, + Element* aTarget, + const Optional>& aFrames, + const Optional& aOptions, + ErrorResult& aRv); Element* GetTarget() const { // Currently we never return animations from the API whose effect // targets a pseudo-element so this should never be called when @@ -238,32 +246,15 @@ public: aPseudoType = mPseudoType; } - void SetParentTime(Nullable aParentTime); - const AnimationTiming& Timing() const { return mTiming; } AnimationTiming& Timing() { return mTiming; } + void SetTiming(const AnimationTiming& aTiming); - // FIXME: Drop |aOwningAnimation| once we make AnimationEffects track their - // owning animation. - void SetTiming(const AnimationTiming& aTiming, Animation& aOwningAnimtion); - - // Return the duration from the start the active interval to the point where - // the animation begins playback. This is zero unless the animation has - // a negative delay in which case it is the absolute value of the delay. - // This is used for setting the elapsedTime member of CSS AnimationEvents. - TimeDuration InitialAdvance() const { - return std::max(TimeDuration(), mTiming.mDelay * -1); - } - - Nullable GetLocalTime() const { - // Since the *animation* start time is currently always zero, the local - // time is equal to the parent time. - return mParentTime; - } + Nullable GetLocalTime() const; // This function takes as input the timing parameters of an animation and // returns the computed timing at the specified local time. @@ -290,10 +281,12 @@ public: static StickyTimeDuration ActiveDuration(const AnimationTiming& aTiming); - bool IsInPlay(const Animation& aAnimation) const; - bool IsCurrent(const Animation& aAnimation) const; + bool IsInPlay() const; + bool IsCurrent() const; bool IsInEffect() const; + void SetAnimation(Animation* aAnimation); + const AnimationProperty* GetAnimationOfProperty(nsCSSProperty aProperty) const; bool HasAnimationOfProperty(nsCSSProperty aProperty) const { @@ -309,8 +302,8 @@ public: } // Updates |aStyleRule| with the animation values produced by this - // Animation for the current time except any properties already contained - // in |aSetProperties|. + // AnimationEffect for the current time except any properties already + // contained in |aSetProperties|. // Any updated properties are added to |aSetProperties|. void ComposeStyle(RefPtr& aStyleRule, nsCSSPropertySet& aSetProperties); @@ -318,11 +311,20 @@ public: void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning); protected: - virtual ~KeyframeEffectReadOnly() { } + virtual ~KeyframeEffectReadOnly(); void ResetIsRunningOnCompositor(); + static AnimationTiming ConvertKeyframeEffectOptions( + const Optional& aOptions); + static void BuildAnimationPropertyList( + JSContext* aCx, + Element* aTarget, + const Optional>& aFrames, + InfallibleTArray& aResult, + ErrorResult& aRv); + nsCOMPtr mTarget; - Nullable mParentTime; + RefPtr mAnimation; AnimationTiming mTiming; nsCSSPseudoElements::Type mPseudoType; diff --git a/dom/animation/test/css-animations/file_keyframeeffect-getframes.html b/dom/animation/test/css-animations/file_keyframeeffect-getframes.html index c501166eec..f127dd22cf 100644 --- a/dom/animation/test/css-animations/file_keyframeeffect-getframes.html +++ b/dom/animation/test/css-animations/file_keyframeeffect-getframes.html @@ -24,6 +24,12 @@ to { color: white; } } +@keyframes anim-simple-three { + from { color: black; } + 50% { color: blue; } + to { color: white; } +} + @keyframes anim-simple-timing { from { color: black; animation-timing-function: linear; } 50% { color: blue; animation-timing-function: ease-in-out; } @@ -183,10 +189,10 @@ test(function(t) { kTimingFunctionValues.forEach(function(easing) { var div = addDiv(t); - div.style.animation = 'anim-simple 100s ' + easing; + div.style.animation = 'anim-simple-three 100s ' + easing; var frames = getFrames(div); - assert_equals(frames.length, 2, "number of frames"); + assert_equals(frames.length, 3, "number of frames"); for (var i = 0; i < frames.length; i++) { assert_equals(frames[i].easing, easing, @@ -364,7 +370,7 @@ test(function(t) { { offset: 1, computedOffset: 1, easing: "ease-in", composite: "replace", marginTop: "16px" }, { offset: 1, computedOffset: 1, easing: "step-end", composite: "replace", - color: "rgb(255, 255, 255)", }, + color: "rgb(255, 255, 255)" }, ]; for (var i = 0; i < frames.length; i++) { diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini index e8d2c951e0..a7a28f5219 100644 --- a/dom/animation/test/mochitest.ini +++ b/dom/animation/test/mochitest.ini @@ -71,3 +71,5 @@ support-files = mozilla/file_deferred_start.html skip-if = (toolkit == 'gonk' && debug) [mozilla/test_hide_and_show.html] support-files = mozilla/file_hide_and_show.html +[mozilla/test_partial_keyframes.html] +support-files = mozilla/file_partial_keyframes.html diff --git a/dom/animation/test/mozilla/file_partial_keyframes.html b/dom/animation/test/mozilla/file_partial_keyframes.html new file mode 100644 index 0000000000..68832be7a0 --- /dev/null +++ b/dom/animation/test/mozilla/file_partial_keyframes.html @@ -0,0 +1,41 @@ + + + + + + diff --git a/dom/animation/test/mozilla/test_partial_keyframes.html b/dom/animation/test/mozilla/test_partial_keyframes.html new file mode 100644 index 0000000000..28eb4c5881 --- /dev/null +++ b/dom/animation/test/mozilla/test_partial_keyframes.html @@ -0,0 +1,14 @@ + + + + +
+ diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg index fa4f4ccc06..bf6e5b0379 100644 --- a/dom/base/domerr.msg +++ b/dom/base/domerr.msg @@ -115,6 +115,11 @@ DOM4_MSG_DEF(BtAuthFailureError, "Authentication failure", NS_ERROR_DOM_BLUETO DOM4_MSG_DEF(BtRmtDevDownError, "Remote device down", NS_ERROR_DOM_BLUETOOTH_RMT_DEV_DOWN) DOM4_MSG_DEF(BtAuthRejectedError, "Authentication rejected", NS_ERROR_DOM_BLUETOOTH_AUTH_REJECTED) +/* Web Animations errors */ + +DOM4_MSG_DEF(NotSupportedError, "Animation to or from an underlying value is not yet supported.", NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR) +DOM4_MSG_DEF(NotSupportedError, "Animation with no target is not yet supported.", NS_ERROR_DOM_ANIM_NO_TARGET_ERR) + /* common global codes (from nsError.h) */ DOM_MSG_DEF(NS_OK , "Success") diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index e179176c00..e78df4ac26 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -3140,7 +3140,8 @@ nsContentUtils::IsImageInCache(nsIURI* aURI, nsIDocument* aDocument) // If something unexpected happened we return false, otherwise if props // is set, the image is cached and we return true nsCOMPtr props; - nsresult rv = cache->FindEntryProperties(aURI, getter_AddRefs(props)); + nsCOMPtr domDoc = do_QueryInterface(aDocument); + nsresult rv = cache->FindEntryProperties(aURI, domDoc, getter_AddRefs(props)); return (NS_SUCCEEDED(rv) && props); } diff --git a/dom/base/nsDOMAttributeMap.cpp b/dom/base/nsDOMAttributeMap.cpp index 87e58627be..93e686da0d 100644 --- a/dom/base/nsDOMAttributeMap.cpp +++ b/dom/base/nsDOMAttributeMap.cpp @@ -557,13 +557,6 @@ nsDOMAttributeMap::Count() const return mAttributeCache.Count(); } -uint32_t -nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc, - void *aUserArg) const -{ - return mAttributeCache.EnumerateRead(aFunc, aUserArg); -} - size_t nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { diff --git a/dom/base/nsDOMAttributeMap.h b/dom/base/nsDOMAttributeMap.h index 6109810325..7ebe61aa13 100644 --- a/dom/base/nsDOMAttributeMap.h +++ b/dom/base/nsDOMAttributeMap.h @@ -128,13 +128,7 @@ public: typedef nsRefPtrHashtable AttrCache; - /** - * Enumerates over the attribute nodess in the map and calls aFunc for each - * one. If aFunc returns PL_DHASH_STOP we'll stop enumerating at that point. - * - * @return The number of attribute nodes that aFunc was called for. - */ - uint32_t Enumerate(AttrCache::EnumReadFunction aFunc, void *aUserArg) const; + static void BlastSubtreeToPieces(nsINode *aNode); Element* GetParentObject() const { diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 672429737b..7c99937f34 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -3265,7 +3265,7 @@ convertSheetType(uint32_t aSheetType) default: NS_ASSERTION(false, "wrong type"); // we must return something although this should never happen - return nsIDocument::SheetTypeCount; + return nsIDocument::AdditionalSheetTypeCount; } } diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index 20d2f7c24a..a2e4f3c0c6 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -69,7 +69,8 @@ #include "mozilla/dom/TreeWalker.h" #include "nsIServiceManager.h" -#include "nsIServiceWorkerManager.h" +#include "mozilla/dom/workers/ServiceWorkerManager.h" +#include "imgLoader.h" #include "nsCanvasFrame.h" #include "nsContentCID.h" @@ -376,30 +377,14 @@ CustomDefinitionsTraverse(CustomElementHashKey* aKey, return PL_DHASH_NEXT; } -struct CustomDefinitionTraceArgs -{ - const TraceCallbacks& callbacks; - void* closure; -}; - -static PLDHashOperator -CustomDefinitionTrace(CustomElementHashKey *aKey, - CustomElementDefinition *aData, - void *aArg) -{ - CustomDefinitionTraceArgs* traceArgs = static_cast(aArg); - MOZ_ASSERT(aData, "Definition must not be null"); - traceArgs->callbacks.Trace(&aData->mPrototype, "mCustomDefinitions prototype", - traceArgs->closure); - return PL_DHASH_NEXT; -} - NS_IMPL_CYCLE_COLLECTION_CLASS(Registry) NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Registry) - CustomDefinitionTraceArgs customDefinitionArgs = { aCallbacks, aClosure }; - tmp->mCustomDefinitions.EnumerateRead(CustomDefinitionTrace, - &customDefinitionArgs); + for (auto iter = tmp->mCustomDefinitions.Iter(); !iter.Done(); iter.Next()) { + aCallbacks.Trace(&iter.UserData()->mPrototype, + "mCustomDefinitions prototype", + aClosure); + } NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Registry) @@ -929,57 +914,16 @@ nsExternalResourceMap::RequestResource(nsIURI* aURI, return nullptr; } -struct -nsExternalResourceEnumArgs -{ - nsIDocument::nsSubDocEnumFunc callback; - void *data; -}; - -static PLDHashOperator -ExternalResourceEnumerator(nsIURI* aKey, - nsExternalResourceMap::ExternalResource* aData, - void* aClosure) -{ - nsExternalResourceEnumArgs* args = - static_cast(aClosure); - bool next = - aData->mDocument ? args->callback(aData->mDocument, args->data) : true; - return next ? PL_DHASH_NEXT : PL_DHASH_STOP; -} - void nsExternalResourceMap::EnumerateResources(nsIDocument::nsSubDocEnumFunc aCallback, void* aData) { - nsExternalResourceEnumArgs args = { aCallback, aData }; - mMap.EnumerateRead(ExternalResourceEnumerator, &args); -} - -static PLDHashOperator -ExternalResourceTraverser(nsIURI* aKey, - nsExternalResourceMap::ExternalResource* aData, - void* aClosure) -{ - nsCycleCollectionTraversalCallback *cb = - static_cast(aClosure); - - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, - "mExternalResourceMap.mMap entry" - "->mDocument"); - cb->NoteXPCOMChild(aData->mDocument); - - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, - "mExternalResourceMap.mMap entry" - "->mViewer"); - cb->NoteXPCOMChild(aData->mViewer); - - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, - "mExternalResourceMap.mMap entry" - "->mLoadGroup"); - cb->NoteXPCOMChild(aData->mLoadGroup); - - return PL_DHASH_NEXT; + for (auto iter = mMap.Iter(); !iter.Done(); iter.Next()) { + nsExternalResourceMap::ExternalResource* resource = iter.UserData(); + if (resource->mDocument && !aCallback(resource->mDocument, aData)) { + break; + } + } } void @@ -987,7 +931,24 @@ nsExternalResourceMap::Traverse(nsCycleCollectionTraversalCallback* aCallback) c { // mPendingLoads will get cleared out as the requests complete, so // no need to worry about those here. - mMap.EnumerateRead(ExternalResourceTraverser, aCallback); + for (auto iter = mMap.ConstIter(); !iter.Done(); iter.Next()) { + nsExternalResourceMap::ExternalResource* resource = iter.UserData(); + + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, + "mExternalResourceMap.mMap entry" + "->mDocument"); + aCallback->NoteXPCOMChild(resource->mDocument); + + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, + "mExternalResourceMap.mMap entry" + "->mViewer"); + aCallback->NoteXPCOMChild(resource->mViewer); + + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback, + "mExternalResourceMap.mMap entry" + "->mLoadGroup"); + aCallback->NoteXPCOMChild(resource->mLoadGroup); + } } static PLDHashOperator @@ -2099,9 +2060,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument) tmp->mInUnlinkOrDeletion = false; NS_IMPL_CYCLE_COLLECTION_UNLINK_END -static bool sPrefsInitialized = false; -static uint32_t sOnloadDecodeLimit = 0; - nsresult nsDocument::Init() { @@ -2109,11 +2067,6 @@ nsDocument::Init() return NS_ERROR_ALREADY_INITIALIZED; } - if (!sPrefsInitialized) { - sPrefsInitialized = true; - Preferences::AddUintVarCache(&sOnloadDecodeLimit, "image.onload.decode.limit", 0); - } - // Force initialization. nsINode::nsSlots* slots = Slots(); @@ -2395,7 +2348,7 @@ nsDocument::RemoveDocStyleSheetsFromStyleSets() } void -nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray& aSheets, nsStyleSet::sheetType aType) +nsDocument::RemoveStyleSheetsFromStyleSets(nsCOMArray& aSheets, SheetType aType) { // The stylesheets should forget us int32_t indx = aSheets.Count(); @@ -2422,16 +2375,17 @@ nsDocument::ResetStylesheetsToURI(nsIURI* aURI) mozAutoDocUpdate upd(this, UPDATE_STYLE, true); RemoveDocStyleSheetsFromStyleSets(); - RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, nsStyleSet::eAgentSheet); - RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], nsStyleSet::eAgentSheet); - RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], nsStyleSet::eUserSheet); - RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], nsStyleSet::eDocSheet); + RemoveStyleSheetsFromStyleSets(mOnDemandBuiltInUASheets, SheetType::Agent); + RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAgentSheet], SheetType::Agent); + RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eUserSheet], SheetType::User); + RemoveStyleSheetsFromStyleSets(mAdditionalSheets[eAuthorSheet], SheetType::Doc); // Release all the sheets mStyleSheets.Clear(); mOnDemandBuiltInUASheets.Clear(); - for (uint32_t i = 0; i < SheetTypeCount; ++i) - mAdditionalSheets[i].Clear(); + for (auto& sheets : mAdditionalSheets) { + sheets.Clear(); + } // NOTE: We don't release the catalog sheets. It doesn't really matter // now, but it could in the future -- in which case not releasing them @@ -2465,14 +2419,14 @@ static bool AppendAuthorSheet(nsIStyleSheet *aSheet, void *aData) { nsStyleSet *styleSet = static_cast(aData); - styleSet->AppendStyleSheet(nsStyleSet::eDocSheet, aSheet); + styleSet->AppendStyleSheet(SheetType::Doc, aSheet); return true; } static void AppendSheetsToStyleSet(nsStyleSet* aStyleSet, const nsCOMArray& aSheets, - nsStyleSet::sheetType aType) + SheetType aType) { for (int32_t i = aSheets.Count() - 1; i >= 0; --i) { aStyleSet->AppendStyleSheet(aType, aSheets[i]); @@ -2484,14 +2438,9 @@ void nsDocument::FillStyleSet(nsStyleSet* aStyleSet) { NS_PRECONDITION(aStyleSet, "Must have a style set"); - NS_PRECONDITION(aStyleSet->SheetCount(nsStyleSet::eDocSheet) == 0, + NS_PRECONDITION(aStyleSet->SheetCount(SheetType::Doc) == 0, "Style set already has document sheets?"); - // We could consider moving this to nsStyleSet::Init, to match its - // handling of the eAnimationSheet and eTransitionSheet levels. - aStyleSet->DirtyRuleProcessors(nsStyleSet::ePresHintSheet); - aStyleSet->DirtyRuleProcessors(nsStyleSet::eStyleAttrSheet); - int32_t i; for (i = mStyleSheets.Count() - 1; i >= 0; --i) { nsIStyleSheet* sheet = mStyleSheets[i]; @@ -2510,16 +2459,16 @@ nsDocument::FillStyleSet(nsStyleSet* aStyleSet) for (i = mOnDemandBuiltInUASheets.Count() - 1; i >= 0; --i) { nsIStyleSheet* sheet = mOnDemandBuiltInUASheets[i]; if (sheet->IsApplicable()) { - aStyleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet); + aStyleSet->PrependStyleSheet(SheetType::Agent, sheet); } } AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAgentSheet], - nsStyleSet::eAgentSheet); + SheetType::Agent); AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eUserSheet], - nsStyleSet::eUserSheet); + SheetType::User); AppendSheetsToStyleSet(aStyleSet, mAdditionalSheets[eAuthorSheet], - nsStyleSet::eDocSheet); + SheetType::Doc); } static void @@ -4180,7 +4129,7 @@ nsDocument::AddOnDemandBuiltInUASheet(CSSStyleSheet* aSheet) // do not override Firefox OS/Mobile's content.css sheet. Maybe we should // have an insertion point to match the order of // nsDocumentViewer::CreateStyleSet though? - shell->StyleSet()->PrependStyleSheet(nsStyleSet::eAgentSheet, aSheet); + shell->StyleSet()->PrependStyleSheet(SheetType::Agent, aSheet); } } @@ -4412,20 +4361,20 @@ nsDocument::NotifyStyleSheetApplicableStateChanged() } } -static nsStyleSet::sheetType +static SheetType ConvertAdditionalSheetType(nsIDocument::additionalSheetType aType) { switch(aType) { case nsIDocument::eAgentSheet: - return nsStyleSet::eAgentSheet; + return SheetType::Agent; case nsIDocument::eUserSheet: - return nsStyleSet::eUserSheet; + return SheetType::User; case nsIDocument::eAuthorSheet: - return nsStyleSet::eDocSheet; + return SheetType::Doc; default: - NS_ASSERTION(false, "wrong type"); + MOZ_ASSERT(false, "wrong type"); // we must return something although this should never happen - return nsStyleSet::eSheetTypeCount; + return SheetType::Count; } } @@ -4480,7 +4429,7 @@ nsDocument::AddAdditionalStyleSheet(additionalSheetType aType, nsIStyleSheet* aS BeginUpdate(UPDATE_STYLE); nsCOMPtr shell = GetShell(); if (shell) { - nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); + SheetType type = ConvertAdditionalSheetType(aType); shell->StyleSet()->AppendStyleSheet(type, aSheet); } @@ -4508,7 +4457,7 @@ nsDocument::RemoveAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheet MOZ_ASSERT(sheetRef->IsApplicable()); nsCOMPtr shell = GetShell(); if (shell) { - nsStyleSet::sheetType type = ConvertAdditionalSheetType(aType); + SheetType type = ConvertAdditionalSheetType(aType); shell->StyleSet()->RemoveStyleSheet(type, sheetRef); } } @@ -7609,42 +7558,36 @@ nsIDocument::GetCompatMode(nsString& aCompatMode) const } } -static void BlastSubtreeToPieces(nsINode *aNode); - -PLDHashOperator -BlastFunc(nsAttrHashKey::KeyType aKey, Attr *aData, void* aUserArg) -{ - nsCOMPtr *attr = - static_cast*>(aUserArg); - - *attr = aData; - - NS_ASSERTION(attr->get(), - "non-nsIAttribute somehow made it into the hashmap?!"); - - return PL_DHASH_STOP; -} - -static void -BlastSubtreeToPieces(nsINode *aNode) +void +nsDOMAttributeMap::BlastSubtreeToPieces(nsINode *aNode) { if (aNode->IsElement()) { Element *element = aNode->AsElement(); const nsDOMAttributeMap *map = element->GetAttributeMap(); if (map) { nsCOMPtr attr; - while (map->Enumerate(BlastFunc, &attr) > 0) { + + // This non-standard style of iteration is presumably used because some + // of the code in the loop body can trigger element removal, which + // invalidates the iterator. + while (true) { + auto iter = map->mAttributeCache.ConstIter(); + if (iter.Done()) { + break; + } + nsCOMPtr attr = iter.UserData(); + NS_ASSERTION(attr.get(), + "non-nsIAttribute somehow made it into the hashmap?!"); + BlastSubtreeToPieces(attr); -#ifdef DEBUG - nsresult rv = -#endif + DebugOnly rv = element->UnsetAttr(attr->NodeInfo()->NamespaceID(), attr->NodeInfo()->NameAtom(), false); // XXX Should we abort here? - NS_ASSERTION(NS_SUCCEEDED(rv), "Uhoh, UnsetAttr shouldn't fail!"); + NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!"); } } } @@ -7811,7 +7754,7 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) if (rv.Failed()) { // Disconnect all nodes from their parents, since some have the old document // as their ownerDocument and some have this as their ownerDocument. - BlastSubtreeToPieces(adoptedNode); + nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode); if (!sameDocument && oldDocument) { uint32_t count = nodesWithProperties.Count(); @@ -7840,7 +7783,7 @@ nsIDocument::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) if (rv.Failed()) { // Disconnect all nodes from their parents. - BlastSubtreeToPieces(adoptedNode); + nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode); return nullptr; } @@ -8869,11 +8812,6 @@ nsDocument::Destroy() mRegistry = nullptr; - nsCOMPtr swm = mozilla::services::GetServiceWorkerManager(); - if (swm) { - swm->MaybeStopControlling(this); - } - // XXX We really should let cycle collection do this, but that currently still // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684). ReleaseWrapper(static_cast(this)); @@ -8885,6 +8823,19 @@ nsDocument::RemovedFromDocShell() if (mRemovedFromDocShell) return; + using mozilla::dom::workers::ServiceWorkerManager; + RefPtr swm = ServiceWorkerManager::GetInstance(); + if (swm) { + ErrorResult error; + if (swm->IsControlled(this, error)) { + imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this); + if (loader) { + loader->ClearCacheForControlledDocument(this); + } + } + swm->MaybeStopControlling(this); + } + mRemovedFromDocShell = true; EnumerateActivityObservers(NotifyActivityChanged, nullptr); @@ -10454,12 +10405,8 @@ nsDocument::AddImage(imgIRequest* aImage) // If this is the first insertion and we're locking images, lock this image // too. - if (oldCount == 0) { - if (mLockingImages) - rv = aImage->LockImage(); - if (NS_SUCCEEDED(rv) && (!sOnloadDecodeLimit || - mImageTracker.Count() < sOnloadDecodeLimit)) - rv = aImage->StartDecoding(); + if (oldCount == 0 && mLockingImages) { + rv = aImage->LockImage(); } // If this is the first insertion and we're animating images, request @@ -10590,7 +10537,6 @@ PLDHashOperator LockEnumerator(imgIRequest* aKey, void* userArg) { aKey->LockImage(); - aKey->RequestDecode(); return PL_DHASH_NEXT; } @@ -11640,6 +11586,19 @@ nsDocument::FullScreenStackTop() return element; } +/* virtual */ nsTArray +nsDocument::GetFullscreenStack() const +{ + nsTArray elements; + for (const nsWeakPtr& ptr : mFullScreenStack) { + if (nsCOMPtr elem = do_QueryReferent(ptr)) { + MOZ_ASSERT(elem->State().HasState(NS_EVENT_STATE_FULL_SCREEN)); + elements.AppendElement(elem); + } + } + return elements; +} + // Returns true if aDoc is in the focused tab in the active window. static bool IsInActiveTab(nsIDocument* aDoc) diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 82852ad3c1..2496b8aae8 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -1183,6 +1183,7 @@ public: virtual Element* FindImageMap(const nsAString& aNormalizedMapName) override; virtual Element* GetFullScreenElement() override; + virtual nsTArray GetFullscreenStack() const override; virtual void AsyncRequestFullScreen(Element* aElement, mozilla::dom::FullScreenOptions& aOptions) override; virtual void RestorePreviousFullScreenState() override; @@ -1502,7 +1503,7 @@ protected: void RemoveDocStyleSheetsFromStyleSets(); void RemoveStyleSheetsFromStyleSets(nsCOMArray& aSheets, - nsStyleSet::sheetType aType); + mozilla::SheetType aType); void ResetStylesheetsToURI(nsIURI* aURI); void FillStyleSet(nsStyleSet* aStyleSet); @@ -1552,7 +1553,7 @@ protected: nsCOMArray mStyleSheets; nsCOMArray mOnDemandBuiltInUASheets; - nsCOMArray mAdditionalSheets[SheetTypeCount]; + nsCOMArray mAdditionalSheets[AdditionalSheetTypeCount]; // Array of observers nsTObserverArray mObservers; diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 19df14811e..36cf38521f 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -121,26 +121,17 @@ nsFrameMessageManager::~nsFrameMessageManager() } } -static PLDHashOperator -CycleCollectorTraverseListeners(const nsAString& aKey, - nsAutoTObserverArray* aListeners, - void* aCb) -{ - nsCycleCollectionTraversalCallback* cb = - static_cast (aCb); - uint32_t count = aListeners->Length(); - for (uint32_t i = 0; i < count; ++i) { - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "listeners[i] mStrongListener"); - cb->NoteXPCOMChild(aListeners->ElementAt(i).mStrongListener.get()); - } - return PL_DHASH_NEXT; -} - NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameMessageManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameMessageManager) - tmp->mListeners.EnumerateRead(CycleCollectorTraverseListeners, - static_cast(&cb)); + for (auto iter = tmp->mListeners.Iter(); !iter.Done(); iter.Next()) { + nsAutoTObserverArray* listeners = iter.UserData(); + uint32_t count = listeners->Length(); + for (uint32_t i = 0; i < count; ++i) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "listeners[i] mStrongListener"); + cb.NoteXPCOMChild(listeners->ElementAt(i).mStrongListener.get()); + } + } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildManagers) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentManager) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS @@ -419,35 +410,6 @@ nsFrameMessageManager::RemoveMessageListener(const nsAString& aMessage, return NS_OK; } -#ifdef DEBUG -typedef struct -{ - nsCOMPtr mCanonical; - nsWeakPtr mWeak; -} CanonicalCheckerParams; - -static PLDHashOperator -CanonicalChecker(const nsAString& aKey, - nsAutoTObserverArray* aListeners, - void* aParams) -{ - CanonicalCheckerParams* params = - static_cast (aParams); - - uint32_t count = aListeners->Length(); - for (uint32_t i = 0; i < count; i++) { - if (!aListeners->ElementAt(i).mWeakListener) { - continue; - } - nsCOMPtr otherCanonical = - do_QueryReferent(aListeners->ElementAt(i).mWeakListener); - MOZ_ASSERT((params->mCanonical == otherCanonical) == - (params->mWeak == aListeners->ElementAt(i).mWeakListener)); - } - return PL_DHASH_NEXT; -} -#endif - NS_IMETHODIMP nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage, nsIMessageListener* aListener) @@ -461,10 +423,17 @@ nsFrameMessageManager::AddWeakMessageListener(const nsAString& aMessage, // this to happen; it will break e.g. RemoveWeakMessageListener. So let's // check that we're not getting ourselves into that situation. nsCOMPtr canonical = do_QueryInterface(aListener); - CanonicalCheckerParams params; - params.mCanonical = canonical; - params.mWeak = weak; - mListeners.EnumerateRead(CanonicalChecker, (void*)¶ms); + for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) { + nsAutoTObserverArray* listeners = iter.UserData(); + uint32_t count = listeners->Length(); + for (uint32_t i = 0; i < count; i++) { + nsWeakPtr weakListener = listeners->ElementAt(i).mWeakListener; + if (weakListener) { + nsCOMPtr otherCanonical = do_QueryReferent(weakListener); + MOZ_ASSERT((canonical == otherCanonical) == (weak == weakListener)); + } + } + } #endif nsAutoTObserverArray* listeners = @@ -1488,55 +1457,45 @@ protected: NS_IMPL_ISUPPORTS(MessageManagerReporter, nsIMemoryReporter) -static PLDHashOperator -CollectMessageListenerData(const nsAString& aKey, - nsAutoTObserverArray* aListeners, - void* aData) -{ - MessageManagerReferentCount* referentCount = - static_cast(aData); - - uint32_t listenerCount = aListeners->Length(); - if (!listenerCount) { - return PL_DHASH_NEXT; - } - - nsString key(aKey); - uint32_t oldCount = 0; - referentCount->mMessageCounter.Get(key, &oldCount); - uint32_t currentCount = oldCount + listenerCount; - referentCount->mMessageCounter.Put(key, currentCount); - - // Keep track of messages that have a suspiciously large - // number of referents (symptom of leak). - if (currentCount == MessageManagerReporter::kSuspectReferentCount) { - referentCount->mSuspectMessages.AppendElement(key); - } - - for (uint32_t i = 0; i < listenerCount; ++i) { - const nsMessageListenerInfo& listenerInfo = - aListeners->ElementAt(i); - if (listenerInfo.mWeakListener) { - nsCOMPtr referent = - do_QueryReferent(listenerInfo.mWeakListener); - if (referent) { - referentCount->mWeakAlive++; - } else { - referentCount->mWeakDead++; - } - } else { - referentCount->mStrong++; - } - } - return PL_DHASH_NEXT; -} - void MessageManagerReporter::CountReferents(nsFrameMessageManager* aMessageManager, MessageManagerReferentCount* aReferentCount) { - aMessageManager->mListeners.EnumerateRead(CollectMessageListenerData, - aReferentCount); + for (auto it = aMessageManager->mListeners.Iter(); !it.Done(); it.Next()) { + nsAutoTObserverArray* listeners = + it.UserData(); + uint32_t listenerCount = listeners->Length(); + if (listenerCount == 0) { + continue; + } + + nsString key(it.Key()); + uint32_t oldCount = 0; + aReferentCount->mMessageCounter.Get(key, &oldCount); + uint32_t currentCount = oldCount + listenerCount; + aReferentCount->mMessageCounter.Put(key, currentCount); + + // Keep track of messages that have a suspiciously large + // number of referents (symptom of leak). + if (currentCount == MessageManagerReporter::kSuspectReferentCount) { + aReferentCount->mSuspectMessages.AppendElement(key); + } + + for (uint32_t i = 0; i < listenerCount; ++i) { + const nsMessageListenerInfo& listenerInfo = listeners->ElementAt(i); + if (listenerInfo.mWeakListener) { + nsCOMPtr referent = + do_QueryReferent(listenerInfo.mWeakListener); + if (referent) { + aReferentCount->mWeakAlive++; + } else { + aReferentCount->mWeakDead++; + } + } else { + aReferentCount->mStrong++; + } + } + } // Add referent count in child managers because the listeners // participate in messages dispatched from parent message manager. @@ -2186,24 +2145,20 @@ NS_NewChildProcessMessageManager(nsISyncMessageSender** aResult) return NS_OK; } -static PLDHashOperator -CycleCollectorMarkListeners(const nsAString& aKey, - nsAutoTObserverArray* aListeners, - void* aData) -{ - uint32_t count = aListeners->Length(); - for (uint32_t i = 0; i < count; i++) { - if (aListeners->ElementAt(i).mStrongListener) { - xpc_TryUnmarkWrappedGrayObject(aListeners->ElementAt(i).mStrongListener); - } - } - return PL_DHASH_NEXT; -} - bool nsFrameMessageManager::MarkForCC() { - mListeners.EnumerateRead(CycleCollectorMarkListeners, nullptr); + for (auto iter = mListeners.Iter(); !iter.Done(); iter.Next()) { + nsAutoTObserverArray* listeners = iter.UserData(); + uint32_t count = listeners->Length(); + for (uint32_t i = 0; i < count; i++) { + nsCOMPtr strongListener = + listeners->ElementAt(i).mStrongListener; + if (strongListener) { + xpc_TryUnmarkWrappedGrayObject(strongListener); + } + } + } if (mRefCnt.IsPurple()) { mRefCnt.RemovePurple(); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 248ac3956b..1ff4f25fa4 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -13403,18 +13403,6 @@ nsGlobalWindow::RemoveGamepad(uint32_t aIndex) mGamepads.Remove(aIndex); } -// static -PLDHashOperator -nsGlobalWindow::EnumGamepadsForGet(const uint32_t& aKey, Gamepad* aData, - void* aUserArg) -{ - nsTArray >* array = - static_cast >*>(aUserArg); - array->EnsureLengthAtLeast(aData->Index() + 1); - (*array)[aData->Index()] = aData; - return PL_DHASH_NEXT; -} - void nsGlobalWindow::GetGamepads(nsTArray >& aGamepads) { @@ -13422,7 +13410,11 @@ nsGlobalWindow::GetGamepads(nsTArray >& aGamepads) aGamepads.Clear(); // mGamepads.Count() may not be sufficient, but it's not harmful. aGamepads.SetCapacity(mGamepads.Count()); - mGamepads.EnumerateRead(EnumGamepadsForGet, &aGamepads); + for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) { + Gamepad* gamepad = iter.UserData(); + aGamepads.EnsureLengthAtLeast(gamepad->Index() + 1); + aGamepads[gamepad->Index()] = gamepad; + } } already_AddRefed @@ -13452,22 +13444,15 @@ nsGlobalWindow::HasSeenGamepadInput() return mHasSeenGamepadInput; } -// static -PLDHashOperator -nsGlobalWindow::EnumGamepadsForSync(const uint32_t& aKey, Gamepad* aData, - void* aUserArg) -{ - RefPtr gamepadsvc(GamepadService::GetService()); - gamepadsvc->SyncGamepadState(aKey, aData); - return PL_DHASH_NEXT; -} - void nsGlobalWindow::SyncGamepadState() { MOZ_ASSERT(IsInnerWindow()); if (mHasSeenGamepadInput) { - mGamepads.EnumerateRead(EnumGamepadsForSync, nullptr); + RefPtr gamepadsvc(GamepadService::GetService()); + for (auto iter = mGamepads.Iter(); !iter.Done(); iter.Next()) { + gamepadsvc->SyncGamepadState(iter.Key(), iter.UserData()); + } } } #endif // MOZ_GAMEPAD @@ -13510,9 +13495,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsGlobalChromeWindow, tmp->mMessageManager.get())->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager) } - - tmp->mGroupMessageManagers.EnumerateRead(DisconnectGroupMessageManager, nullptr); - tmp->mGroupMessageManagers.Clear(); + tmp->DisconnectAndClearGroupMessageManagers(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mGroupMessageManagers) NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index cadaa2e46d..98b82013d5 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -765,12 +765,6 @@ public: void SetHasSeenGamepadInput(bool aHasSeen); bool HasSeenGamepadInput(); void SyncGamepadState(); - static PLDHashOperator EnumGamepadsForSync(const uint32_t& aKey, - mozilla::dom::Gamepad* aData, - void* aUserArg); - static PLDHashOperator EnumGamepadsForGet(const uint32_t& aKey, - mozilla::dom::Gamepad* aData, - void* aUserArg); #endif // Inner windows only. @@ -1882,15 +1876,15 @@ public: static already_AddRefed Create(nsGlobalWindow *aOuterWindow); - static PLDHashOperator - DisconnectGroupMessageManager(const nsAString& aKey, - nsIMessageBroadcaster* aMM, - void* aUserArg) + void DisconnectAndClearGroupMessageManagers() { - if (aMM) { - static_cast(aMM)->Disconnect(); + for (auto iter = mGroupMessageManagers.Iter(); !iter.Done(); iter.Next()) { + nsIMessageBroadcaster* mm = iter.UserData(); + if (mm) { + static_cast(mm)->Disconnect(); + } } - return PL_DHASH_NEXT; + mGroupMessageManagers.Clear(); } protected: @@ -1907,8 +1901,7 @@ protected: MOZ_ASSERT(mCleanMessageManager, "chrome windows may always disconnect the msg manager"); - mGroupMessageManagers.EnumerateRead(DisconnectGroupMessageManager, nullptr); - mGroupMessageManagers.Clear(); + DisconnectAndClearGroupMessageManagers(); if (mMessageManager) { static_cast( diff --git a/dom/base/nsHostObjectProtocolHandler.cpp b/dom/base/nsHostObjectProtocolHandler.cpp index ea52e6ed28..d5e4843fb6 100644 --- a/dom/base/nsHostObjectProtocolHandler.cpp +++ b/dom/base/nsHostObjectProtocolHandler.cpp @@ -66,15 +66,125 @@ class BlobURLsReporter final : public nsIMemoryReporter NS_IMETHOD CollectReports(nsIHandleReportCallback* aCallback, nsISupports* aData, bool aAnonymize) override { - EnumArg env; - env.mCallback = aCallback; - env.mData = aData; - env.mAnonymize = aAnonymize; - - if (gDataTable) { - gDataTable->EnumerateRead(CountCallback, &env); - gDataTable->EnumerateRead(ReportCallback, &env); + if (!gDataTable) { + return NS_OK; } + + nsDataHashtable, uint32_t> refCounts; + + // Determine number of URLs per blob, to handle the case where it's > 1. + for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) { + nsCOMPtr blob = + do_QueryInterface(iter.UserData()->mObject); + if (blob) { + refCounts.Put(blob, refCounts.Get(blob) + 1); + } + } + + for (auto iter = gDataTable->Iter(); !iter.Done(); iter.Next()) { + nsCStringHashKey::KeyType key = iter.Key(); + DataInfo* info = iter.UserData(); + + nsCOMPtr tmp = do_QueryInterface(info->mObject); + RefPtr blob = + static_cast(tmp.get()); + + if (blob) { + NS_NAMED_LITERAL_CSTRING(desc, + "A blob URL allocated with URL.createObjectURL; the referenced " + "blob cannot be freed until all URLs for it have been explicitly " + "invalidated with URL.revokeObjectURL."); + nsAutoCString path, url, owner, specialDesc; + nsCOMPtr principalURI; + uint64_t size = 0; + uint32_t refCount = 1; + DebugOnly blobWasCounted; + + blobWasCounted = refCounts.Get(blob, &refCount); + MOZ_ASSERT(blobWasCounted); + MOZ_ASSERT(refCount > 0); + + bool isMemoryFile = blob->IsMemoryFile(); + + if (isMemoryFile) { + ErrorResult rv; + size = blob->GetSize(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + size = 0; + } + } + + path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/"; + BuildPath(path, key, info, aAnonymize); + + if (refCount > 1) { + nsAutoCString addrStr; + + addrStr = "0x"; + addrStr.AppendInt((uint64_t)(nsIDOMBlob*)blob, 16); + + path += " "; + path.AppendInt(refCount); + path += "@"; + path += addrStr; + + specialDesc = desc; + specialDesc += "\n\nNOTE: This blob (address "; + specialDesc += addrStr; + specialDesc += ") has "; + specialDesc.AppendInt(refCount); + specialDesc += " URLs."; + if (isMemoryFile) { + specialDesc += " Its size is divided "; + specialDesc += refCount > 2 ? "among" : "between"; + specialDesc += " them in this report."; + } + } + + const nsACString& descString = specialDesc.IsEmpty() + ? static_cast(desc) + : static_cast(specialDesc); + if (isMemoryFile) { + aCallback->Callback(EmptyCString(), + path, + KIND_OTHER, + UNITS_BYTES, + size / refCount, + descString, + aData); + } else { + aCallback->Callback(EmptyCString(), + path, + KIND_OTHER, + UNITS_COUNT, + 1, + descString, + aData); + } + } else { + // Just report the path for the DOMMediaStream or MediaSource. + nsCOMPtr + ms(do_QueryInterface(info->mObject)); + nsAutoCString path; + path = ms ? "media-source-urls/" : "dom-media-stream-urls/"; + BuildPath(path, key, info, aAnonymize); + + NS_NAMED_LITERAL_CSTRING(desc, + "An object URL allocated with URL.createObjectURL; the referenced " + "data cannot be freed until all URLs for it have been explicitly " + "invalidated with URL.revokeObjectURL."); + + aCallback->Callback(EmptyCString(), + path, + KIND_OTHER, + UNITS_COUNT, + 1, + desc, + aData); + } + } + return NS_OK; } @@ -143,138 +253,41 @@ class BlobURLsReporter final : public nsIMemoryReporter private: ~BlobURLsReporter() {} - struct EnumArg { - nsIHandleReportCallback* mCallback; - nsISupports* mData; - bool mAnonymize; - nsDataHashtable, uint32_t> mRefCounts; - }; - - // Determine number of URLs per blob, to handle the case where it's > 1. - static PLDHashOperator CountCallback(nsCStringHashKey::KeyType aKey, - DataInfo* aInfo, - void* aUserArg) + static void BuildPath(nsAutoCString& path, + nsCStringHashKey::KeyType aKey, + DataInfo* aInfo, + bool anonymize) { - EnumArg* envp = static_cast(aUserArg); - nsCOMPtr blob; - - blob = do_QueryInterface(aInfo->mObject); - if (blob) { - envp->mRefCounts.Put(blob, envp->mRefCounts.Get(blob) + 1); + nsCOMPtr principalURI; + nsAutoCString url, owner; + if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) && + principalURI != nullptr && + NS_SUCCEEDED(principalURI->GetSpec(owner)) && + !owner.IsEmpty()) { + owner.ReplaceChar('/', '\\'); + path += "owner("; + if (anonymize) { + path += ""; + } else { + path += owner; + } + path += ")"; + } else { + path += "owner unknown"; } - return PL_DHASH_NEXT; - } - - static PLDHashOperator ReportCallback(nsCStringHashKey::KeyType aKey, - DataInfo* aInfo, - void* aUserArg) - { - EnumArg* envp = static_cast(aUserArg); - nsCOMPtr tmp = do_QueryInterface(aInfo->mObject); - RefPtr blob = static_cast(tmp.get()); - - if (blob) { - NS_NAMED_LITERAL_CSTRING - (desc, "A blob URL allocated with URL.createObjectURL; the referenced " - "blob cannot be freed until all URLs for it have been explicitly " - "invalidated with URL.revokeObjectURL."); - nsAutoCString path, url, owner, specialDesc; - nsCOMPtr principalURI; - uint64_t size = 0; - uint32_t refCount = 1; - DebugOnly blobWasCounted; - - blobWasCounted = envp->mRefCounts.Get(blob, &refCount); - MOZ_ASSERT(blobWasCounted); - MOZ_ASSERT(refCount > 0); - - bool isMemoryFile = blob->IsMemoryFile(); - - if (isMemoryFile) { - ErrorResult rv; - size = blob->GetSize(rv); - if (NS_WARN_IF(rv.Failed())) { - rv.SuppressException(); - size = 0; - } - } - - path = isMemoryFile ? "memory-blob-urls/" : "file-blob-urls/"; - if (NS_SUCCEEDED(aInfo->mPrincipal->GetURI(getter_AddRefs(principalURI))) && - principalURI != nullptr && - NS_SUCCEEDED(principalURI->GetSpec(owner)) && - !owner.IsEmpty()) { - owner.ReplaceChar('/', '\\'); - path += "owner("; - if (envp->mAnonymize) { - path += ""; - } else { - path += owner; - } - path += ")"; - } else { - path += "owner unknown"; - } - path += "/"; - if (envp->mAnonymize) { - path += ""; - } else { - path += aInfo->mStack; - } - url = aKey; - url.ReplaceChar('/', '\\'); - if (envp->mAnonymize) { - path += ""; - } else { - path += url; - } - if (refCount > 1) { - nsAutoCString addrStr; - - addrStr = "0x"; - addrStr.AppendInt((uint64_t)(nsIDOMBlob*)blob, 16); - - path += " "; - path.AppendInt(refCount); - path += "@"; - path += addrStr; - - specialDesc = desc; - specialDesc += "\n\nNOTE: This blob (address "; - specialDesc += addrStr; - specialDesc += ") has "; - specialDesc.AppendInt(refCount); - specialDesc += " URLs."; - if (isMemoryFile) { - specialDesc += " Its size is divided "; - specialDesc += refCount > 2 ? "among" : "between"; - specialDesc += " them in this report."; - } - } - - const nsACString& descString = specialDesc.IsEmpty() - ? static_cast(desc) - : static_cast(specialDesc); - if (isMemoryFile) { - envp->mCallback->Callback(EmptyCString(), - path, - KIND_OTHER, - UNITS_BYTES, - size / refCount, - descString, - envp->mData); - } - else { - envp->mCallback->Callback(EmptyCString(), - path, - KIND_OTHER, - UNITS_COUNT, - 1, - descString, - envp->mData); - } + path += "/"; + if (anonymize) { + path += ""; + } else { + path += aInfo->mStack; + } + url = aKey; + url.ReplaceChar('/', '\\'); + if (anonymize) { + path += ""; + } else { + path += url; } - return PL_DHASH_NEXT; } }; diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index 598aee0067..fa70b1dedf 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -160,8 +160,8 @@ struct FullScreenOptions { } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0x72391609, 0x673d, 0x4bec, \ - { 0xbd, 0x75, 0x64, 0xbf, 0x1f, 0x6a, 0x6b, 0x5e } } +{ 0x5f51e18c, 0x9e0e, 0x4dc0, \ + { 0x9f, 0x08, 0x7a, 0x32, 0x65, 0x52, 0xea, 0x11 } } // Enum for requesting a particular type of document when creating a doc enum DocumentFlavor { @@ -957,7 +957,7 @@ public: eAgentSheet, eUserSheet, eAuthorSheet, - SheetTypeCount + AdditionalSheetTypeCount }; virtual nsresult LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) = 0; @@ -1105,6 +1105,11 @@ public: */ virtual Element* GetFullScreenElement() = 0; + /** + * Returns all elements in the fullscreen stack in the insertion order. + */ + virtual nsTArray GetFullscreenStack() const = 0; + /** * Asynchronously requests that the document make aElement the fullscreen * element, and move into fullscreen mode. The current fullscreen element diff --git a/dom/base/nsWindowMemoryReporter.cpp b/dom/base/nsWindowMemoryReporter.cpp index ffd0c5af18..c3ea89d716 100644 --- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -793,37 +793,6 @@ CheckForGhostWindowsEnumerator(nsISupports *aKey, TimeStamp& aTimeStamp, return PL_DHASH_NEXT; } -struct GetNonDetachedWindowDomainsEnumeratorData -{ - nsTHashtable *nonDetachedDomains; - nsIEffectiveTLDService *tldService; -}; - -static PLDHashOperator -GetNonDetachedWindowDomainsEnumerator(const uint64_t& aId, nsGlobalWindow* aWindow, - void* aClosure) -{ - GetNonDetachedWindowDomainsEnumeratorData *data = - static_cast(aClosure); - - // Null outer window implies null top, but calling GetTop() when there's no - // outer window causes us to spew debug warnings. - if (!aWindow->GetOuterWindow() || !aWindow->GetTopInternal()) { - // This window is detached, so we don't care about its domain. - return PL_DHASH_NEXT; - } - - nsCOMPtr uri = GetWindowURI(aWindow); - - nsAutoCString domain; - if (uri) { - data->tldService->GetBaseDomain(uri, 0, domain); - } - - data->nonDetachedDomains->PutEntry(domain); - return PL_DHASH_NEXT; -} - /** * Iterate over mDetachedWindows and update it to reflect the current state of * the world. In particular: @@ -865,10 +834,22 @@ nsWindowMemoryReporter::CheckForGhostWindows( nsTHashtable nonDetachedWindowDomains; // Populate nonDetachedWindowDomains. - GetNonDetachedWindowDomainsEnumeratorData nonDetachedEnumData = - { &nonDetachedWindowDomains, tldService }; - windowsById->EnumerateRead(GetNonDetachedWindowDomainsEnumerator, - &nonDetachedEnumData); + for (auto iter = windowsById->Iter(); !iter.Done(); iter.Next()) { + // Null outer window implies null top, but calling GetTop() when there's no + // outer window causes us to spew debug warnings. + nsGlobalWindow* window = iter.UserData(); + if (!window->GetOuterWindow() || !window->GetTopInternal()) { + // This window is detached, so we don't care about its domain. + continue; + } + + nsCOMPtr uri = GetWindowURI(window); + nsAutoCString domain; + if (uri) { + tldService->GetBaseDomain(uri, 0, domain); + } + nonDetachedWindowDomains.PutEntry(domain); + } // Update mDetachedWindows and write the ghost window IDs into aOutGhostIDs, // if it's not null. diff --git a/dom/bindings/Errors.msg b/dom/bindings/Errors.msg index c05d684bf5..c60d164623 100644 --- a/dom/bindings/Errors.msg +++ b/dom/bindings/Errors.msg @@ -82,3 +82,4 @@ MSG_DEF(MSG_NO_ACTIVE_WORKER, 1, JSEXN_TYPEERR, "No active worker for scope {0}. MSG_DEF(MSG_NOTIFICATION_PERMISSION_DENIED, 0, JSEXN_TYPEERR, "Permission to show Notification denied.") MSG_DEF(MSG_NOTIFICATION_NO_CONSTRUCTOR_IN_SERVICEWORKER, 0, JSEXN_TYPEERR, "Notification constructor cannot be used in ServiceWorkerGlobalScope. Use registration.showNotification() instead.") MSG_DEF(MSG_INVALID_SCOPE, 2, JSEXN_TYPEERR, "Invalid scope trying to resolve {0} with base URL {1}.") +MSG_DEF(MSG_INVALID_KEYFRAME_OFFSETS, 0, JSEXN_TYPEERR, "Keyframes with specified offsets must be in order and all be in the range [0, 1].") diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp index c443dbd0bf..80ac49e204 100644 --- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -158,6 +158,7 @@ CreateException(JSContext* aCx, nsresult aRv, const nsACString& aMessage) case NS_ERROR_MODULE_DOM_INDEXEDDB: case NS_ERROR_MODULE_DOM_FILEHANDLE: case NS_ERROR_MODULE_DOM_BLUETOOTH: + case NS_ERROR_MODULE_DOM_ANIM: if (aMessage.IsEmpty()) { return DOMException::Create(aRv); } diff --git a/dom/events/test/chrome.ini b/dom/events/test/chrome.ini index a42b9e612d..d92539399c 100644 --- a/dom/events/test/chrome.ini +++ b/dom/events/test/chrome.ini @@ -21,4 +21,7 @@ support-files = [test_bug617528.xul] [test_bug679494.xul] [test_bug930374-chrome.html] +[test_bug1128787-1.html] +[test_bug1128787-2.html] +[test_bug1128787-3.html] [test_eventctors.xul] diff --git a/dom/events/test/test_bug1128787-1.html b/dom/events/test/test_bug1128787-1.html new file mode 100644 index 0000000000..947bace386 --- /dev/null +++ b/dom/events/test/test_bug1128787-1.html @@ -0,0 +1,54 @@ + + + + + + Test for Bug 1128787 + + + + + + +Mozilla Bug 1128787 +

+ + + +
+
+ + diff --git a/dom/events/test/test_bug1128787-2.html b/dom/events/test/test_bug1128787-2.html new file mode 100644 index 0000000000..3e2a6cadaa --- /dev/null +++ b/dom/events/test/test_bug1128787-2.html @@ -0,0 +1,55 @@ + + + + + + Test for Bug 1128787 + + + + + + +Mozilla Bug 1128787 +

+ +

+ + +
+
+ + diff --git a/dom/events/test/test_bug1128787-3.html b/dom/events/test/test_bug1128787-3.html new file mode 100644 index 0000000000..a7b86cdb67 --- /dev/null +++ b/dom/events/test/test_bug1128787-3.html @@ -0,0 +1,54 @@ + + + + + + Test for Bug 1128787 + + + + + + +Mozilla Bug 1128787 +

+ + + +
+
+ + diff --git a/dom/html/nsHTMLDocument.cpp b/dom/html/nsHTMLDocument.cpp index e016742ed9..2347432ef3 100644 --- a/dom/html/nsHTMLDocument.cpp +++ b/dom/html/nsHTMLDocument.cpp @@ -110,6 +110,10 @@ #include "nsCharsetSource.h" #include "nsIStringBundle.h" #include "nsDOMClassInfo.h" +#include "nsFocusManager.h" +#include "nsIFrame.h" +#include "nsIContent.h" +#include "nsLayoutStylesheetCache.h" using namespace mozilla; using namespace mozilla::dom; @@ -143,26 +147,6 @@ static bool ConvertToMidasInternalCommand(const nsAString & inCommandID, // ================================================================== // = // ================================================================== -static nsresult -RemoveFromAgentSheets(nsCOMArray &aAgentSheets, const nsAString& url) -{ - nsCOMPtr uri; - nsresult rv = NS_NewURI(getter_AddRefs(uri), url); - NS_ENSURE_SUCCESS(rv, rv); - - for (int32_t i = aAgentSheets.Count() - 1; i >= 0; --i) { - nsIStyleSheet* sheet = aAgentSheets[i]; - nsIURI* sheetURI = sheet->GetSheetURI(); - - bool equals = false; - uri->Equals(sheetURI, &equals); - if (equals) { - aAgentSheets.RemoveObjectAt(i); - } - } - - return NS_OK; -} nsresult NS_NewHTMLDocument(nsIDocument** aInstancePtrResult, bool aLoadedAsData) @@ -1424,6 +1408,9 @@ nsHTMLDocument::Open(JSContext* cx, const nsAString& aReplace, ErrorResult& rv) { + // Implements the "When called with two arguments (or fewer)" steps here: + // https://html.spec.whatwg.org/multipage/webappapis.html#opening-the-input-stream + NS_ASSERTION(nsContentUtils::CanCallerAccess(static_cast(this)), "XOW should have caught this!"); if (!IsHTMLDocument() || mDisableDocWrite || !IsMasterDocument()) { @@ -1564,7 +1551,7 @@ nsHTMLDocument::Open(JSContext* cx, } // The open occurred after the document finished loading. - // So we reset the document and create a new one. + // So we reset the document and then reinitialize it. nsCOMPtr channel; nsCOMPtr group = do_QueryReferent(mDocumentLoadGroup); rv = NS_NewChannel(getter_AddRefs(channel), @@ -1640,8 +1627,9 @@ nsHTMLDocument::Open(JSContext* cx, } #endif - // Should this pass true for aForceReuseInnerWindow? - rv = window->SetNewDocument(this, nullptr, false); + // Per spec, we pass false here so that a new Window is created. + rv = window->SetNewDocument(this, nullptr, + /* aForceReuseInnerWindow */ false); if (rv.Failed()) { return nullptr; } @@ -2657,9 +2645,9 @@ nsHTMLDocument::TearingDownEditor(nsIEditor *aEditor) nsCOMArray agentSheets; presShell->GetAgentStyleSheets(agentSheets); - RemoveFromAgentSheets(agentSheets, NS_LITERAL_STRING("resource://gre/res/contenteditable.css")); + agentSheets.RemoveObject(nsLayoutStylesheetCache::ContentEditableSheet()); if (oldState == eDesignMode) - RemoveFromAgentSheets(agentSheets, NS_LITERAL_STRING("resource://gre/res/designmode.css")); + agentSheets.RemoveObject(nsLayoutStylesheetCache::DesignModeSheet()); presShell->SetAgentStyleSheets(agentSheets); @@ -2716,7 +2704,7 @@ nsHTMLDocument::EditingStateChanged() } if (mEditingState == eSettingUp || mEditingState == eTearingDown) { - // XXX We shouldn't recurse. + // XXX We shouldn't recurse return NS_OK; } @@ -2780,12 +2768,84 @@ nsHTMLDocument::EditingStateChanged() bool makeWindowEditable = mEditingState == eOff; bool updateState = false; bool spellRecheckAll = false; + bool putOffToRemoveScriptBlockerUntilModifyingEditingState = false; nsCOMPtr editor; { EditingState oldState = mEditingState; nsAutoEditingState push(this, eSettingUp); + nsCOMPtr presShell = GetShell(); + NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); + + // Before making this window editable, we need to modify UA style sheet + // because new style may change whether focused element will be focusable + // or not. + nsCOMArray agentSheets; + rv = presShell->GetAgentStyleSheets(agentSheets); + NS_ENSURE_SUCCESS(rv, rv); + + CSSStyleSheet* contentEditableSheet = + nsLayoutStylesheetCache::ContentEditableSheet(); + + bool result; + + if (!agentSheets.Contains(contentEditableSheet)) { + bool result = agentSheets.AppendObject(contentEditableSheet); + NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); + } + + // Should we update the editable state of all the nodes in the document? We + // need to do this when the designMode value changes, as that overrides + // specific states on the elements. + if (designMode) { + // designMode is being turned on (overrides contentEditable). + CSSStyleSheet* designModeSheet = + nsLayoutStylesheetCache::DesignModeSheet(); + if (!agentSheets.Contains(designModeSheet)) { + result = agentSheets.AppendObject(designModeSheet); + NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); + } + + updateState = true; + spellRecheckAll = oldState == eContentEditable; + } + else if (oldState == eDesignMode) { + // designMode is being turned off (contentEditable is still on). + agentSheets.RemoveObject(nsLayoutStylesheetCache::DesignModeSheet()); + updateState = true; + } + + rv = presShell->SetAgentStyleSheets(agentSheets); + NS_ENSURE_SUCCESS(rv, rv); + + presShell->ReconstructStyleData(); + + // Adjust focused element with new style but blur event shouldn't be fired + // until mEditingState is modified with newState. + nsAutoScriptBlocker scriptBlocker; + if (designMode) { + nsCOMPtr focusedWindow; + nsIContent* focusedContent = + nsFocusManager::GetFocusedDescendant(window, false, + getter_AddRefs(focusedWindow)); + if (focusedContent) { + nsIFrame* focusedFrame = focusedContent->GetPrimaryFrame(); + bool clearFocus = focusedFrame ? !focusedFrame->IsFocusable() : + !focusedContent->IsFocusable(); + if (clearFocus) { + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + if (fm) { + fm->ClearFocus(window); + // If we need to dispatch blur event, we should put off after + // modifying mEditingState since blur event handler may change + // designMode state again. + putOffToRemoveScriptBlockerUntilModifyingEditingState = true; + } + } + } + } + if (makeWindowEditable) { // Editing is being turned on (through designMode or contentEditable) // Turn on editor. @@ -2801,61 +2861,26 @@ nsHTMLDocument::EditingStateChanged() if (!editor) return NS_ERROR_FAILURE; - nsCOMPtr presShell = GetShell(); - NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE); - // If we're entering the design mode, put the selection at the beginning of // the document for compatibility reasons. if (designMode && oldState == eOff) { editor->BeginningOfDocument(); } - nsCOMArray agentSheets; - rv = presShell->GetAgentStyleSheets(agentSheets); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("resource://gre/res/contenteditable.css")); - NS_ENSURE_SUCCESS(rv, rv); - - RefPtr sheet; - rv = LoadChromeSheetSync(uri, true, getter_AddRefs(sheet)); - NS_ENSURE_TRUE(sheet, rv); - - bool result = agentSheets.AppendObject(sheet); - NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); - - // Should we update the editable state of all the nodes in the document? We - // need to do this when the designMode value changes, as that overrides - // specific states on the elements. - if (designMode) { - // designMode is being turned on (overrides contentEditable). - rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("resource://gre/res/designmode.css")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = LoadChromeSheetSync(uri, true, getter_AddRefs(sheet)); - NS_ENSURE_TRUE(sheet, rv); - - result = agentSheets.AppendObject(sheet); - NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); - - updateState = true; - spellRecheckAll = oldState == eContentEditable; + if (putOffToRemoveScriptBlockerUntilModifyingEditingState) { + nsContentUtils::AddScriptBlocker(); } - else if (oldState == eDesignMode) { - // designMode is being turned off (contentEditable is still on). - RemoveFromAgentSheets(agentSheets, NS_LITERAL_STRING("resource://gre/res/designmode.css")); - - updateState = true; - } - - rv = presShell->SetAgentStyleSheets(agentSheets); - NS_ENSURE_SUCCESS(rv, rv); - - presShell->ReconstructStyleData(); } mEditingState = newState; + if (putOffToRemoveScriptBlockerUntilModifyingEditingState) { + nsContentUtils::RemoveScriptBlocker(); + // If mEditingState is overwritten by another call and already disabled + // the editing, we shouldn't keep making window editable. + if (mEditingState == eOff) { + return NS_OK; + } + } if (makeWindowEditable) { // Set the editor to not insert br's on return when in p @@ -3519,6 +3544,24 @@ nsHTMLDocument::QueryCommandSupported(const nsAString & commandID, bool nsHTMLDocument::QueryCommandSupported(const nsAString& commandID) { + // Gecko technically supports all the clipboard commands including + // cut/copy/paste, but non-privileged content will be unable to call + // paste, and depending on the pref "dom.allow_cut_copy", cut and copy + // may also be disallowed to be called from non-privileged content. + // For that reason, we report the support status of corresponding + // command accordingly. + if (!nsContentUtils::IsCallerChrome()) { + if (commandID.LowerCaseEqualsLiteral("paste")) { + return false; + } + if (nsContentUtils::IsCutCopyRestricted()) { + if (commandID.LowerCaseEqualsLiteral("cut") || + commandID.LowerCaseEqualsLiteral("copy")) { + return false; + } + } + } + // commandID is supported if it can be converted to a Midas command nsAutoCString cmdToDispatch; return ConvertToMidasInternalCommand(commandID, cmdToDispatch); diff --git a/dom/html/test/file_fullscreen-ancestor-stacking-context.html b/dom/html/test/file_fullscreen-ancestor-stacking-context.html deleted file mode 100644 index d0b7658c4e..0000000000 --- a/dom/html/test/file_fullscreen-ancestor-stacking-context.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - - Test for Bug 1056203 - - - -Mozilla Bug 1056203 -

-

-
-
-
-
-
-
-

-
-
-
- - - diff --git a/dom/html/test/file_fullscreen-top-layer.html b/dom/html/test/file_fullscreen-top-layer.html new file mode 100644 index 0000000000..9d3e9ef594 --- /dev/null +++ b/dom/html/test/file_fullscreen-top-layer.html @@ -0,0 +1,176 @@ + + + + + Test for Bug 1126230 + + + + + + + +Mozilla Bug 1126230 +
+
+
+
+ + + + + + + + + diff --git a/dom/html/test/mochitest.ini b/dom/html/test/mochitest.ini index 80c2ee397b..2abf6f6f17 100644 --- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -46,7 +46,6 @@ support-files = file_bug893537.html file_formSubmission_img.jpg file_formSubmission_text.txt - file_fullscreen-ancestor-stacking-context.html file_fullscreen-api-keys.html file_fullscreen-api.html file_fullscreen-denied-inner.html @@ -63,6 +62,7 @@ support-files = file_fullscreen-scrollbar.html file_fullscreen-selector.html file_fullscreen-svg-element.html + file_fullscreen-top-layer.html file_fullscreen-utils.js file_iframe_sandbox_a_if1.html file_iframe_sandbox_a_if10.html diff --git a/dom/html/test/test_fullscreen-api.html b/dom/html/test/test_fullscreen-api.html index be257e6240..a94f737cd4 100644 --- a/dom/html/test/test_fullscreen-api.html +++ b/dom/html/test/test_fullscreen-api.html @@ -28,7 +28,6 @@ SimpleTest.requestFlakyTimeout("untriaged"); // run in an iframe, which by default will not have the allowfullscreen // attribute set, so full-screen won't work. var gTestWindows = [ - "file_fullscreen-ancestor-stacking-context.html", "file_fullscreen-multiple.html", "file_fullscreen-rollback.html", "file_fullscreen-esc-context-menu.html", @@ -41,7 +40,8 @@ var gTestWindows = [ "file_fullscreen-svg-element.html", "file_fullscreen-navigation.html", "file_fullscreen-scrollbar.html", - "file_fullscreen-selector.html" + "file_fullscreen-selector.html", + "file_fullscreen-top-layer.html" ]; var testWindow = null; diff --git a/dom/tests/mochitest/general/mochitest.ini b/dom/tests/mochitest/general/mochitest.ini index 29b7618bd9..df48f92c12 100644 --- a/dom/tests/mochitest/general/mochitest.ini +++ b/dom/tests/mochitest/general/mochitest.ini @@ -110,6 +110,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec skip-if = buildapp == 'b2g' || buildapp == 'mulet' [test_bug1012662_editor.html] [test_bug1012662_noeditor.html] +[test_bug1170911.html] [test_storagePermissionsAccept.html] skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g [test_storagePermissionsRejectForeign.html] diff --git a/dom/tests/mochitest/general/test_bug1170911.html b/dom/tests/mochitest/general/test_bug1170911.html new file mode 100644 index 0000000000..e739a0f32b --- /dev/null +++ b/dom/tests/mochitest/general/test_bug1170911.html @@ -0,0 +1,90 @@ + + + + + Test for Bug 1170911 + + + + + + +Mozilla Bug 1170911 +

+ +
+ +
+ +
+
+
+ + diff --git a/dom/webidl/KeyframeEffect.webidl b/dom/webidl/KeyframeEffect.webidl index d7c2ef75df..c9c76c5364 100644 --- a/dom/webidl/KeyframeEffect.webidl +++ b/dom/webidl/KeyframeEffect.webidl @@ -10,8 +10,29 @@ * liability, trademark and document use rules apply. */ +// For the constructor: +// +// 1. We use Element? for the first argument since we don't support Animatable +// for pseudo-elements yet. +// +// 2. We use object? instead of +// +// (PropertyIndexedKeyframes or sequence or SharedKeyframeList) +// +// for the second argument so that we can get the property-value pairs from +// the PropertyIndexedKeyframes or Keyframe objects. We also don't support +// SharedKeyframeList yet. +// +// 3. We use unrestricted double instead of +// +// (unrestricted double or KeyframeEffectOptions) +// +// since we don't support KeyframeEffectOptions yet. [HeaderFile="mozilla/dom/KeyframeEffect.h", - Func="nsDocument::IsWebAnimationsEnabled"] + Func="nsDocument::IsWebAnimationsEnabled", + Constructor(Element? target, + optional object? frames, + optional unrestricted double options)] interface KeyframeEffectReadOnly : AnimationEffectReadOnly { readonly attribute Element? target; // Not yet implemented: diff --git a/dom/webidl/PropertyIndexedKeyframes.webidl b/dom/webidl/PropertyIndexedKeyframes.webidl new file mode 100644 index 0000000000..9f4f8e514c --- /dev/null +++ b/dom/webidl/PropertyIndexedKeyframes.webidl @@ -0,0 +1,18 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + * + * The origin of this IDL file is + * https://w3c.github.io/web-animations/#the-propertyindexedkeyframe-dictionary + * + * Copyright © 2015 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C + * liability, trademark and document use rules apply. + */ + +// No other .webidl files references this dictionary, but we use it for +// manual JS->IDL conversions in KeyframeEffectReadOnly's implementation. +dictionary PropertyIndexedKeyframes { + DOMString easing = "linear"; + CompositeOperation? composite = null; +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 136a312de3..66854e0e9d 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -384,6 +384,7 @@ WEBIDL_FILES = [ 'ProfileTimelineMarker.webidl', 'Promise.webidl', 'PromiseDebugging.webidl', + 'PropertyIndexedKeyframes.webidl', 'RadioNodeList.webidl', 'Range.webidl', 'Rect.webidl', diff --git a/dom/workers/ServiceWorkerManager.cpp b/dom/workers/ServiceWorkerManager.cpp index f22bc24746..e0b0082512 100644 --- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -3067,7 +3067,6 @@ bool ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv) { MOZ_ASSERT(aDoc); - MOZ_ASSERT(!nsContentUtils::IsInPrivateBrowsing(aDoc)); RefPtr registration; nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration)); @@ -3077,6 +3076,7 @@ ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv) return false; } + MOZ_ASSERT_IF(!!registration, !nsContentUtils::IsInPrivateBrowsing(aDoc)); return !!registration; } diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png new file mode 100644 index 0000000000..ae6a8a6b88 Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png new file mode 100644 index 0000000000..fe391dc8a2 Binary files /dev/null and b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png differ diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js b/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js new file mode 100644 index 0000000000..598d8213f3 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js @@ -0,0 +1,15 @@ +function synthesizeImage() { + return clients.matchAll().then(clients => { + var url = "image-40px.png"; + clients.forEach(client => { + client.postMessage(url); + }); + return fetch(url); + }); +} + +self.addEventListener("fetch", function(event) { + if (event.request.url.indexOf("image-20px.png") >= 0) { + event.respondWith(synthesizeImage()); + } +}); diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/index.html b/dom/workers/test/serviceworkers/fetch/imagecache/index.html new file mode 100644 index 0000000000..93b30f1842 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/index.html @@ -0,0 +1,20 @@ + + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html b/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html new file mode 100644 index 0000000000..72a650d26c --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html @@ -0,0 +1,9 @@ + + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/register.html b/dom/workers/test/serviceworkers/fetch/imagecache/register.html new file mode 100644 index 0000000000..f6d1eb382f --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/register.html @@ -0,0 +1,16 @@ + + + + diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html b/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html new file mode 100644 index 0000000000..1f13508fa7 --- /dev/null +++ b/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html @@ -0,0 +1,12 @@ + + diff --git a/dom/workers/test/serviceworkers/mochitest.ini b/dom/workers/test/serviceworkers/mochitest.ini index baa737cd1b..0bc58268ad 100644 --- a/dom/workers/test/serviceworkers/mochitest.ini +++ b/dom/workers/test/serviceworkers/mochitest.ini @@ -59,6 +59,13 @@ support-files = fetch/https/clonedresponse/register.html fetch/https/clonedresponse/unregister.html fetch/https/clonedresponse/https_test.js + fetch/imagecache/image-20px.png + fetch/imagecache/image-40px.png + fetch/imagecache/imagecache_test.js + fetch/imagecache/index.html + fetch/imagecache/postmortem.html + fetch/imagecache/register.html + fetch/imagecache/unregister.html fetch/interrupt.sjs fetch/origin/index.sjs fetch/origin/index-to-https.sjs @@ -265,3 +272,4 @@ skip-if = toolkit == "android" || toolkit == "gonk" [test_file_blob_upload.html] [test_hsts_upgrade_intercept.html] skip-if = e10s # Bug 1214305 +[test_imagecache.html] diff --git a/dom/workers/test/serviceworkers/test_imagecache.html b/dom/workers/test/serviceworkers/test_imagecache.html new file mode 100644 index 0000000000..319b4edcfd --- /dev/null +++ b/dom/workers/test/serviceworkers/test_imagecache.html @@ -0,0 +1,56 @@ + + + + + Bug 1202085 - Test that images from different controllers don't cached together + + + + +

+
+ +
+

+
+
+
+
diff --git a/dom/xbl/nsXBLPrototypeResources.cpp b/dom/xbl/nsXBLPrototypeResources.cpp
index f3146b2166..cd1cb192d1 100644
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -143,7 +143,7 @@ void
 nsXBLPrototypeResources::GatherRuleProcessor()
 {
   mRuleProcessor = new nsCSSRuleProcessor(mStyleSheetList,
-                                          nsStyleSet::eDocSheet,
+                                          SheetType::Doc,
                                           nullptr,
                                           mRuleProcessor);
 }
diff --git a/editor/libeditor/crashtests/1128787.html b/editor/libeditor/crashtests/1128787.html
new file mode 100644
index 0000000000..fc6bff0972
--- /dev/null
+++ b/editor/libeditor/crashtests/1128787.html
@@ -0,0 +1,17 @@
+
+
+
+Bug 1128787
+
+
+  
+  
+
+
diff --git a/editor/libeditor/crashtests/crashtests.list b/editor/libeditor/crashtests/crashtests.list
index 03b93903e5..03bf9d7ca9 100644
--- a/editor/libeditor/crashtests/crashtests.list
+++ b/editor/libeditor/crashtests/crashtests.list
@@ -59,4 +59,5 @@ load 772282.html
 load 776323.html
 needs-focus load 793866.html
 load 1057677.html
+needs-focus load 1128787.html
 load 1134545.html
diff --git a/image/ImageCacheKey.cpp b/image/ImageCacheKey.cpp
index f47b3fafe5..a19a11c900 100644
--- a/image/ImageCacheKey.cpp
+++ b/image/ImageCacheKey.cpp
@@ -10,6 +10,10 @@
 #include "ImageURL.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsString.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "nsIDOMDocument.h"
+#include "nsIDocument.h"
+#include "nsPrintfCString.h"
 
 namespace mozilla {
 
@@ -42,8 +46,9 @@ BlobSerial(ImageURL* aURI)
   return Nothing();
 }
 
-ImageCacheKey::ImageCacheKey(nsIURI* aURI)
+ImageCacheKey::ImageCacheKey(nsIURI* aURI, nsIDOMDocument* aDocument)
   : mURI(new ImageURL(aURI))
+  , mControlledDocument(GetControlledDocumentToken(aDocument))
   , mIsChrome(URISchemeIs(mURI, "chrome"))
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -52,11 +57,12 @@ ImageCacheKey::ImageCacheKey(nsIURI* aURI)
     mBlobSerial = BlobSerial(mURI);
   }
 
-  mHash = ComputeHash(mURI, mBlobSerial);
+  mHash = ComputeHash(mURI, mBlobSerial, mControlledDocument);
 }
 
-ImageCacheKey::ImageCacheKey(ImageURL* aURI)
+ImageCacheKey::ImageCacheKey(ImageURL* aURI, nsIDOMDocument* aDocument)
   : mURI(aURI)
+  , mControlledDocument(GetControlledDocumentToken(aDocument))
   , mIsChrome(URISchemeIs(mURI, "chrome"))
 {
   MOZ_ASSERT(aURI);
@@ -65,12 +71,13 @@ ImageCacheKey::ImageCacheKey(ImageURL* aURI)
     mBlobSerial = BlobSerial(mURI);
   }
 
-  mHash = ComputeHash(mURI, mBlobSerial);
+  mHash = ComputeHash(mURI, mBlobSerial, mControlledDocument);
 }
 
 ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
   : mURI(aOther.mURI)
   , mBlobSerial(aOther.mBlobSerial)
+  , mControlledDocument(aOther.mControlledDocument)
   , mHash(aOther.mHash)
   , mIsChrome(aOther.mIsChrome)
 { }
@@ -78,6 +85,7 @@ ImageCacheKey::ImageCacheKey(const ImageCacheKey& aOther)
 ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
   : mURI(Move(aOther.mURI))
   , mBlobSerial(Move(aOther.mBlobSerial))
+  , mControlledDocument(aOther.mControlledDocument)
   , mHash(aOther.mHash)
   , mIsChrome(aOther.mIsChrome)
 { }
@@ -85,6 +93,10 @@ ImageCacheKey::ImageCacheKey(ImageCacheKey&& aOther)
 bool
 ImageCacheKey::operator==(const ImageCacheKey& aOther) const
 {
+  // Don't share the image cache between a controlled document and anything else.
+  if (mControlledDocument != aOther.mControlledDocument) {
+    return false;
+  }
   if (mBlobSerial || aOther.mBlobSerial) {
     // If at least one of us has a blob serial, just compare the blob serial and
     // the ref portion of the URIs.
@@ -104,11 +116,13 @@ ImageCacheKey::Spec() const
 
 /* static */ uint32_t
 ImageCacheKey::ComputeHash(ImageURL* aURI,
-                           const Maybe& aBlobSerial)
+                           const Maybe& aBlobSerial,
+                           void* aControlledDocument)
 {
   // Since we frequently call Hash() several times in a row on the same
   // ImageCacheKey, as an optimization we compute our hash once and store it.
 
+  nsPrintfCString ptr("%p", aControlledDocument);
   if (aBlobSerial) {
     // For blob URIs, we hash the serial number of the underlying blob, so that
     // different blob URIs which point to the same blob share a cache entry. We
@@ -117,13 +131,32 @@ ImageCacheKey::ComputeHash(ImageURL* aURI,
     // if the source data is the same.
     nsAutoCString ref;
     aURI->GetRef(ref);
-    return HashGeneric(*aBlobSerial, HashString(ref));
+    return HashGeneric(*aBlobSerial, HashString(ref + ptr));
   }
 
   // For non-blob URIs, we hash the URI spec.
   nsAutoCString spec;
   aURI->GetSpec(spec);
-  return HashString(spec);
+  return HashString(spec + ptr);
+}
+
+/* static */ void*
+ImageCacheKey::GetControlledDocumentToken(nsIDOMDocument* aDocument)
+{
+  // For non-controlled documents, we just return null.  For controlled
+  // documents, we cast the pointer into a void* to avoid dereferencing
+  // it (since we only use it for comparisons), and return it.
+  void* pointer = nullptr;
+  using dom::workers::ServiceWorkerManager;
+  RefPtr swm = ServiceWorkerManager::GetInstance();
+  nsCOMPtr doc = do_QueryInterface(aDocument);
+  if (doc && swm) {
+    ErrorResult rv;
+    if (swm->IsControlled(doc, rv)) {
+      pointer = doc;
+    }
+  }
+  return pointer;
 }
 
 } // namespace image
diff --git a/image/ImageCacheKey.h b/image/ImageCacheKey.h
index e2dc36c99c..919bff6d91 100644
--- a/image/ImageCacheKey.h
+++ b/image/ImageCacheKey.h
@@ -12,6 +12,7 @@
 
 #include "mozilla/Maybe.h"
 
+class nsIDOMDocument;
 class nsIURI;
 
 namespace mozilla {
@@ -24,12 +25,14 @@ class ImageURL;
  *
  * We key the cache on the initial URI (before any redirects), with some
  * canonicalization applied. See ComputeHash() for the details.
+ * Controlled documents do not share their cache entries with
+ * non-controlled documents, or other controlled documents.
  */
 class ImageCacheKey final
 {
 public:
-  explicit ImageCacheKey(nsIURI* aURI);
-  explicit ImageCacheKey(ImageURL* aURI);
+  ImageCacheKey(nsIURI* aURI, nsIDOMDocument* aDocument);
+  ImageCacheKey(ImageURL* aURI, nsIDOMDocument* aDocument);
 
   ImageCacheKey(const ImageCacheKey& aOther);
   ImageCacheKey(ImageCacheKey&& aOther);
@@ -43,12 +46,19 @@ public:
   /// Is this cache entry for a chrome image?
   bool IsChrome() const { return mIsChrome; }
 
+  /// A token indicating which service worker controlled document this entry
+  /// belongs to, if any.
+  void* ControlledDocument() const { return mControlledDocument; }
+
 private:
   static uint32_t ComputeHash(ImageURL* aURI,
-                              const Maybe& aBlobSerial);
+                              const Maybe& aBlobSerial,
+                              void* aControlledDocument);
+  static void* GetControlledDocumentToken(nsIDOMDocument* aDocument);
 
   RefPtr mURI;
   Maybe mBlobSerial;
+  void* mControlledDocument;
   uint32_t mHash;
   bool mIsChrome;
 };
diff --git a/image/imgICache.idl b/image/imgICache.idl
index 1efa2d456f..abfe0d0909 100644
--- a/image/imgICache.idl
+++ b/image/imgICache.idl
@@ -6,9 +6,11 @@
 
 #include "nsISupports.idl"
 
-interface nsIURI;
 interface imgIRequest;
+interface nsIDocument;
+interface nsIDOMDocument;
 interface nsIProperties;
+interface nsIURI;
 
 /**
  * imgICache interface
@@ -17,7 +19,7 @@ interface nsIProperties;
  * @version 0.1
  * @see imagelib2
  */
-[scriptable, builtinclass, uuid(b06e0fa5-d6e2-4fa3-8fc0-7775aed96522)]
+[scriptable, builtinclass, uuid(bfdf23ff-378e-402e-8a6c-840f0c82b6c3)]
 interface imgICache : nsISupports
 {
   /**
@@ -28,15 +30,6 @@ interface imgICache : nsISupports
    */
   void clearCache(in boolean chrome);
 
-  /**
-   * Evict images from the cache.
-   *
-   * @param uri The URI to remove.
-   * @throws NS_ERROR_NOT_AVAILABLE if \a uri was unable to be removed from
-   * the cache.
-   */
-  void removeEntry(in nsIURI uri);
-
   /**
    * Find Properties
    * Used to get properties such as 'type' and 'content-disposition'
@@ -49,9 +42,10 @@ interface imgICache : nsISupports
    * Hopefully this will be removed with bug 805119
    *
    * @param uri The URI to look up.
+   * @param doc Optional pointer to the document that the cache entry belongs to.
    * @returns NULL if the URL was not found in the cache
    */
-  nsIProperties findEntryProperties(in nsIURI uri);
+  nsIProperties findEntryProperties(in nsIURI uri, [optional] in nsIDOMDocument doc);
 
   /**
    * Make this cache instance respect private browsing notifications. This
@@ -59,4 +53,11 @@ interface imgICache : nsISupports
    * last-pb-context-exited notification is observed.
    */
   void respectPrivacyNotifications();
+
+  /**
+   * Clear the image cache for a document.  Controlled documents are responsible
+   * for doing this manually when they get destroyed.
+   */
+  [noscript, notxpcom]
+  void clearCacheForControlledDocument(in nsIDocument doc);
 };
diff --git a/image/imgLoader.cpp b/image/imgLoader.cpp
index 439ec9687b..4358be7a89 100644
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -52,6 +52,7 @@
 #include "nsIHttpChannelInternal.h"
 #include "nsILoadContext.h"
 #include "nsILoadGroupChild.h"
+#include "nsIDOMDocument.h"
 
 using namespace mozilla;
 using namespace mozilla::image;
@@ -1403,21 +1404,13 @@ imgLoader::ClearCache(bool chrome)
 }
 
 NS_IMETHODIMP
-imgLoader::RemoveEntry(nsIURI* aURI)
-{
-  if (aURI && RemoveFromCache(ImageCacheKey(aURI))) {
-    return NS_OK;
-  }
-
-  return NS_ERROR_NOT_AVAILABLE;
-}
-
-NS_IMETHODIMP
-imgLoader::FindEntryProperties(nsIURI* uri, nsIProperties** _retval)
+imgLoader::FindEntryProperties(nsIURI* uri,
+                               nsIDOMDocument* doc,
+                               nsIProperties** _retval)
 {
   *_retval = nullptr;
 
-  ImageCacheKey key(uri);
+  ImageCacheKey key(uri, doc);
   imgCacheTable& cache = GetCache(key);
 
   RefPtr entry;
@@ -1436,6 +1429,25 @@ imgLoader::FindEntryProperties(nsIURI* uri, nsIProperties** _retval)
   return NS_OK;
 }
 
+NS_IMETHODIMP_(void)
+imgLoader::ClearCacheForControlledDocument(nsIDocument* aDoc)
+{
+  MOZ_ASSERT(aDoc);
+  nsAutoTArray, 128> entriesToBeRemoved;
+  imgCacheTable& cache = GetCache(false);
+  for (auto iter = cache.Iter(); !iter.Done(); iter.Next()) {
+    auto& key = iter.Key();
+    if (key.ControlledDocument() == aDoc) {
+      entriesToBeRemoved.AppendElement(iter.Data());
+    }
+  }
+  for (auto& entry : entriesToBeRemoved) {
+    if (!RemoveFromCache(entry)) {
+      NS_WARNING("Couldn't remove an entry from the cache in ClearCacheForControlledDocument()\n");
+    }
+  }
+}
+
 void
 imgLoader::Shutdown()
 {
@@ -2187,7 +2199,8 @@ imgLoader::LoadImage(nsIURI* aURI,
   // XXX For now ignore aCacheKey. We will need it in the future
   // for correctly dealing with image load requests that are a result
   // of post data.
-  ImageCacheKey key(aURI);
+  nsCOMPtr doc = do_QueryInterface(aCX);
+  ImageCacheKey key(aURI, doc);
   imgCacheTable& cache = GetCache(key);
 
   if (cache.Get(key, getter_AddRefs(entry)) && entry) {
@@ -2416,7 +2429,8 @@ imgLoader::LoadImageWithChannel(nsIChannel* channel,
 
   nsCOMPtr uri;
   channel->GetURI(getter_AddRefs(uri));
-  ImageCacheKey key(uri);
+  nsCOMPtr doc = do_QueryInterface(aCX);
+  ImageCacheKey key(uri, doc);
 
   nsLoadFlags requestFlags = nsIRequest::LOAD_NORMAL;
   channel->GetLoadFlags(&requestFlags);
@@ -2510,7 +2524,7 @@ imgLoader::LoadImageWithChannel(nsIChannel* channel,
     // constructed above with the *current URI* and not the *original URI*. I'm
     // pretty sure this is a bug, and it's preventing us from ever getting a
     // cache hit in LoadImageWithChannel when redirects are involved.
-    ImageCacheKey originalURIKey(originalURI);
+    ImageCacheKey originalURIKey(originalURI, doc);
 
     // Default to doing a principal check because we don't know who
     // started that load and whether their principal ended up being
diff --git a/image/imgRequest.cpp b/image/imgRequest.cpp
index 916d8d30f8..52c8d58611 100644
--- a/image/imgRequest.cpp
+++ b/image/imgRequest.cpp
@@ -500,7 +500,7 @@ imgRequest::RemoveFromCache()
     if (mCacheEntry) {
       mLoader->RemoveFromCache(mCacheEntry);
     } else {
-      mLoader->RemoveFromCache(ImageCacheKey(mURI));
+      mLoader->RemoveFromCache(mCacheKey);
     }
   }
 
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp
index 74c4844760..e88e89f18f 100644
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -4051,7 +4051,7 @@ ElementRestyler::RestyleSelf(nsIFrame* aSelf,
       for (nsStyleStructID sid = nsStyleStructID(0);
            sid < nsStyleStructID_Length;
            sid = nsStyleStructID(sid + 1)) {
-        if (oldContext->HasCachedInheritedStyleData(sid) &&
+        if (oldContext->HasCachedDependentStyleData(sid) &&
             !(samePointerStructs & nsCachedStyleData::GetBitForSID(sid))) {
           LOG_RESTYLE_CONTINUE("there are different struct pointers");
           result = eRestyleResult_Continue;
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index b3452621dd..c6621f50c1 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -748,6 +748,11 @@ public:
   nsAbsoluteItems           mFixedItems;
   nsAbsoluteItems           mAbsoluteItems;
   nsAbsoluteItems           mFloatedItems;
+  // Items in the top layer are always fixed positioned children of the
+  // viewport frame. It differs from mFixedItems that the items here
+  // should not be caught by any other fixed-pos containing block like
+  // frames with transform or filter.
+  nsAbsoluteItems           mTopLayerItems;
 
   nsCOMPtr mFrameState;
   // These bits will be added to the state bits of any frame we construct
@@ -781,11 +786,12 @@ public:
 
   // Constructor
   // Use the passed-in history state.
-  nsFrameConstructorState(nsIPresShell*          aPresShell,
-                          nsContainerFrame*      aFixedContainingBlock,
-                          nsContainerFrame*      aAbsoluteContainingBlock,
-                          nsContainerFrame*      aFloatContainingBlock,
-                          nsILayoutHistoryState* aHistoryState);
+  nsFrameConstructorState(
+    nsIPresShell* aPresShell,
+    nsContainerFrame* aFixedContainingBlock,
+    nsContainerFrame* aAbsoluteContainingBlock,
+    nsContainerFrame* aFloatContainingBlock,
+    already_AddRefed aHistoryState);
   // Get the history state from the pres context's pres shell.
   nsFrameConstructorState(nsIPresShell*          aPresShell,
                           nsContainerFrame*      aFixedContainingBlock,
@@ -918,6 +924,20 @@ protected:
   void ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
                               ChildListID aChildListID);
 
+  /**
+   * GetOutOfFlowFrameItems selects the out-of-flow frame list the new
+   * frame should be added to. If the frame shouldn't be added to any
+   * out-of-flow list, it returns nullptr. The corresponding type of
+   * placeholder is also returned via the aPlaceholderType parameter
+   * if this method doesn't return nullptr. The caller should check
+   * whether the returned list really has a containing block.
+   */
+  nsAbsoluteItems* GetOutOfFlowFrameItems(nsIFrame* aNewFrame,
+                                          bool aCanBePositioned,
+                                          bool aCanBeFloated,
+                                          bool aIsOutOfFlowPopup,
+                                          nsFrameState* aPlaceholderType);
+
   // Our list of all pending bindings.  When we're done, we need to call
   // AddToAttachedQueue on all of them, in order.
   LinkedList mPendingBindings;
@@ -925,11 +945,12 @@ protected:
   PendingBinding* mCurrentPendingBindingInsertionPoint;
 };
 
-nsFrameConstructorState::nsFrameConstructorState(nsIPresShell*          aPresShell,
-                                                 nsContainerFrame*      aFixedContainingBlock,
-                                                 nsContainerFrame*      aAbsoluteContainingBlock,
-                                                 nsContainerFrame*      aFloatContainingBlock,
-                                                 nsILayoutHistoryState* aHistoryState)
+nsFrameConstructorState::nsFrameConstructorState(
+  nsIPresShell* aPresShell,
+  nsContainerFrame* aFixedContainingBlock,
+  nsContainerFrame* aAbsoluteContainingBlock,
+  nsContainerFrame* aFloatContainingBlock,
+  already_AddRefed aHistoryState)
   : mPresContext(aPresShell->GetPresContext()),
     mPresShell(aPresShell),
     mFrameManager(aPresShell->FrameManager()),
@@ -939,6 +960,7 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell*          aPresShe
     mFixedItems(aFixedContainingBlock),
     mAbsoluteItems(aAbsoluteContainingBlock),
     mFloatedItems(aFloatContainingBlock),
+    mTopLayerItems(do_QueryFrame(mFrameManager->GetRootFrame())),
     // See PushAbsoluteContaningBlock below
     mFrameState(aHistoryState),
     mAdditionalStateBits(nsFrameState(0)),
@@ -965,48 +987,18 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
                                                  nsContainerFrame* aFixedContainingBlock,
                                                  nsContainerFrame* aAbsoluteContainingBlock,
                                                  nsContainerFrame* aFloatContainingBlock)
-  : mPresContext(aPresShell->GetPresContext()),
-    mPresShell(aPresShell),
-    mFrameManager(aPresShell->FrameManager()),
-#ifdef MOZ_XUL
-    mPopupItems(nullptr),
-#endif
-    mFixedItems(aFixedContainingBlock),
-    mAbsoluteItems(aAbsoluteContainingBlock),
-    mFloatedItems(aFloatContainingBlock),
-    // See PushAbsoluteContaningBlock below
-    mAdditionalStateBits(nsFrameState(0)),
-    // If the fixed-pos containing block is equal to the abs-pos containing
-    // block, use the abs-pos containing block's abs-pos list for fixed-pos
-    // frames.
-    mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
-    mHavePendingPopupgroup(false),
-    mCreatingExtraFrames(false),
-    mTreeMatchContext(true, nsRuleWalker::eRelevantLinkUnvisited,
-                      aPresShell->GetDocument()),
-    mCurrentPendingBindingInsertionPoint(nullptr)
+  : nsFrameConstructorState(aPresShell, aFixedContainingBlock,
+                            aAbsoluteContainingBlock,
+                            aFloatContainingBlock,
+                            aPresShell->GetDocument()->GetLayoutHistoryState())
 {
-#ifdef MOZ_XUL
-  nsIRootBox* rootBox = nsIRootBox::GetRootBox(aPresShell);
-  if (rootBox) {
-    mPopupItems.containingBlock = rootBox->GetPopupSetFrame();
-  }
-#endif
-  MOZ_COUNT_CTOR(nsFrameConstructorState);
-  mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
 }
 
 nsFrameConstructorState::~nsFrameConstructorState()
 {
-  // Frame order comparison functions only work properly when the placeholders
-  // have been inserted into the frame tree. So for example if we have a new float
-  // containing the placeholder for a new abs-pos frame, and we process the abs-pos
-  // insertion first, then we won't be able to find the right place to insert in
-  // in the abs-pos list. So put floats in first, because they can contain placeholders
-  // for abs-pos and fixed-pos items whose containing blocks are outside the floats.
-  // Then put abs-pos frames in, because they can contain placeholders for fixed-pos
-  // items whose containing block is outside the abs-pos frames.
   MOZ_COUNT_DTOR(nsFrameConstructorState);
+  // Items in the top layer are fixed positioned children of the viewport frame.
+  ProcessFrameInsertions(mTopLayerItems, nsIFrame::kFixedList);
   ProcessFrameInsertions(mFloatedItems, nsIFrame::kFloatList);
   ProcessFrameInsertions(mAbsoluteItems, nsIFrame::kAbsoluteList);
   ProcessFrameInsertions(mFixedItems, nsIFrame::kFixedList);
@@ -1122,6 +1114,15 @@ nsFrameConstructorState::GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
     return mFloatedItems.containingBlock;
   }
 
+  if (aStyleDisplay->mTopLayer != NS_STYLE_TOP_LAYER_NONE) {
+    MOZ_ASSERT(aStyleDisplay->mTopLayer == NS_STYLE_TOP_LAYER_TOP,
+               "-moz-top-layer should be either none or top");
+    MOZ_ASSERT(mTopLayerItems.containingBlock, "No root frame?");
+    MOZ_ASSERT(aStyleDisplay->IsAbsolutelyPositionedStyle(),
+               "Top layer items should always be absolutely positioned");
+    return mTopLayerItems.containingBlock;
+  }
+
   if (aStyleDisplay->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
       mAbsoluteItems.containingBlock) {
     return mAbsoluteItems.containingBlock;
@@ -1135,6 +1136,43 @@ nsFrameConstructorState::GetGeometricParent(const nsStyleDisplay* aStyleDisplay,
   return aContentParentFrame;
 }
 
+nsAbsoluteItems*
+nsFrameConstructorState::GetOutOfFlowFrameItems(nsIFrame* aNewFrame,
+                                                bool aCanBePositioned,
+                                                bool aCanBeFloated,
+                                                bool aIsOutOfFlowPopup,
+                                                nsFrameState* aPlaceholderType)
+{
+#ifdef MOZ_XUL
+  if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
+    MOZ_ASSERT(mPopupItems.containingBlock, "Must have a popup set frame!");
+    *aPlaceholderType = PLACEHOLDER_FOR_POPUP;
+    return &mPopupItems;
+  }
+#endif // MOZ_XUL
+  if (aCanBeFloated && aNewFrame->IsFloating()) {
+    *aPlaceholderType = PLACEHOLDER_FOR_FLOAT;
+    return &mFloatedItems;
+  }
+
+  if (aCanBePositioned) {
+    const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
+    if (disp->mTopLayer != NS_STYLE_TOP_LAYER_NONE) {
+      *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
+      return &mTopLayerItems;
+    }
+    if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE) {
+      *aPlaceholderType = PLACEHOLDER_FOR_ABSPOS;
+      return &mAbsoluteItems;
+    }
+    if (disp->mPosition == NS_STYLE_POSITION_FIXED) {
+      *aPlaceholderType = PLACEHOLDER_FOR_FIXEDPOS;
+      return &GetFixedItems();
+    }
+  }
+  return nullptr;
+}
+
 void
 nsFrameConstructorState::AddChild(nsIFrame* aNewFrame,
                                   nsFrameItems& aFrameItems,
@@ -1149,53 +1187,25 @@ nsFrameConstructorState::AddChild(nsIFrame* aNewFrame,
 {
   NS_PRECONDITION(!aNewFrame->GetNextSibling(), "Shouldn't happen");
 
-  const nsStyleDisplay* disp = aNewFrame->StyleDisplay();
+  nsFrameState placeholderType;
+  nsAbsoluteItems* outOfFlowFrameItems =
+    GetOutOfFlowFrameItems(aNewFrame, aCanBePositioned, aCanBeFloated,
+                           aIsOutOfFlowPopup, &placeholderType);
 
   // The comments in GetGeometricParent regarding root table frames
-  // all apply here, unfortunately.
-
-  bool needPlaceholder = false;
-  nsFrameState placeholderType;
-  nsFrameItems* frameItems = &aFrameItems;
-#ifdef MOZ_XUL
-  if (MOZ_UNLIKELY(aIsOutOfFlowPopup)) {
-      NS_ASSERTION(aNewFrame->GetParent() == mPopupItems.containingBlock,
-                   "Popup whose parent is not the popup containing block?");
-      NS_ASSERTION(mPopupItems.containingBlock, "Must have a popup set frame!");
-      needPlaceholder = true;
-      frameItems = &mPopupItems;
-      placeholderType = PLACEHOLDER_FOR_POPUP;
-  }
-  else
-#endif // MOZ_XUL
-  if (aCanBeFloated && aNewFrame->IsFloating() &&
-      mFloatedItems.containingBlock) {
-    NS_ASSERTION(aNewFrame->GetParent() == mFloatedItems.containingBlock,
-                 "Float whose parent is not the float containing block?");
-    needPlaceholder = true;
-    frameItems = &mFloatedItems;
-    placeholderType = PLACEHOLDER_FOR_FLOAT;
-  }
-  else if (aCanBePositioned) {
-    if (disp->mPosition == NS_STYLE_POSITION_ABSOLUTE &&
-        mAbsoluteItems.containingBlock) {
-      NS_ASSERTION(aNewFrame->GetParent() == mAbsoluteItems.containingBlock,
-                   "Abs pos whose parent is not the abs pos containing block?");
-      needPlaceholder = true;
-      frameItems = &mAbsoluteItems;
-      placeholderType = PLACEHOLDER_FOR_ABSPOS;
-    }
-    if (disp->mPosition == NS_STYLE_POSITION_FIXED &&
-        GetFixedItems().containingBlock) {
-      NS_ASSERTION(aNewFrame->GetParent() == GetFixedItems().containingBlock,
-                   "Fixed pos whose parent is not the fixed pos containing block?");
-      needPlaceholder = true;
-      frameItems = &GetFixedItems();
-      placeholderType = PLACEHOLDER_FOR_FIXEDPOS;
-    }
+  // all apply here, unfortunately. Thus, we need to check whether
+  // the returned frame items really has containing block.
+  nsFrameItems* frameItems;
+  if (outOfFlowFrameItems && outOfFlowFrameItems->containingBlock) {
+    MOZ_ASSERT(aNewFrame->GetParent() == outOfFlowFrameItems->containingBlock,
+               "Parent of the frame is not the containing block?");
+    frameItems = outOfFlowFrameItems;
+  } else {
+    frameItems = &aFrameItems;
+    placeholderType = nsFrameState(0);
   }
 
-  if (needPlaceholder) {
+  if (placeholderType) {
     NS_ASSERTION(frameItems != &aFrameItems,
                  "Putting frame in-flow _and_ want a placeholder?");
     nsIFrame* placeholderFrame =
@@ -1233,7 +1243,8 @@ nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
                              aChildListID == nsIFrame::kFloatList)    ||  \
                             (&aFrameItems == &mAbsoluteItems &&           \
                              aChildListID == nsIFrame::kAbsoluteList) ||  \
-                            (&aFrameItems == &mFixedItems &&              \
+                            ((&aFrameItems == &mFixedItems ||             \
+                              &aFrameItems == &mTopLayerItems) &&         \
                              aChildListID == nsIFrame::kFixedList)
 #ifdef MOZ_XUL
   NS_PRECONDITION(NS_NONXUL_LIST_TEST ||
@@ -1274,6 +1285,11 @@ nsFrameConstructorState::ProcessFrameInsertions(nsAbsoluteItems& aFrameItems,
     } else {
       containingBlock->SetInitialChildList(aChildListID, aFrameItems);
     }
+  } else if (aChildListID == nsIFrame::kFixedList ||
+             aChildListID == nsIFrame::kAbsoluteList) {
+    // The order is not important for abs-pos/fixed-pos frame list, just
+    // append the frame items to the list directly.
+    mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems);
   } else {
     // Note that whether the frame construction context is doing an append or
     // not is not helpful here, since it could be appending to some frame in
@@ -2329,7 +2345,7 @@ nsCSSFrameConstructor::ConstructDocElementFrame(Element*                 aDocEle
   nsFrameConstructorState state(mPresShell,
                                 GetAbsoluteContainingBlock(mDocElementContainingBlock, FIXED_POS),
                                 nullptr,
-                                nullptr, aFrameState);
+                                nullptr, do_AddRef(Move(aFrameState)));
   // Initialize the ancestor filter with null for now; we'll push
   // aDocElement once we finish resolving style for it.
   state.mTreeMatchContext.InitAncestors(nullptr);
@@ -7684,7 +7700,7 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent*            aContainer,
                                 GetAbsoluteContainingBlock(insertion.mParentFrame, FIXED_POS),
                                 GetAbsoluteContainingBlock(insertion.mParentFrame, ABS_POS),
                                 GetFloatContainingBlock(insertion.mParentFrame),
-                                aFrameState);
+                                do_AddRef(Move(aFrameState)));
   state.mTreeMatchContext.InitAncestors(aContainer ?
                                           aContainer->AsElement() :
                                           nullptr);
@@ -11421,7 +11437,7 @@ nsCSSFrameConstructor::CreateListBoxContent(nsPresContext*         aPresContext,
     nsFrameConstructorState state(mPresShell, GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
                                   GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
                                   GetFloatContainingBlock(aParentFrame),
-                                  mTempFrameTreeState);
+                                  do_AddRef(mTempFrameTreeState.get()));
 
     // If we ever initialize the ancestor filter on |state|, make sure
     // to push the right parent!
diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h
index 83e20aa2f9..58b1c063b4 100644
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -864,6 +864,13 @@ public:
 
   NS_DECLARE_FRAME_PROPERTY(OutOfFlowDisplayDataProperty,
                             DeleteValue)
+
+  static OutOfFlowDisplayData* GetOutOfFlowData(nsIFrame* aFrame)
+  {
+    return static_cast(
+      aFrame->Properties().Get(OutOfFlowDisplayDataProperty()));
+  }
+
   NS_DECLARE_FRAME_PROPERTY(Preserve3DDirtyRectProperty, DeleteValue)
 
   nsPresContext* CurrentPresContext() {
diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp
index 011e2084d3..62f0c19206 100644
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2251,7 +2251,7 @@ static bool
 AppendAgentSheet(nsIStyleSheet *aSheet, void *aData)
 {
   nsStyleSet *styleSet = static_cast(aData);
-  styleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, aSheet);
+  styleSet->AppendStyleSheet(SheetType::Agent, aSheet);
   return true;
 }
 
@@ -2259,7 +2259,7 @@ static bool
 PrependUserSheet(nsIStyleSheet *aSheet, void *aData)
 {
   nsStyleSet *styleSet = static_cast(aData);
-  styleSet->PrependStyleSheet(nsStyleSet::eUserSheet, aSheet);
+  styleSet->PrependStyleSheet(SheetType::User, aSheet);
   return true;
 }
 
@@ -2302,7 +2302,7 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
   }
 
   if (sheet)
-    styleSet->AppendStyleSheet(nsStyleSet::eUserSheet, sheet);
+    styleSet->AppendStyleSheet(SheetType::User, sheet);
 
   // Append chrome sheets (scrollbars + forms).
   bool shouldOverride = false;
@@ -2338,7 +2338,7 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
           cssLoader->LoadSheetSync(uri, getter_AddRefs(csssheet));
           if (!csssheet) continue;
 
-          styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, csssheet);
+          styleSet->PrependStyleSheet(SheetType::Agent, csssheet);
           shouldOverride = true;
         }
         free(str);
@@ -2349,15 +2349,10 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
   if (!shouldOverride) {
     sheet = nsLayoutStylesheetCache::ScrollbarsSheet();
     if (sheet) {
-      styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
   }
 
-  sheet = nsLayoutStylesheetCache::FullScreenOverrideSheet();
-  if (sheet) {
-    styleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, sheet);
-  }
-
   if (!aDocument->IsSVGDocument()) {
     // !!! IMPORTANT - KEEP THIS BLOCK IN SYNC WITH
     // !!! SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded.
@@ -2370,12 +2365,12 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
 
     sheet = nsLayoutStylesheetCache::NumberControlSheet();
     if (sheet) {
-      styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
 
     sheet = nsLayoutStylesheetCache::FormsSheet();
     if (sheet) {
-      styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
 
     if (aDocument->LoadsFullXULStyleSheetUpFront()) {
@@ -2383,7 +2378,7 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
       // up-front here.
       sheet = nsLayoutStylesheetCache::XULSheet();
       if (sheet) {
-        styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+        styleSet->PrependStyleSheet(SheetType::Agent, sheet);
       }
     }
 
@@ -2391,25 +2386,25 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
     if (sheet) {
       // Load the minimal XUL rules for scrollbars and a few other XUL things
       // that non-XUL (typically HTML) documents commonly use.
-      styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
 
     sheet = nsLayoutStylesheetCache::CounterStylesSheet();
     if (sheet) {
-      styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
 
     if (nsLayoutUtils::ShouldUseNoScriptSheet(aDocument)) {
       sheet = nsLayoutStylesheetCache::NoScriptSheet();
       if (sheet) {
-        styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+        styleSet->PrependStyleSheet(SheetType::Agent, sheet);
       }
     }
 
     if (nsLayoutUtils::ShouldUseNoFramesSheet(aDocument)) {
       sheet = nsLayoutStylesheetCache::NoFramesSheet();
       if (sheet) {
-        styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+        styleSet->PrependStyleSheet(SheetType::Agent, sheet);
       }
     }
 
@@ -2418,16 +2413,16 @@ nsDocumentViewer::CreateStyleSet(nsIDocument* aDocument,
 
     sheet = nsLayoutStylesheetCache::HTMLSheet();
     if (sheet) {
-      styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
 
-    styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet,
+    styleSet->PrependStyleSheet(SheetType::Agent,
                                 nsLayoutStylesheetCache::UASheet());
   } else {
     // SVG documents may have scrollbars and need the scrollbar styling.
     sheet = nsLayoutStylesheetCache::MinimalXULSheet();
     if (sheet) {
-      styleSet->PrependStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
   }
 
diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp
index a9d4c8fb80..57e76535a0 100644
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1348,11 +1348,11 @@ nsPresContext::CompatibilityModeChanged()
   if (needsQuirkSheet) {
     // quirk.css needs to come after html.css; we just keep it at the end.
     DebugOnly rv =
-      styleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->AppendStyleSheet(SheetType::Agent, sheet);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to insert quirk.css");
   } else {
     DebugOnly rv =
-      styleSet->RemoveStyleSheet(nsStyleSet::eAgentSheet, sheet);
+      styleSet->RemoveStyleSheet(SheetType::Agent, sheet);
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to remove quirk.css");
   }
 
diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp
index 276e0fb25a..19ee6b6cf3 100644
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -168,6 +168,7 @@
 #include "mozilla/Telemetry.h"
 #include "nsCanvasFrame.h"
 #include "nsIImageLoadingContent.h"
+#include "nsImageFrame.h"
 #include "nsIScreen.h"
 #include "nsIScreenManager.h"
 #include "nsPlaceholderFrame.h"
@@ -1407,7 +1408,7 @@ PresShell::UpdatePreferenceStyles()
 
   RemovePreferenceStyles();
 
-  mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, newPrefSheet);
+  mStyleSet->AppendStyleSheet(SheetType::User, newPrefSheet);
   mPrefStyleSheet = newPrefSheet;
 
   mStyleSet->EndUpdate();
@@ -1417,7 +1418,7 @@ void
 PresShell::RemovePreferenceStyles()
 {
   if (mPrefStyleSheet) {
-    mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
+    mStyleSet->RemoveStyleSheet(SheetType::User, mPrefStyleSheet);
     mPrefStyleSheet = nullptr;
   }
 }
@@ -1441,13 +1442,13 @@ PresShell::AddUserSheet(nsISupports* aSheet)
   // Iterate forwards when removing so the searches for RemoveStyleSheet are as
   // short as possible.
   for (i = 0; i < userSheets.Count(); ++i) {
-    mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
+    mStyleSet->RemoveStyleSheet(SheetType::User, userSheets[i]);
   }
 
   // Now iterate backwards, so that the order of userSheets will be the same as
   // the order of sheets from it in the style set.
   for (i = userSheets.Count() - 1; i >= 0; --i) {
-    mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
+    mStyleSet->PrependStyleSheet(SheetType::User, userSheets[i]);
   }
 
   mStyleSet->EndUpdate();
@@ -1465,7 +1466,7 @@ PresShell::AddAgentSheet(nsISupports* aSheet)
     return;
   }
 
-  mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
+  mStyleSet->AppendStyleSheet(SheetType::Agent, sheet);
   ReconstructStyleData();
 }
 
@@ -1481,16 +1482,16 @@ PresShell::AddAuthorSheet(nsISupports* aSheet)
   // added with the StyleSheetService.
   nsIStyleSheet* firstAuthorSheet = mDocument->FirstAdditionalAuthorSheet();
   if (firstAuthorSheet) {
-    mStyleSet->InsertStyleSheetBefore(nsStyleSet::eDocSheet, sheet, firstAuthorSheet);
+    mStyleSet->InsertStyleSheetBefore(SheetType::Doc, sheet, firstAuthorSheet);
   } else {
-    mStyleSet->AppendStyleSheet(nsStyleSet::eDocSheet, sheet);
+    mStyleSet->AppendStyleSheet(SheetType::Doc, sheet);
   }
 
   ReconstructStyleData();
 }
 
 void
-PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet)
+PresShell::RemoveSheet(SheetType aType, nsISupports* aSheet)
 {
   nsCOMPtr sheet = do_QueryInterface(aSheet);
   if (!sheet) {
@@ -4071,12 +4072,10 @@ PresShell::FlushPendingNotifications(mozilla::ChangesToFlush aFlush)
       if (aFlush.mFlushAnimations &&
           !mPresContext->StyleUpdateForAllAnimationsIsUpToDate()) {
         if (mPresContext->AnimationManager()) {
-          mPresContext->AnimationManager()->
-            FlushAnimations(CommonAnimationManager::Cannot_Throttle);
+          mPresContext->AnimationManager()->FlushAnimations();
         }
         if (mPresContext->TransitionManager()) {
-          mPresContext->TransitionManager()->
-            FlushAnimations(CommonAnimationManager::Cannot_Throttle);
+          mPresContext->TransitionManager()->FlushAnimations();
         }
         mPresContext->TickLastStyleUpdateForAllAnimations();
       }
@@ -8685,10 +8684,10 @@ nsresult
 PresShell::GetAgentStyleSheets(nsCOMArray& aSheets)
 {
   aSheets.Clear();
-  int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet);
+  int32_t sheetCount = mStyleSet->SheetCount(SheetType::Agent);
 
   for (int32_t i = 0; i < sheetCount; ++i) {
-    nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
+    nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(SheetType::Agent, i);
     if (!aSheets.AppendObject(sheet))
       return NS_ERROR_OUT_OF_MEMORY;
   }
@@ -8699,19 +8698,19 @@ PresShell::GetAgentStyleSheets(nsCOMArray& aSheets)
 nsresult
 PresShell::SetAgentStyleSheets(const nsCOMArray& aSheets)
 {
-  return mStyleSet->ReplaceSheets(nsStyleSet::eAgentSheet, aSheets);
+  return mStyleSet->ReplaceSheets(SheetType::Agent, aSheets);
 }
 
 nsresult
 PresShell::AddOverrideStyleSheet(nsIStyleSheet *aSheet)
 {
-  return mStyleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
+  return mStyleSet->PrependStyleSheet(SheetType::Override, aSheet);
 }
 
 nsresult
 PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet)
 {
-  return mStyleSet->RemoveStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
+  return mStyleSet->RemoveStyleSheet(SheetType::Override, aSheet);
 }
 
 static void
@@ -9402,6 +9401,11 @@ PresShell::Observe(nsISupports* aSubject,
                    const char* aTopic,
                    const char16_t* aData)
 {
+  if (mIsDestroying) {
+    NS_WARNING("our observers should have been unregistered by now");
+    return NS_OK;
+  }
+
 #ifdef MOZ_XUL
   if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
     nsIFrame *rootFrame = mFrameConstructor->GetRootFrame();
@@ -9456,17 +9460,17 @@ PresShell::Observe(nsISupports* aSubject,
   }
 
   if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) {
-    RemoveSheet(nsStyleSet::eAgentSheet, aSubject);
+    RemoveSheet(SheetType::Agent, aSubject);
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) {
-    RemoveSheet(nsStyleSet::eUserSheet, aSubject);
+    RemoveSheet(SheetType::User, aSubject);
     return NS_OK;
   }
 
   if (!nsCRT::strcmp(aTopic, "author-sheet-removed") && mStyleSet) {
-    RemoveSheet(nsStyleSet::eDocSheet, aSubject);
+    RemoveSheet(SheetType::Doc, aSubject);
     return NS_OK;
   }
 
@@ -9809,35 +9813,35 @@ PresShell::CloneStyleSet(nsStyleSet* aSet)
 {
   nsStyleSet *clone = new nsStyleSet();
 
-  int32_t i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet);
+  int32_t i, n = aSet->SheetCount(SheetType::Override);
   for (i = 0; i < n; i++) {
-    nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i);
+    nsIStyleSheet* ss = aSet->StyleSheetAt(SheetType::Override, i);
     if (ss)
-      clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss);
+      clone->AppendStyleSheet(SheetType::Override, ss);
   }
 
   // The document expects to insert document stylesheets itself
 #if 0
-  n = aSet->SheetCount(nsStyleSet::eDocSheet);
+  n = aSet->SheetCount(SheetType::Doc);
   for (i = 0; i < n; i++) {
-    nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i);
+    nsIStyleSheet* ss = aSet->StyleSheetAt(SheetType::Doc, i);
     if (ss)
       clone->AddDocStyleSheet(ss, mDocument);
   }
 #endif
 
-  n = aSet->SheetCount(nsStyleSet::eUserSheet);
+  n = aSet->SheetCount(SheetType::User);
   for (i = 0; i < n; i++) {
-    nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i);
+    nsIStyleSheet* ss = aSet->StyleSheetAt(SheetType::User, i);
     if (ss)
-      clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss);
+      clone->AppendStyleSheet(SheetType::User, ss);
   }
 
-  n = aSet->SheetCount(nsStyleSet::eAgentSheet);
+  n = aSet->SheetCount(SheetType::Agent);
   for (i = 0; i < n; i++) {
-    nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
+    nsIStyleSheet* ss = aSet->StyleSheetAt(SheetType::Agent, i);
     if (ss)
-      clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss);
+      clone->AppendStyleSheet(SheetType::Agent, ss);
   }
   return clone;
 }
@@ -9966,9 +9970,9 @@ PresShell::ListStyleContexts(nsIFrame *aRootFrame, FILE *out, int32_t aIndent)
 void
 PresShell::ListStyleSheets(FILE *out, int32_t aIndent)
 {
-  int32_t sheetCount = mStyleSet->SheetCount(nsStyleSet::eDocSheet);
+  int32_t sheetCount = mStyleSet->SheetCount(SheetType::Doc);
   for (int32_t i = 0; i < sheetCount; ++i) {
-    mStyleSet->StyleSheetAt(nsStyleSet::eDocSheet, i)->List(out, aIndent);
+    mStyleSet->StyleSheetAt(SheetType::Doc, i)->List(out, aIndent);
     fputs("\n", out);
   }
 }
@@ -10721,7 +10725,23 @@ nsresult
 PresShell::UpdateImageLockingState()
 {
   // We're locked if we're both thawed and active.
-  return mDocument->SetImageLockingState(!mFrozen && mIsActive);
+  bool locked = !mFrozen && mIsActive;
+
+  nsresult rv = mDocument->SetImageLockingState(locked);
+
+  if (locked) {
+    // Request decodes for visible images; we want to start decoding as
+    // quickly as possible when we get foregrounded to minimize flashing.
+    for (auto iter = mVisibleImages.Iter(); !iter.Done(); iter.Next()) {
+      nsCOMPtr content = do_QueryInterface(iter.Get()->GetKey());
+      nsImageFrame* imageFrame = do_QueryFrame(content->GetPrimaryFrame());
+      if (imageFrame) {
+        imageFrame->MaybeDecodeForPredictedSize();
+      }
+    }
+  }
+
+  return rv;
 }
 
 PresShell*
@@ -10949,16 +10969,16 @@ nsresult
 nsIPresShell::HasRuleProcessorUsedByMultipleStyleSets(uint32_t aSheetType,
                                                       bool* aRetVal)
 {
-  nsStyleSet::sheetType type;
+  SheetType type;
   switch (aSheetType) {
     case nsIStyleSheetService::AGENT_SHEET:
-      type = nsStyleSet::eAgentSheet;
+      type = SheetType::Agent;
       break;
     case nsIStyleSheetService::USER_SHEET:
-      type = nsStyleSet::eUserSheet;
+      type = SheetType::User;
       break;
     case nsIStyleSheetService::AUTHOR_SHEET:
-      type = nsStyleSet::eDocSheet;
+      type = SheetType::Doc;
       break;
     default:
       MOZ_ASSERT(false, "unexpected aSheetType value");
diff --git a/layout/base/nsPresShell.h b/layout/base/nsPresShell.h
index 052f1a90f2..d099d25bd3 100644
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -564,7 +564,7 @@ protected:
   void AddUserSheet(nsISupports* aSheet);
   void AddAgentSheet(nsISupports* aSheet);
   void AddAuthorSheet(nsISupports* aSheet);
-  void RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet);
+  void RemoveSheet(mozilla::SheetType aType, nsISupports* aSheet);
 
   // Hide a view if it is a popup
   void HideViewIfPopup(nsView* aView);
diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp
index c4ad2ff787..e4f266f34b 100644
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1486,44 +1486,32 @@ TakeFrameRequestCallbacksFrom(nsIDocument* aDocument,
   aDocument->TakeFrameRequestCallbacks(aTarget.LastElement().mCallbacks);
 }
 
-namespace {
-  enum class AnimationEventType {
-    CSSAnimations,
-    CSSTransitions
-  };
-
-  struct DispatchAnimationEventParams {
-    AnimationEventType mEventType;
-    nsRefreshDriver* mRefreshDriver;
-  };
-}
-
 static bool
 DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument,
-                                      void* aParams)
+                                      void* aRefreshDriver)
 {
-  MOZ_ASSERT(aParams, "Animation event parameters should be set");
-  auto params = static_cast(aParams);
-
   nsIPresShell* shell = aDocument->GetShell();
   if (!shell) {
     return true;
   }
 
   nsPresContext* context = shell->GetPresContext();
-  if (!context || context->RefreshDriver() != params->mRefreshDriver) {
+  if (!context || context->RefreshDriver() != aRefreshDriver) {
     return true;
   }
 
   nsCOMPtr kungFuDeathGrip(aDocument);
 
-  if (params->mEventType == AnimationEventType::CSSAnimations) {
-    context->AnimationManager()->DispatchEvents();
-  } else {
-    context->TransitionManager()->DispatchEvents();
-  }
+  context->TransitionManager()->SortEvents();
+  context->AnimationManager()->SortEvents();
+
+  // Dispatch transition events first since transitions conceptually sit
+  // below animations in terms of compositing order.
+  context->TransitionManager()->DispatchEvents();
+  context->AnimationManager()->DispatchEvents();
+
   aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments,
-                                   aParams);
+                                   aRefreshDriver);
 
   return true;
 }
@@ -1535,19 +1523,7 @@ nsRefreshDriver::DispatchAnimationEvents()
     return;
   }
 
-  nsIDocument* doc = mPresContext->Document();
-
-  // Dispatch transition events first since transitions conceptually sit
-  // below animations in terms of compositing order.
-  DispatchAnimationEventParams params { AnimationEventType::CSSTransitions,
-                                        this };
-  DispatchAnimationEventsOnSubDocuments(doc, ¶ms);
-  if (!mPresContext) {
-    return;
-  }
-
-  params.mEventType = AnimationEventType::CSSAnimations;
-  DispatchAnimationEventsOnSubDocuments(doc, ¶ms);
+  DispatchAnimationEventsOnSubDocuments(mPresContext->Document(), this);
 }
 
 void
diff --git a/layout/generic/WritingModes.h b/layout/generic/WritingModes.h
index 410d9ffb2c..36abe007dc 100644
--- a/layout/generic/WritingModes.h
+++ b/layout/generic/WritingModes.h
@@ -456,10 +456,19 @@ public:
   explicit WritingMode(nsStyleContext* aStyleContext)
   {
     NS_ASSERTION(aStyleContext, "we need an nsStyleContext here");
+    InitFromStyleVisibility(aStyleContext->StyleVisibility());
+  }
 
-    const nsStyleVisibility* styleVisibility = aStyleContext->StyleVisibility();
+  explicit WritingMode(const nsStyleVisibility* aStyleVisibility)
+  {
+    NS_ASSERTION(aStyleVisibility, "we need an nsStyleVisibility here");
+    InitFromStyleVisibility(aStyleVisibility);
+  }
 
-    switch (styleVisibility->mWritingMode) {
+private:
+  void InitFromStyleVisibility(const nsStyleVisibility* aStyleVisibility)
+  {
+    switch (aStyleVisibility->mWritingMode) {
       case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
         mWritingMode = 0;
         break;
@@ -469,7 +478,7 @@ public:
         mWritingMode = eBlockFlowMask |
                        eLineOrientMask |
                        eOrientationMask;
-        uint8_t textOrientation = aStyleContext->StyleVisibility()->mTextOrientation;
+        uint8_t textOrientation = aStyleVisibility->mTextOrientation;
         if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
           mWritingMode |= eSidewaysMask;
         }
@@ -479,7 +488,7 @@ public:
       case NS_STYLE_WRITING_MODE_VERTICAL_RL:
       {
         mWritingMode = eOrientationMask;
-        uint8_t textOrientation = aStyleContext->StyleVisibility()->mTextOrientation;
+        uint8_t textOrientation = aStyleVisibility->mTextOrientation;
         if (textOrientation == NS_STYLE_TEXT_ORIENTATION_SIDEWAYS) {
           mWritingMode |= eSidewaysMask;
         }
@@ -504,23 +513,34 @@ public:
         break;
     }
 
-    if (NS_STYLE_DIRECTION_RTL == styleVisibility->mDirection) {
+    if (NS_STYLE_DIRECTION_RTL == aStyleVisibility->mDirection) {
       mWritingMode ^= eInlineFlowMask | eBidiMask;
     }
   }
+public:
 
-  // For unicode-bidi: plaintext, reset the direction of the writing mode from
-  // the bidi paragraph level of the content
-
-  //XXX change uint8_t to UBiDiLevel after bug 924851
+  /**
+   * This function performs fixup for elements with 'unicode-bidi: plaintext',
+   * where inline directionality is derived from the Unicode bidi categories
+   * of the element's content, and not the CSS 'direction' property.
+   *
+   * The WritingMode constructor will have already incorporated the 'direction'
+   * property into our flag bits, so such elements need to use this method
+   * (after resolving the bidi level of their content) to update the direction
+   * bits as needed.
+   *
+   * If it turns out that our bidi direction already matches what plaintext
+   * resolution determined, there's nothing to do here. If it didn't (i.e. if
+   * the rtl-ness doesn't match), then we correct the direction by flipping the
+   * same bits that get flipped in the constructor's CSS 'direction'-based
+   * chunk.
+   *
+   * XXX change uint8_t to UBiDiLevel after bug 924851
+   */
   void SetDirectionFromBidiLevel(uint8_t level)
   {
-    if (IS_LEVEL_RTL(level)) {
-      // set RTL
-      mWritingMode |= eBidiMask;
-    } else {
-      // set LTR
-      mWritingMode &= ~eBidiMask;
+    if (IS_LEVEL_RTL(level) == IsBidiLTR()) {
+      mWritingMode ^= eBidiMask | eInlineFlowMask;
     }
   }
 
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index ea98a5d30e..c8fd417ddc 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2325,9 +2325,12 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
   // dirty rect in child-relative coordinates
   nsRect dirty = aDirtyRect - child->GetOffsetTo(this);
 
+  const nsStyleDisplay* disp;
   nsIAtom* childType = child->GetType();
   nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
-  if (childType == nsGkAtoms::placeholderFrame) {
+  if (childType != nsGkAtoms::placeholderFrame) {
+    disp = child->StyleDisplay();
+  } else {
     nsPlaceholderFrame* placeholder = static_cast(child);
     child = placeholder->GetOutOfFlowFrame();
     NS_ASSERTION(child, "No out of flow frame?");
@@ -2337,6 +2340,16 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
     if (!child || nsLayoutUtils::IsPopup(child) ||
         (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT))
       return;
+    MOZ_ASSERT(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW);
+    disp = child->StyleDisplay();
+    // If the out-of-flow frame is in the top layer, the viewport frame
+    // will paint it. Skip it here. Note that, only out-of-flow frames
+    // with this property should be skipped, because non-HTML elements
+    // may stop their children from being out-of-flow. Those frames
+    // should still be handled in the normal in-flow path.
+    if (disp->mTopLayer != NS_STYLE_TOP_LAYER_NONE) {
+      return;
+    }
     // Make sure that any attempt to use childType below is disappointed. We
     // could call GetType again but since we don't currently need it, let's
     // avoid the virtual call.
@@ -2344,8 +2357,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
     // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE
     if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE)
       return;
-    savedOutOfFlowData = static_cast
-      (child->Properties().Get(nsDisplayListBuilder::OutOfFlowDisplayDataProperty()));
+    savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
     if (savedOutOfFlowData) {
       dirty = savedOutOfFlowData->mDirtyRect;
     } else {
@@ -2415,7 +2427,6 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
 
   // Child is composited if it's transformed, partially transparent, or has
   // SVG effects or a blend mode..
-  const nsStyleDisplay* disp = child->StyleDisplay();
   const nsStylePosition* pos = child->StylePosition();
   bool isVisuallyAtomic = child->HasOpacity()
     || child->IsTransformed()
diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h
index 44e640bb6b..7154f9c973 100644
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -231,6 +231,7 @@ protected:
 protected:
   friend class nsImageListener;
   friend class nsImageLoadingContent;
+  friend class PresShell;
 
   nsresult OnSizeAvailable(imgIRequest* aRequest, imgIContainer* aImage);
   nsresult OnFrameUpdate(imgIRequest* aRequest, const nsIntRect* aRect);
diff --git a/layout/generic/nsViewportFrame.cpp b/layout/generic/nsViewportFrame.cpp
index a0e73a2f4c..e51066c043 100644
--- a/layout/generic/nsViewportFrame.cpp
+++ b/layout/generic/nsViewportFrame.cpp
@@ -14,6 +14,7 @@
 #include "nsSubDocumentFrame.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "GeckoProfiler.h"
+#include "nsIMozBrowserFrame.h"
 
 using namespace mozilla;
 
@@ -51,14 +52,89 @@ ViewportFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   PROFILER_LABEL("ViewportFrame", "BuildDisplayList",
     js::ProfileEntry::Category::GRAPHICS);
 
-  nsIFrame* kid = mFrames.FirstChild();
-  if (!kid)
-    return;
+  if (nsIFrame* kid = mFrames.FirstChild()) {
+    // make the kid's BorderBackground our own. This ensures that the canvas
+    // frame's background becomes our own background and therefore appears
+    // below negative z-index elements.
+    BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
+  }
 
-  // make the kid's BorderBackground our own. This ensures that the canvas
-  // frame's background becomes our own background and therefore appears
-  // below negative z-index elements.
-  BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
+  nsDisplayList topLayerList;
+  BuildDisplayListForTopLayer(aBuilder, &topLayerList);
+  if (!topLayerList.IsEmpty()) {
+    // Wrap the whole top layer in a single item with maximum z-index,
+    // and append it at the very end, so that it stays at the topmost.
+    nsDisplayWrapList* wrapList =
+      new (aBuilder) nsDisplayWrapList(aBuilder, this, &topLayerList);
+    wrapList->SetOverrideZIndex(
+      std::numeric_limitsZIndex())>::max());
+    aLists.PositionedDescendants()->AppendNewToTop(wrapList);
+  }
+}
+
+#ifdef DEBUG
+/**
+ * Returns whether we are going to put an element in the top layer for
+ * fullscreen. This function should matches the CSS rule in ua.css.
+ */
+static bool
+ShouldInTopLayerForFullscreen(Element* aElement)
+{
+  if (!aElement->GetParent()) {
+    return false;
+  }
+  nsCOMPtr browserFrame = do_QueryInterface(aElement);
+  if (browserFrame && browserFrame->GetReallyIsBrowserOrApp()) {
+    return false;
+  }
+  return true;
+}
+#endif // DEBUG
+
+void
+ViewportFrame::BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
+                                           nsDisplayList* aList)
+{
+  nsIDocument* doc = PresContext()->Document();
+  nsTArray fullscreenStack = doc->GetFullscreenStack();
+  for (Element* elem : fullscreenStack) {
+    if (nsIFrame* frame = elem->GetPrimaryFrame()) {
+      // There are two cases where an element in fullscreen is not in
+      // the top layer:
+      // 1. When building display list for purpose other than painting,
+      //    it is possible that there is inconsistency between the style
+      //    info and the content tree.
+      // 2. This is an element which we are not going to put in the top
+      //    layer for fullscreen. See ShouldInTopLayerForFullscreen().
+      // In both cases, we want to skip the frame here and paint it in
+      // the normal path.
+      if (frame->StyleDisplay()->mTopLayer == NS_STYLE_TOP_LAYER_NONE) {
+        MOZ_ASSERT(!aBuilder->IsForPainting() ||
+                   !ShouldInTopLayerForFullscreen(elem));
+        continue;
+      }
+      MOZ_ASSERT(ShouldInTopLayerForFullscreen(elem));
+      // Inner SVG, MathML elements, as well as children of some XUL
+      // elements are not allowed to be out-of-flow. They should not
+      // be handled as top layer element here.
+      if (!(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+        MOZ_ASSERT(!elem->GetParent()->IsHTMLElement(), "HTML element "
+                   "should always be out-of-flow if in the top layer");
+        continue;
+      }
+      MOZ_ASSERT(frame->GetParent() == this);
+
+      nsRect dirty;
+      nsDisplayListBuilder::OutOfFlowDisplayData*
+        savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(frame);
+      if (savedOutOfFlowData) {
+        dirty = savedOutOfFlowData->mDirtyRect;
+      }
+      nsDisplayList list;
+      frame->BuildDisplayListForStackingContext(aBuilder, dirty, &list);
+      aList->AppendToTop(&list);
+    }
+  }
 }
 
 #ifdef DEBUG
diff --git a/layout/generic/nsViewportFrame.h b/layout/generic/nsViewportFrame.h
index ad7739363b..13b5950454 100644
--- a/layout/generic/nsViewportFrame.h
+++ b/layout/generic/nsViewportFrame.h
@@ -63,6 +63,9 @@ public:
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
+  void BuildDisplayListForTopLayer(nsDisplayListBuilder* aBuilder,
+                                   nsDisplayList* aList);
+
   virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
   virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
   virtual void Reflow(nsPresContext*           aPresContext,
diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp
index 9aa4a6f358..d9e6edd1df 100644
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -83,11 +83,11 @@ inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength,
   nsIPresShell* presShell = document->GetShell();
   if (presShell) {
     nsStyleSet* styleSet = presShell->StyleSet();
-    nsStyleSet::sheetType sheetType = nsStyleSet::eAgentSheet;
+    SheetType sheetType = SheetType::Agent;
     for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) {
       sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i));
     }
-    sheetType = nsStyleSet::eUserSheet;
+    sheetType = SheetType::User;
     for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) {
       sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i));
     }
@@ -511,14 +511,14 @@ inDOMUtils::GetCSSPropertyNames(uint32_t aFlags, uint32_t* aCount,
   char16_t** props =
     static_cast(moz_xmalloc(maxCount * sizeof(char16_t*)));
 
-#define DO_PROP(_prop)                                                  \
-  PR_BEGIN_MACRO                                                        \
-    nsCSSProperty cssProp = nsCSSProperty(_prop);                       \
-    if (nsCSSProps::IsEnabled(cssProp)) {                               \
-      props[propCount] =                                                \
-        ToNewUnicode(nsDependentCString(kCSSRawProperties[_prop]));     \
-      ++propCount;                                                      \
-    }                                                                   \
+#define DO_PROP(_prop)                                                       \
+  PR_BEGIN_MACRO                                                             \
+    nsCSSProperty cssProp = nsCSSProperty(_prop);                            \
+    if (nsCSSProps::IsEnabled(cssProp, nsCSSProps::eEnabledForAllContent)) { \
+      props[propCount] =                                                     \
+        ToNewUnicode(nsDependentCString(kCSSRawProperties[_prop]));          \
+      ++propCount;                                                           \
+    }                                                                        \
   PR_END_MACRO
 
   // prop is the property id we're considering; propCount is how many properties
@@ -1259,7 +1259,7 @@ inDOMUtils::ParseStyleSheet(nsIDOMCSSStyleSheet *aSheet,
   RefPtr sheet = do_QueryObject(aSheet);
   NS_ENSURE_ARG_POINTER(sheet);
 
-  return sheet->ParseSheet(aInput);
+  return sheet->ReparseSheet(aInput);
 }
 
 NS_IMETHODIMP
diff --git a/layout/inspector/tests/bug1202095-2.css b/layout/inspector/tests/bug1202095-2.css
new file mode 100644
index 0000000000..a484814ebf
--- /dev/null
+++ b/layout/inspector/tests/bug1202095-2.css
@@ -0,0 +1,7 @@
+/*
+ * Bug 1202095 - parseStyleSheet should not re-load @imports
+ */
+
+body {
+  color: chartreuse;
+}
diff --git a/layout/inspector/tests/bug1202095.css b/layout/inspector/tests/bug1202095.css
new file mode 100644
index 0000000000..fa8ef0feb6
--- /dev/null
+++ b/layout/inspector/tests/bug1202095.css
@@ -0,0 +1,7 @@
+/*
+ * Bug 1202095 - parseStyleSheet should not re-load @imports
+ */
+
+body {
+  background-color: purple;
+}
diff --git a/layout/inspector/tests/mochitest.ini b/layout/inspector/tests/mochitest.ini
index 6507fbbe07..4dd9acdd3a 100644
--- a/layout/inspector/tests/mochitest.ini
+++ b/layout/inspector/tests/mochitest.ini
@@ -1,5 +1,7 @@
 [DEFAULT]
 support-files = 
+  bug1202095.css
+  bug1202095-2.css
   bug856317.css
   file_bug522601.html
 
@@ -21,4 +23,6 @@ support-files =
 [test_get_all_style_sheets.html]
 [test_is_valid_css_color.html]
 [test_isinheritableproperty.html]
+[test_parseStyleSheet.html]
+[test_parseStyleSheetImport.html]
 [test_selectormatcheselement.html]
diff --git a/layout/inspector/tests/test_parseStyleSheet.html b/layout/inspector/tests/test_parseStyleSheet.html
new file mode 100644
index 0000000000..f7ddf6e9e5
--- /dev/null
+++ b/layout/inspector/tests/test_parseStyleSheet.html
@@ -0,0 +1,34 @@
+
+
+
+  
+    Test for Bug 1195978
+    
+    
+    
+  
+  
+    
+  
+
diff --git a/layout/inspector/tests/test_parseStyleSheetImport.html b/layout/inspector/tests/test_parseStyleSheetImport.html
new file mode 100644
index 0000000000..73fef2d51a
--- /dev/null
+++ b/layout/inspector/tests/test_parseStyleSheetImport.html
@@ -0,0 +1,83 @@
+
+
+
+  
+    Test for Bug 1202095
+    
+    
+    
+  
+  
+    
+  
+
diff --git a/layout/reftests/bugs/1209603-1-ref.html b/layout/reftests/bugs/1209603-1-ref.html
new file mode 100644
index 0000000000..a2eacedbc6
--- /dev/null
+++ b/layout/reftests/bugs/1209603-1-ref.html
@@ -0,0 +1,13 @@
+
+Testcase, bug 1209603
+
+
+

Should be 40px font size.

+ +

Should be 20px font size.

diff --git a/layout/reftests/bugs/1209603-1.html b/layout/reftests/bugs/1209603-1.html new file mode 100644 index 0000000000..5bbc45ee40 --- /dev/null +++ b/layout/reftests/bugs/1209603-1.html @@ -0,0 +1,50 @@ + +Testcase, bug 1209603 + + +

Should be 40px font size.

+ + + + +

Should be 20px font size.

+ + diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 5e57d2d18d..8f6dc4ccb5 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1933,3 +1933,4 @@ fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 1174332-1.html 1174332-1-ref.html == 1179078-1.html 1179078-1-ref.html == 1179288-1.html 1179288-1-ref.html == 1190635-1.html 1190635-1-ref.html +== 1209603-1.html 1209603-1-ref.html diff --git a/layout/reftests/text/reftest.list b/layout/reftests/text/reftest.list index eb83a772e9..9787587b57 100644 --- a/layout/reftests/text/reftest.list +++ b/layout/reftests/text/reftest.list @@ -310,6 +310,10 @@ pref(layout.css.text-align-unsafe-value.enabled,true) == text-align-unsafe.html != control-chars-02.html control-chars-02-notref.html == control-chars-03a.html control-chars-03-ref.html == control-chars-03b.html control-chars-03-ref.html +pref(layout.css.control-characters.visible,true) != control-chars-04a.html control-chars-04-notref.html +pref(layout.css.control-characters.visible,true) != control-chars-04b.html control-chars-04-notref.html +pref(layout.css.control-characters.visible,true) != control-chars-04c.html control-chars-04-notref.html +pref(layout.css.control-characters.visible,true) != control-chars-04d.html control-chars-04-notref.html # font fallback for when not supported in the primary font family - bug 970891 HTTP(..) == space-font-1.html space-font-1-ref.html diff --git a/layout/style/AnimationCommon.cpp b/layout/style/AnimationCommon.cpp index 1105623b6a..789e58ab38 100644 --- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -362,7 +362,7 @@ CommonAnimationManager::GetAnimations(dom::Element *aElement, } void -CommonAnimationManager::FlushAnimations(FlushFlags aFlags) +CommonAnimationManager::FlushAnimations() { TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh(); for (AnimationCollection* collection = mElementCollections.getFirst(); @@ -371,15 +371,13 @@ CommonAnimationManager::FlushAnimations(FlushFlags aFlags) continue; } - if (aFlags == Cannot_Throttle) { - collection->RequestRestyle(AnimationCollection::RestyleType::Standard); - } + MOZ_ASSERT(collection->mElement->GetComposedDoc() == + mPresContext->Document(), + "Should not have a transition/animation collection for an " + "element that is not part of the document tree"); - nsAutoAnimationMutationBatch mb(collection->mElement->OwnerDoc()); - collection->Tick(); + collection->RequestRestyle(AnimationCollection::RestyleType::Standard); } - - MaybeStartOrStopObservingRefreshDriver(); } nsIStyleRule* @@ -911,10 +909,6 @@ AnimationCollection::RequestRestyle(RestyleType aRestyleType) return; } - MOZ_ASSERT(mElement->GetCrossShadowCurrentDoc() == presContext->Document(), - "Element::UnbindFromTree should have destroyed the element " - "transition/animations object"); - // Steps for Restyle::Layer: if (aRestyleType == RestyleType::Layer) { @@ -995,7 +989,7 @@ AnimationCollection::HasCurrentAnimationsForProperties( const Animation& anim = *mAnimations[animIdx]; const KeyframeEffectReadOnly* effect = anim.GetEffect(); if (effect && - effect->IsCurrent(anim) && + effect->IsCurrent() && effect->HasAnimationOfProperties(aProperties, aPropertyCount)) { return true; } diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h index d183d37847..f7544fc1a8 100644 --- a/layout/style/AnimationCommon.h +++ b/layout/style/AnimationCommon.h @@ -6,12 +6,14 @@ #ifndef mozilla_css_AnimationCommon_h #define mozilla_css_AnimationCommon_h +#include // For #include "nsIStyleRuleProcessor.h" #include "nsIStyleRule.h" #include "nsRefreshDriver.h" #include "nsChangeHint.h" #include "nsCSSProperty.h" #include "nsDisplayList.h" // For nsDisplayItem::Type +#include "mozilla/AnimationComparator.h" #include "mozilla/EventDispatcher.h" #include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" @@ -96,11 +98,9 @@ public: return false; } - enum FlushFlags { - Can_Throttle, - Cannot_Throttle - }; - void FlushAnimations(FlushFlags aFlags); + // Requests a standard restyle on each managed AnimationCollection that has + // an out-of-date mStyleRuleRefreshTime. + void FlushAnimations(); nsIStyleRule* GetAnimationRule(dom::Element* aElement, nsCSSPseudoElements::Type aPseudoType); @@ -507,23 +507,49 @@ template class DelayedEventDispatcher { public: + DelayedEventDispatcher() : mIsSorted(true) { } + void QueueEvent(EventInfo&& aEventInfo) { - mPendingEvents.AppendElement(mozilla::Forward(aEventInfo)); + mPendingEvents.AppendElement(Forward(aEventInfo)); + mIsSorted = false; + } + + // This is exposed as a separate method so that when we are dispatching + // *both* transition events and animation events we can sort both lists + // once using the current state of the document before beginning any + // dispatch. + void SortEvents() + { + if (mIsSorted) { + return; + } + + // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is + // fixed. + std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(), + EventInfoLessThan()); + mIsSorted = true; } // Takes a reference to the owning manager's pres context so it can // detect if the pres context is destroyed while dispatching one of // the events. + // + // This will call SortEvents automatically if it has not already been + // called. void DispatchEvents(nsPresContext* const & aPresContext) { if (!aPresContext || mPendingEvents.IsEmpty()) { return; } + SortEvents(); + EventArray events; mPendingEvents.SwapElements(events); - // FIXME: Sort events here in timeline order, then document order + // mIsSorted will be set to true by SortEvents above, and we leave it + // that way since mPendingEvents is now empty for (EventInfo& info : events) { EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent); @@ -533,7 +559,11 @@ public: } } - void ClearEventQueue() { mPendingEvents.Clear(); } + void ClearEventQueue() + { + mPendingEvents.Clear(); + mIsSorted = true; + } bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); } // Methods for supporting cycle-collection @@ -542,14 +572,34 @@ public: { for (EventInfo& info : mPendingEvents) { ImplCycleCollectionTraverse(*aCallback, info.mElement, aName); + ImplCycleCollectionTraverse(*aCallback, info.mAnimation, aName); } } - void Unlink() { mPendingEvents.Clear(); } + void Unlink() { ClearEventQueue(); } protected: - typedef nsTArray EventArray; + class EventInfoLessThan + { + public: + bool operator()(const EventInfo& a, const EventInfo& b) const + { + if (a.mTimeStamp != b.mTimeStamp) { + // Null timestamps sort first + if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) { + return a.mTimeStamp.IsNull(); + } else { + return a.mTimeStamp < b.mTimeStamp; + } + } + AnimationPtrComparator> comparator; + return comparator.LessThan(a.mAnimation, b.mAnimation); + } + }; + + typedef nsTArray EventArray; EventArray mPendingEvents; + bool mIsSorted; }; template diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp index e213997ef2..b95a4c5b4f 100644 --- a/layout/style/CSSStyleSheet.cpp +++ b/layout/style/CSSStyleSheet.cpp @@ -47,6 +47,7 @@ #include "nsComponentManagerUtils.h" #include "nsNullPrincipal.h" #include "mozilla/RuleProcessorCache.h" +#include "nsIStyleSheetLinkingElement.h" using namespace mozilla; using namespace mozilla::dom; @@ -2267,7 +2268,7 @@ CSSStyleSheet::StyleSheetLoaded(CSSStyleSheet* aSheet, } nsresult -CSSStyleSheet::ParseSheet(const nsAString& aInput) +CSSStyleSheet::ReparseSheet(const nsAString& aInput) { // Not doing this if the sheet is not complete! if (!mInner->mComplete) { @@ -2289,21 +2290,37 @@ CSSStyleSheet::ParseSheet(const nsAString& aInput) WillDirty(); // detach existing rules (including child sheets via import rules) + css::LoaderReusableStyleSheets reusableSheets; int ruleCount; while ((ruleCount = mInner->mOrderedRules.Count()) != 0) { RefPtr rule = mInner->mOrderedRules.ObjectAt(ruleCount - 1); mInner->mOrderedRules.RemoveObjectAt(ruleCount - 1); rule->SetStyleSheet(nullptr); + if (rule->GetType() == css::Rule::IMPORT_RULE) { + nsCOMPtr importRule(do_QueryInterface(rule)); + NS_ASSERTION(importRule, "GetType lied"); + + nsCOMPtr childSheet; + importRule->GetStyleSheet(getter_AddRefs(childSheet)); + + RefPtr cssSheet = do_QueryObject(childSheet); + if (cssSheet && cssSheet->GetOriginalURI()) { + reusableSheets.AddReusableSheet(cssSheet); + } + } if (mDocument) { mDocument->StyleRuleRemoved(this, rule); } } // nuke child sheets list and current namespace map - for (CSSStyleSheet* child = mInner->mFirstChild; child; child = child->mNext) { + for (CSSStyleSheet* child = mInner->mFirstChild; child; ) { NS_ASSERTION(child->mParent == this, "Child sheet is not parented to this!"); + CSSStyleSheet* next = child->mNext; child->mParent = nullptr; child->mDocument = nullptr; + child->mNext = nullptr; + child = next; } mInner->mFirstChild = nullptr; mInner->mNameSpaceMap = nullptr; @@ -2311,9 +2328,18 @@ CSSStyleSheet::ParseSheet(const nsAString& aInput) // allow unsafe rules if the style sheet's principal is the system principal bool allowUnsafeRules = nsContentUtils::IsSystemPrincipal(mInner->mPrincipal); + uint32_t lineNumber = 1; + if (mOwningNode) { + nsCOMPtr link = do_QueryInterface(mOwningNode); + if (link) { + lineNumber = link->GetLineNumber(); + } + } + nsCSSParser parser(loader, this); nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI, - mInner->mPrincipal, 1, allowUnsafeRules); + mInner->mPrincipal, lineNumber, + allowUnsafeRules, &reusableSheets); DidDirty(); // we are always 'dirty' here since we always remove rules first NS_ENSURE_SUCCESS(rv, rv); diff --git a/layout/style/CSSStyleSheet.h b/layout/style/CSSStyleSheet.h index 7e1ba545c2..4bccac1f2d 100644 --- a/layout/style/CSSStyleSheet.h +++ b/layout/style/CSSStyleSheet.h @@ -242,7 +242,7 @@ public: bool UseForPresentation(nsPresContext* aPresContext, nsMediaQueryResultCacheKey& aKey) const; - nsresult ParseSheet(const nsAString& aInput); + nsresult ReparseSheet(const nsAString& aInput); void SetInRuleProcessorCache() { mInRuleProcessorCache = true; } diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 5b7cbfbcbb..153a60ee72 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -22,7 +22,6 @@ namespace css { Declaration::Declaration() : mImmutable(false) { - MOZ_COUNT_CTOR(mozilla::css::Declaration); } Declaration::Declaration(const Declaration& aCopy) @@ -39,12 +38,10 @@ Declaration::Declaration(const Declaration& aCopy) nullptr), mImmutable(false) { - MOZ_COUNT_CTOR(mozilla::css::Declaration); } Declaration::~Declaration() { - MOZ_COUNT_DTOR(mozilla::css::Declaration); } void @@ -1250,7 +1247,7 @@ Declaration::ToString(nsAString& aString) const continue; } - if (!nsCSSProps::IsEnabled(property)) { + if (!nsCSSProps::IsEnabled(property, nsCSSProps::eEnabledForAllContent)) { continue; } bool doneProperty = false; @@ -1379,15 +1376,17 @@ Declaration::InitializeEmpty() mData = nsCSSCompressedDataBlock::CreateEmptyBlock(); } -Declaration* +already_AddRefed Declaration::EnsureMutable() { MOZ_ASSERT(mData, "should only be called when not expanded"); + RefPtr result; if (!IsMutable()) { - return new Declaration(*this); + result = new Declaration(*this); } else { - return this; + result = this; } + return result.forget(); } size_t diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index 0f7ae3f2a9..d90e52d2fa 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -49,8 +49,12 @@ public: Declaration(const Declaration& aCopy); + NS_INLINE_DECL_REFCOUNTING(Declaration) + +private: ~Declaration(); +public: /** * |ValueAppended| must be called to maintain this declaration's * |mOrder| whenever a property is parsed into an expanded data block @@ -243,7 +247,7 @@ public: /** * Copy |this|, if necessary to ensure that it can be modified. */ - Declaration* EnsureMutable(); + already_AddRefed EnsureMutable(); /** * Crash if |this| cannot be modified. diff --git a/layout/style/FontFaceSet.cpp b/layout/style/FontFaceSet.cpp index c80410b640..5b4adacd19 100644 --- a/layout/style/FontFaceSet.cpp +++ b/layout/style/FontFaceSet.cpp @@ -173,8 +173,8 @@ FontFaceSet::ParseFontShorthandForMatching( ErrorResult& aRv) { // Parse aFont as a 'font' property value. - Declaration declaration; - declaration.InitializeEmpty(); + RefPtr declaration = new Declaration; + declaration->InitializeEmpty(); bool changed = false; nsCSSParser parser; @@ -183,23 +183,23 @@ FontFaceSet::ParseFontShorthandForMatching( mDocument->GetDocumentURI(), mDocument->GetDocumentURI(), mDocument->NodePrincipal(), - &declaration, + declaration, &changed, /* aIsImportant */ false); // All of the properties we are interested in should have been set at once. - MOZ_ASSERT(changed == (declaration.HasProperty(eCSSProperty_font_family) && - declaration.HasProperty(eCSSProperty_font_style) && - declaration.HasProperty(eCSSProperty_font_weight) && - declaration.HasProperty(eCSSProperty_font_stretch))); + MOZ_ASSERT(changed == (declaration->HasProperty(eCSSProperty_font_family) && + declaration->HasProperty(eCSSProperty_font_style) && + declaration->HasProperty(eCSSProperty_font_weight) && + declaration->HasProperty(eCSSProperty_font_stretch))); if (!changed) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return; } - nsCSSCompressedDataBlock* data = declaration.GetNormalBlock(); - MOZ_ASSERT(!declaration.GetImportantBlock()); + nsCSSCompressedDataBlock* data = declaration->GetNormalBlock(); + MOZ_ASSERT(!declaration->GetImportantBlock()); const nsCSSValue* family = data->ValueFor(eCSSProperty_font_family); if (family->GetUnit() != eCSSUnit_FontFamilyList) { @@ -426,7 +426,7 @@ FontFaceSet::Add(FontFace& aFontFace, ErrorResult& aRv) FontFaceRecord* rec = mNonRuleFaces.AppendElement(); rec->mFontFace = &aFontFace; - rec->mSheetType = 0; // unused for mNonRuleFaces + rec->mSheetType = SheetType::Unknown; // unused for mNonRuleFaces rec->mLoadEventShouldFire = aFontFace.Status() == FontFaceLoadStatus::Unloaded || aFontFace.Status() == FontFaceLoadStatus::Loading; @@ -834,7 +834,7 @@ FontFaceSet::InsertNonRuleFontFace(FontFace* aFontFace, // InsertRuleFontFace does? RefPtr entry = FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace, - nsStyleSet::eDocSheet); + SheetType::Doc); if (!entry) { return; } @@ -846,7 +846,7 @@ FontFaceSet::InsertNonRuleFontFace(FontFace* aFontFace, } void -FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, uint8_t aSheetType, +FontFaceSet::InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType, nsTArray& aOldRecords, bool& aFontSetModified) { @@ -957,13 +957,13 @@ FontFaceSet::FindOrCreateUserFontEntryFromFontFace(FontFace* aFontFace) } return FindOrCreateUserFontEntryFromFontFace(fontfamily, aFontFace, - nsStyleSet::eDocSheet); + SheetType::Doc); } /* static */ already_AddRefed FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName, FontFace* aFontFace, - uint8_t aSheetType) + SheetType aSheetType) { FontFaceSet* set = aFontFace->GetPrimaryFontFaceSet(); @@ -1102,8 +1102,8 @@ FontFaceSet::FindOrCreateUserFontEntryFromFontFace(const nsAString& aFamilyName, // the same-site origin check and access control headers are // enforced against the sheet principal rather than the document // principal to allow user stylesheets to include @font-face rules - face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet || - aSheetType == nsStyleSet::eAgentSheet); + face->mUseOriginPrincipal = (aSheetType == SheetType::User || + aSheetType == SheetType::Agent); face->mLocalName.Truncate(); face->mFormatFlags = 0; diff --git a/layout/style/FontFaceSet.h b/layout/style/FontFaceSet.h index 1d6c5ac1e2..541bb6c7e8 100644 --- a/layout/style/FontFaceSet.h +++ b/layout/style/FontFaceSet.h @@ -226,7 +226,7 @@ private: // accordingly. struct FontFaceRecord { RefPtr mFontFace; - uint8_t mSheetType; // only relevant for mRuleFaces entries + SheetType mSheetType; // only relevant for mRuleFaces entries // When true, indicates that when finished loading, the FontFace should be // included in the subsequent loadingdone/loadingerror event fired at the @@ -237,7 +237,7 @@ private: static already_AddRefed FindOrCreateUserFontEntryFromFontFace( const nsAString& aFamilyName, FontFace* aFontFace, - uint8_t aSheetType); + SheetType aSheetType); // search for @font-face rule that matches a userfont font entry nsCSSFontFaceRule* FindRuleForUserFontEntry(gfxUserFontEntry* aUserFontEntry); @@ -258,7 +258,7 @@ private: nsresult aStatus); void RebuildUserFontSet(); - void InsertRuleFontFace(FontFace* aFontFace, uint8_t aSheetType, + void InsertRuleFontFace(FontFace* aFontFace, SheetType aSheetType, nsTArray& aOldRecords, bool& aFontSetModified); void InsertNonRuleFontFace(FontFace* aFontFace, bool& aFontSetModified); diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 24b253f779..2bf3a648a8 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -507,6 +507,30 @@ SheetLoadData::ScheduleLoadEventIfNeeded(nsresult aStatus) } } +/********************* + * Style sheet reuse * + *********************/ + +bool +LoaderReusableStyleSheets::FindReusableStyleSheet(nsIURI* aURL, + RefPtr& aResult) +{ + MOZ_ASSERT(aURL); + for (size_t i = mReusableSheets.Length(); i > 0; --i) { + size_t index = i - 1; + bool sameURI; + MOZ_ASSERT(mReusableSheets[index]->GetOriginalURI()); + nsresult rv = aURL->Equals(mReusableSheets[index]->GetOriginalURI(), + &sameURI); + if (!NS_FAILED(rv) && sameURI) { + aResult = mReusableSheets[index]; + mReusableSheets.RemoveElementAt(index); + return true; + } + } + return false; +} + /************************* * Loader Implementation * *************************/ @@ -2151,7 +2175,8 @@ nsresult Loader::LoadChildSheet(CSSStyleSheet* aParentSheet, nsIURI* aURL, nsMediaList* aMedia, - ImportRule* aParentRule) + ImportRule* aParentRule, + LoaderReusableStyleSheets* aReusableSheets) { LOG(("css::Loader::LoadChildSheet")); NS_PRECONDITION(aURL, "Must have a URI to load"); @@ -2220,18 +2245,23 @@ Loader::LoadChildSheet(CSSStyleSheet* aParentSheet, // Now that we know it's safe to load this (passes security check and not a // loop) do so. RefPtr sheet; - bool isAlternate; StyleSheetState state; - const nsSubstring& empty = EmptyString(); - // For now, use CORS_NONE for child sheets - rv = CreateSheet(aURL, nullptr, principal, CORS_NONE, - aParentSheet->GetReferrerPolicy(), - EmptyString(), // integrity is only checked on main sheet - parentData ? parentData->mSyncLoad : false, - false, empty, state, &isAlternate, getter_AddRefs(sheet)); - NS_ENSURE_SUCCESS(rv, rv); + if (aReusableSheets && aReusableSheets->FindReusableStyleSheet(aURL, sheet)) { + aParentRule->SetSheet(sheet); + state = eSheetComplete; + } else { + bool isAlternate; + const nsSubstring& empty = EmptyString(); + // For now, use CORS_NONE for child sheets + rv = CreateSheet(aURL, nullptr, principal, CORS_NONE, + aParentSheet->GetReferrerPolicy(), + EmptyString(), // integrity is only checked on main sheet + parentData ? parentData->mSyncLoad : false, + false, empty, state, &isAlternate, getter_AddRefs(sheet)); + NS_ENSURE_SUCCESS(rv, rv); - PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate); + PrepareSheet(sheet, empty, empty, aMedia, nullptr, isAlternate); + } rv = InsertChildSheet(sheet, aParentSheet, aParentRule); NS_ENSURE_SUCCESS(rv, rv); diff --git a/layout/style/Loader.h b/layout/style/Loader.h index 6c86097120..22a32f4bd2 100644 --- a/layout/style/Loader.h +++ b/layout/style/Loader.h @@ -20,6 +20,7 @@ #include "nsURIHashKey.h" #include "mozilla/Attributes.h" #include "mozilla/CORSMode.h" +#include "mozilla/CSSStyleSheet.h" #include "mozilla/MemoryReporting.h" #include "mozilla/net/ReferrerPolicy.h" @@ -30,7 +31,6 @@ class nsMediaList; class nsIStyleSheetLinkingElement; namespace mozilla { -class CSSStyleSheet; namespace dom { class Element; } // namespace dom @@ -131,6 +131,47 @@ namespace css { class SheetLoadData; class ImportRule; +/********************* + * Style sheet reuse * + *********************/ + +class MOZ_RAII LoaderReusableStyleSheets +{ +public: + LoaderReusableStyleSheets() + { + } + + /** + * Look for a reusable sheet (see AddReusableSheet) matching the + * given URL. If found, set aResult, remove the reused sheet from + * the internal list, and return true. If not found, return false; + * in this case, aResult is not modified. + * + * @param aURL the url to match + * @param aResult [out] the style sheet which can be reused + */ + bool FindReusableStyleSheet(nsIURI* aURL, RefPtr& aResult); + + /** + * Indicate that a certain style sheet is available for reuse if its + * URI matches the URI of an @import. Sheets should be added in the + * opposite order in which they are intended to be reused. + * + * @param aSheet the sheet which can be reused + */ + void AddReusableSheet(CSSStyleSheet* aSheet) { + mReusableSheets.AppendElement(aSheet); + } + +private: + LoaderReusableStyleSheets(const LoaderReusableStyleSheets&) = delete; + LoaderReusableStyleSheets& operator=(const LoaderReusableStyleSheets&) = delete; + + // The sheets that can be reused. + nsTArray> mReusableSheets; +}; + /*********************************************************************** * Enum that describes the state of the sheet returned by CreateSheet. * ***********************************************************************/ @@ -242,11 +283,14 @@ public: * @param aMedia the already-parsed media list for the child sheet * @param aRule the @import rule importing this child. This is used to * properly order the child sheet list of aParentSheet. + * @param aSavedSheets any saved style sheets which could be reused + * for this load */ nsresult LoadChildSheet(CSSStyleSheet* aParentSheet, nsIURI* aURL, nsMediaList* aMedia, - ImportRule* aRule); + ImportRule* aRule, + LoaderReusableStyleSheets* aSavedSheets); /** * Synchronously load and return the stylesheet at aURL. Any child sheets diff --git a/layout/style/RuleNodeCacheConditions.h b/layout/style/RuleNodeCacheConditions.h index e34b6a8381..044434e29d 100644 --- a/layout/style/RuleNodeCacheConditions.h +++ b/layout/style/RuleNodeCacheConditions.h @@ -20,6 +20,22 @@ class nsStyleContext; namespace mozilla { +/** + * nsRuleNodeCacheConditions is used to store information about whether + * we can store a style struct that we're computing in the rule tree. + * + * For inherited structs (i.e., structs with inherited properties), we + * cache the struct in the rule tree if it does not depend on any data + * in the style context tree, and otherwise store it in the style + * context tree. This means that for inherited structs, setting any + * conditions is equivalent to making the struct uncacheable. + * + * For reset structs (i.e., structs with non-inherited properties), we + * are also able to cache structs in the rule tree conditionally on + * certain common conditions. For these structs, setting conditions + * (SetFontSizeDependency, SetWritingModeDependency) instead causes the + * struct to be stored, with the condition, in the rule tree. + */ class RuleNodeCacheConditions { public: @@ -45,6 +61,15 @@ public: bool Matches(nsStyleContext* aStyleContext) const; + /** + * Record that the data being computed depend on the font-size + * property of the element for which they are being computed. + * + * Note that we sometimes actually call this when there is a + * dependency on the font-size property of the parent element, but we + * only do so while computing inherited structs (nsStyleFont), and we + * only store reset structs conditionally. + */ void SetFontSizeDependency(nscoord aCoord) { MOZ_ASSERT(!(mBits & eHaveFontSize) || mFontSize == aCoord); @@ -52,6 +77,12 @@ public: mBits |= eHaveFontSize; } + /** + * Record that the data being computed depend on the writing mode of + * the element for which they are being computed, which in turn + * depends on its 'writing-mode', 'direction', and 'text-orientation' + * properties. + */ void SetWritingModeDependency(uint8_t aWritingMode) { MOZ_ASSERT(!(mBits & eHaveWritingMode) || GetWritingMode() == aWritingMode); diff --git a/layout/style/SheetType.h b/layout/style/SheetType.h new file mode 100644 index 0000000000..8554116247 --- /dev/null +++ b/layout/style/SheetType.h @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +/* enum to represent a level in the cascade */ + +#ifndef mozilla_SheetType_h +#define mozilla_SheetType_h + +namespace mozilla { + +// The "origins" of the CSS cascade, from lowest precedence to +// highest (for non-!important rules). +// +// Be sure to update NS_RULE_NODE_LEVEL_MASK when changing the number +// of sheet types; static assertions enforce this. +enum class SheetType : uint8_t { + Agent, // CSS + User, // CSS + PresHint, + SVGAttrAnimation, + Doc, // CSS + ScopedDoc, + StyleAttr, + Override, // CSS + Animation, + Transition, + + Count, + Unknown = 0xff +}; + +} // namespace mozilla + +#endif // mozilla_SheetType_h diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index 4d4bda7ab1..dcf04abd3d 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -2491,7 +2491,7 @@ BuildStyleRule(nsCSSProperty aProperty, bool aUseSVGMode) { // Set up an empty CSS Declaration - nsAutoPtr declaration(new css::Declaration()); + RefPtr declaration(new css::Declaration()); declaration->InitializeEmpty(); bool changed; // ignored, but needed as outparam for ParseProperty @@ -2510,12 +2510,11 @@ BuildStyleRule(nsCSSProperty aProperty, // check whether property parsed without CSS parsing errors if (!declaration->HasNonImportantValueFor(propertyToCheck)) { - NS_WARNING("failure in BuildStyleRule"); return nullptr; } RefPtr rule = new css::StyleRule(nullptr, - declaration.forget(), + declaration, 0, 0); return rule.forget(); } @@ -2532,7 +2531,7 @@ LookupStyleContext(dom::Element* aElement) return nsComputedDOMStyle::GetStyleContextForElement(aElement, nullptr, shell); } -bool +/* static */ bool StyleAnimationValue::ComputeValue(nsCSSProperty aProperty, dom::Element* aTargetElement, const nsAString& aSpecifiedValue, @@ -2564,6 +2563,59 @@ StyleAnimationValue::ComputeValue(nsCSSProperty aProperty, return true; } + nsAutoTArray values; + bool ok = ComputeValues(aProperty, nsCSSProps::eIgnoreEnabledState, + aTargetElement, styleRule, values, + aIsContextSensitive); + if (!ok) { + return false; + } + + MOZ_ASSERT(values.Length() == 1); + MOZ_ASSERT(values[0].mProperty == aProperty); + + aComputedValue = values[0].mValue; + return true; +} + +/* static */ bool +StyleAnimationValue::ComputeValues(nsCSSProperty aProperty, + nsCSSProps::EnabledState aEnabledState, + dom::Element* aTargetElement, + const nsAString& aSpecifiedValue, + bool aUseSVGMode, + nsTArray& aResult) +{ + MOZ_ASSERT(aTargetElement, "null target element"); + MOZ_ASSERT(aTargetElement->GetCurrentDoc(), + "we should only be able to actively animate nodes that " + "are in a document"); + + // Parse specified value into a temporary css::StyleRule + RefPtr styleRule = + BuildStyleRule(aProperty, aTargetElement, aSpecifiedValue, aUseSVGMode); + if (!styleRule) { + return false; + } + + aResult.Clear(); + return ComputeValues(aProperty, aEnabledState, aTargetElement, styleRule, + aResult, /* aIsContextSensitive */ nullptr); +} + +/* static */ bool +StyleAnimationValue::ComputeValues( + nsCSSProperty aProperty, + nsCSSProps::EnabledState aEnabledState, + dom::Element* aTargetElement, + css::StyleRule* aStyleRule, + nsTArray& aValues, + bool* aIsContextSensitive) +{ + if (!nsCSSProps::IsEnabled(aProperty, aEnabledState)) { + return false; + } + // Look up style context for our target element RefPtr styleContext = LookupStyleContext(aTargetElement); if (!styleContext) { @@ -2573,10 +2625,14 @@ StyleAnimationValue::ComputeValue(nsCSSProperty aProperty, RefPtr tmpStyleContext; if (aIsContextSensitive) { + MOZ_ASSERT(!nsCSSProps::IsShorthand(aProperty), + "to correctly set aIsContextSensitive for shorthand properties, " + "this code must be adjusted"); + nsCOMArray ruleArray; ruleArray.AppendObject(styleSet->InitialStyleRule()); - ruleArray.AppendObject(styleRule); - styleRule->RuleMatched(); + ruleArray.AppendObject(aStyleRule); + aStyleRule->RuleMatched(); tmpStyleContext = styleSet->ResolveStyleByAddingRules(styleContext, ruleArray); if (!tmpStyleContext) { @@ -2587,8 +2643,8 @@ StyleAnimationValue::ComputeValue(nsCSSProperty aProperty, nsStyleStructID sid = nsCSSProps::kSIDTable[aProperty]; tmpStyleContext->StyleData(sid); - // If the rule node will have cached style data if the value is not - // context-sensitive. So if there's nothing cached, it's not context + // The rule node will have unconditional cached style data if the value is + // not context-sensitive. So if there's nothing cached, it's not context // sensitive. *aIsContextSensitive = !tmpStyleContext->RuleNode()->NodeHasCachedUnconditionalData(sid); @@ -2602,8 +2658,8 @@ StyleAnimationValue::ComputeValue(nsCSSProperty aProperty, // value may have been biased by the 'initial' values supplied. if (!aIsContextSensitive || *aIsContextSensitive) { nsCOMArray ruleArray; - ruleArray.AppendObject(styleRule); - styleRule->RuleMatched(); + ruleArray.AppendObject(aStyleRule); + aStyleRule->RuleMatched(); tmpStyleContext = styleSet->ResolveStyleByAddingRules(styleContext, ruleArray); if (!tmpStyleContext) { @@ -2611,8 +2667,27 @@ StyleAnimationValue::ComputeValue(nsCSSProperty aProperty, } } - // Extract computed value of our property from the temporary style rule - return ExtractComputedValue(aProperty, tmpStyleContext, aComputedValue); + // Extract computed value of our property (or all longhand components, if + // aProperty is a shorthand) from the temporary style rule + if (nsCSSProps::IsShorthand(aProperty)) { + CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(p, aProperty, aEnabledState) { + if (nsCSSProps::kAnimTypeTable[*p] == eStyleAnimType_None) { + // Skip non-animatable component longhands. + continue; + } + PropertyStyleAnimationValuePair* pair = aValues.AppendElement(); + pair->mProperty = *p; + if (!ExtractComputedValue(*p, tmpStyleContext, + pair->mValue)) { + return false; + } + } + return true; + } else { + PropertyStyleAnimationValuePair* pair = aValues.AppendElement(); + pair->mProperty = aProperty; + return ExtractComputedValue(aProperty, tmpStyleContext, pair->mValue); + } } bool diff --git a/layout/style/StyleAnimationValue.h b/layout/style/StyleAnimationValue.h index e2984109b4..46152f52f9 100644 --- a/layout/style/StyleAnimationValue.h +++ b/layout/style/StyleAnimationValue.h @@ -10,9 +10,9 @@ #include "nsStringFwd.h" #include "nsStringBuffer.h" -#include "nsCSSProperty.h" #include "nsCoord.h" #include "nsColor.h" +#include "nsCSSProps.h" #include "nsCSSValue.h" class nsIFrame; @@ -21,6 +21,10 @@ class gfx3DMatrix; namespace mozilla { +namespace css { +class StyleRule; +} // namespace css + namespace dom { class Element; } // namespace dom @@ -29,6 +33,8 @@ namespace gfx { class Matrix4x4; } // namespace gfx +struct PropertyStyleAnimationValuePair; + /** * Utility class to handle animated style values */ @@ -153,6 +159,23 @@ public: StyleAnimationValue& aComputedValue, bool* aIsContextSensitive = nullptr); + /** + * Like ComputeValue, but returns an array of StyleAnimationValues. + * + * On success, when aProperty is a longhand, aResult will have a single + * value in it. When aProperty is a shorthand, aResult will be filled with + * values for all of aProperty's longhand components. aEnabledState + * is used to filter the longhand components that will be appended + * to aResult. On failure, aResult might still have partial results + * in it. + */ + static bool ComputeValues(nsCSSProperty aProperty, + nsCSSProps::EnabledState aEnabledState, + mozilla::dom::Element* aTargetElement, + const nsAString& aSpecifiedValue, + bool aUseSVGMode, + nsTArray& aResult); + /** * Creates a specified value for the given computed value. * @@ -373,6 +396,13 @@ public: { return !(*this == aOther); } private: + static bool ComputeValues(nsCSSProperty aProperty, + nsCSSProps::EnabledState aEnabledState, + mozilla::dom::Element* aTargetElement, + mozilla::css::StyleRule* aStyleRule, + nsTArray& aValues, + bool* aIsContextSensitive); + void FreeValue(); static const char16_t* GetBufferValue(nsStringBuffer* aBuffer) { @@ -412,6 +442,12 @@ private: } }; +struct PropertyStyleAnimationValuePair +{ + nsCSSProperty mProperty; + StyleAnimationValue mValue; +}; + } // namespace mozilla #endif diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index e34a897c13..973ceba64e 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -1448,7 +1448,6 @@ StyleRule::StyleRule(StyleRule& aCopy, StyleRule::~StyleRule() { delete mSelector; - delete mDeclaration; if (mDOMRule) { mDOMRule->DOMDeclaration()->DropReference(); } diff --git a/layout/style/StyleRule.h b/layout/style/StyleRule.h index 6fdffb0775..5ba5c9a201 100644 --- a/layout/style/StyleRule.h +++ b/layout/style/StyleRule.h @@ -307,10 +307,7 @@ public: protected: virtual ~ImportantRule(); - // Not an owning reference; the StyleRule that owns this - // ImportantRule also owns the mDeclaration, and any rule node - // pointing to this rule keeps that StyleRule alive as well. - Declaration* mDeclaration; + RefPtr mDeclaration; friend class StyleRule; }; @@ -385,7 +382,7 @@ private: private: nsCSSSelectorList* mSelector; // null for style attribute - Declaration* mDeclaration; + RefPtr mDeclaration; RefPtr mImportantRule; // initialized by RuleMatched RefPtr mDOMRule; diff --git a/layout/style/crashtests/1200568-1.html b/layout/style/crashtests/1200568-1.html new file mode 100644 index 0000000000..e2dc9c09df --- /dev/null +++ b/layout/style/crashtests/1200568-1.html @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list index 6e2be3ba3e..0e729eda87 100644 --- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -114,5 +114,6 @@ pref(dom.webcomponents.enabled,true) load 1089463-1.html load 1136010-1.html load 1153693-1.html load 1167782-1.html +load 1200568-1.html load large_border_image_width.html load border-image-visited-link.html diff --git a/layout/style/full-screen-override.css b/layout/style/full-screen-override.css deleted file mode 100644 index 2358f35bb4..0000000000 --- a/layout/style/full-screen-override.css +++ /dev/null @@ -1,26 +0,0 @@ -/* 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/. */ - - -*|*:-moz-full-screen-ancestor { - /* Ancestors of a full-screen element should not induce stacking contexts - that would prevent the full-screen element from being on top. */ - z-index: initial !important; - /* Ancestors of a full-screen element should not be partially transparent, - since that would apply to the full-screen element and make the page visible - behind it. It would also create a pseudo-stacking-context that would let content - draw on top of the full-screen element. */ - opacity: initial !important; - /* Ancestors of a full-screen element should not apply SVG masking, clipping, or - filtering, since that would affect the full-screen element and create a pseudo- - stacking context. */ - mask: initial !important; - clip-path: initial !important; - filter: initial !important; - clip: initial !important; - transform: initial !important; - transform-style: initial !important; - /* FIXME: do we need to worry about 'overflow'? */ - will-change: initial !important; -} diff --git a/layout/style/html.css b/layout/style/html.css index 9c0819f0b1..931ba6a6eb 100644 --- a/layout/style/html.css +++ b/layout/style/html.css @@ -831,7 +831,12 @@ marquee[direction="up"], marquee[direction="down"] { white-space: nowrap; font-size: 50%; line-height: 1; +%ifndef XP_WIN + /* The widely-used Windows font Meiryo doesn't work fine with this + * setting, so disable this on Windows. We should re-enable it once + * Microsoft fixes this issue. See bug 1164279. */ font-variant-east-asian: ruby; +%endif } @supports (text-emphasis: none) { rtc, rt { diff --git a/layout/style/jar.mn b/layout/style/jar.mn index 531d0375f0..7bc7b4b8aa 100644 --- a/layout/style/jar.mn +++ b/layout/style/jar.mn @@ -4,9 +4,8 @@ toolkit.jar: * res/ua.css (ua.css) - res/html.css (html.css) +* res/html.css (html.css) res/quirk.css (quirk.css) - res/full-screen-override.css (full-screen-override.css) res/plaintext.css (plaintext.css) res/viewsource.css (viewsource.css) res/counterstyles.css (counterstyles.css) diff --git a/layout/style/moz.build b/layout/style/moz.build index 7702dd4903..2833d04701 100644 --- a/layout/style/moz.build +++ b/layout/style/moz.build @@ -88,6 +88,7 @@ EXPORTS.mozilla += [ 'LayerAnimationInfo.h', 'RuleNodeCacheConditions.h', 'RuleProcessorCache.h', + 'SheetType.h', 'StyleAnimationValue.h', ] diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index 8fff0e4c34..d76e5d3b83 100644 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -244,11 +244,13 @@ CSSAnimation::QueueEvents() // First notifying for start of 0th iteration by appending an // 'animationstart': StickyTimeDuration elapsedTime = - std::min(StickyTimeDuration(mEffect->InitialAdvance()), + std::min(StickyTimeDuration(InitialAdvance()), computedTiming.mActiveDuration); - manager->QueueEvent( - AnimationEventInfo(owningElement, mAnimationName, eAnimationStart, - elapsedTime, owningPseudoType)); + manager->QueueEvent(AnimationEventInfo(owningElement, owningPseudoType, + eAnimationStart, mAnimationName, + elapsedTime, + ElapsedTimeToTimeStamp(elapsedTime), + this)); // Then have the shared code below append an 'animationend': message = eAnimationEnd; } else { @@ -261,15 +263,16 @@ CSSAnimation::QueueEvents() TimeDuration iterationStart = mEffect->Timing().mIterationDuration * computedTiming.mCurrentIteration; elapsedTime = StickyTimeDuration(std::max(iterationStart, - mEffect->InitialAdvance())); + InitialAdvance())); } else { MOZ_ASSERT(message == eAnimationEnd); elapsedTime = computedTiming.mActiveDuration; } - manager->QueueEvent( - AnimationEventInfo(owningElement, mAnimationName, message, elapsedTime, - owningPseudoType)); + manager->QueueEvent(AnimationEventInfo(owningElement, owningPseudoType, + message, mAnimationName, elapsedTime, + ElapsedTimeToTimeStamp(elapsedTime), + this)); } bool @@ -310,6 +313,30 @@ CSSAnimation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag) Animation::UpdateTiming(aSeekFlag, aSyncNotifyFlag); } +TimeStamp +CSSAnimation::ElapsedTimeToTimeStamp(const StickyTimeDuration& + aElapsedTime) const +{ + // Initializes to null. We always return this object to benefit from + // return-value-optimization. + TimeStamp result; + + // Currently we may dispatch animationstart events before resolving + // mStartTime if we have a delay <= 0. This will change in bug 1134163 + // but until then we should just use the latest refresh driver time as + // the event timestamp in that case. + if (!mEffect || mStartTime.IsNull()) { + nsPresContext* presContext = GetPresContext(); + if (presContext) { + result = presContext->RefreshDriver()->MostRecentRefresh(); + } + return result; + } + + result = AnimationTimeToTimeStamp(aElapsedTime + mEffect->Timing().mDelay); + return result; +} + ////////////////////////// nsAnimationManager //////////////////////////// NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher) @@ -366,8 +393,9 @@ nsIStyleRule* nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, mozilla::dom::Element* aElement) { - if (!mPresContext->IsDynamic()) { - // For print or print preview, ignore animations. + // Ignore animations for print or print preview, and for elements + // that are not attached to the document tree. + if (!mPresContext->IsDynamic() || !aElement->IsInComposedDoc()) { return nullptr; } @@ -465,7 +493,7 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, animationChanged = oldEffect->Timing() != newEffect->Timing() || oldEffect->Properties() != newEffect->Properties(); - oldEffect->SetTiming(newEffect->Timing(), *oldAnim); + oldEffect->SetTiming(newEffect->Timing()); oldEffect->Properties() = newEffect->Properties(); } diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h index 9c608bafd6..131d405394 100644 --- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -26,15 +26,22 @@ class Promise; } /* namespace dom */ struct AnimationEventInfo { - RefPtr mElement; - mozilla::InternalAnimationEvent mEvent; + RefPtr mElement; + RefPtr mAnimation; + InternalAnimationEvent mEvent; + TimeStamp mTimeStamp; - AnimationEventInfo(mozilla::dom::Element *aElement, - const nsSubstring& aAnimationName, + AnimationEventInfo(dom::Element* aElement, + nsCSSPseudoElements::Type aPseudoType, EventMessage aMessage, - const mozilla::StickyTimeDuration& aElapsedTime, - nsCSSPseudoElements::Type aPseudoType) - : mElement(aElement), mEvent(true, aMessage) + const nsSubstring& aAnimationName, + const StickyTimeDuration& aElapsedTime, + const TimeStamp& aTimeStamp, + dom::Animation* aAnimation) + : mElement(aElement) + , mAnimation(aAnimation) + , mEvent(true, aMessage) + , mTimeStamp(aTimeStamp) { // XXX Looks like nobody initialize WidgetEvent::time mEvent.animationName = aAnimationName; @@ -44,9 +51,11 @@ struct AnimationEventInfo { // InternalAnimationEvent doesn't support copy-construction, so we need // to ourselves in order to work with nsTArray - AnimationEventInfo(const AnimationEventInfo &aOther) + AnimationEventInfo(const AnimationEventInfo& aOther) : mElement(aOther.mElement) + , mAnimation(aOther.mAnimation) , mEvent(true, aOther.mEvent.mMessage) + , mTimeStamp(aOther.mTimeStamp) { mEvent.AssignAnimationEventData(aOther.mEvent, false); } @@ -165,6 +174,21 @@ protected: void UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag) override; + // Returns the duration from the start of the animation's source effect's + // active interval to the point where the animation actually begins playback. + // This is zero unless the animation's source effect has a negative delay in + // which // case it is the absolute value of that delay. + // This is used for setting the elapsedTime member of CSS AnimationEvents. + TimeDuration InitialAdvance() const { + return mEffect ? + std::max(TimeDuration(), mEffect->Timing().mDelay * -1) : + TimeDuration(); + } + // Converts an AnimationEvent's elapsedTime value to an equivalent TimeStamp + // that can be used to sort events by when they occurred. + TimeStamp ElapsedTimeToTimeStamp(const StickyTimeDuration& aElapsedTime) + const; + nsString mAnimationName; // The (pseudo-)element whose computed animation-name refers to this @@ -306,6 +330,7 @@ public: * contexts are created. */ void DispatchEvents() { mEventDispatcher.DispatchEvents(mPresContext); } + void SortEvents() { mEventDispatcher.SortEvents(); } void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } // Stop animations on the element. This method takes the real element diff --git a/layout/style/nsCSSDataBlock.cpp b/layout/style/nsCSSDataBlock.cpp index 9d63be15fc..8655d8f497 100644 --- a/layout/style/nsCSSDataBlock.cpp +++ b/layout/style/nsCSSDataBlock.cpp @@ -40,8 +40,8 @@ MoveValue(nsCSSValue* aSource, nsCSSValue* aDest) static bool ShouldIgnoreColors(nsRuleData *aRuleData) { - return aRuleData->mLevel != nsStyleSet::eAgentSheet && - aRuleData->mLevel != nsStyleSet::eUserSheet && + return aRuleData->mLevel != SheetType::Agent && + aRuleData->mLevel != SheetType::User && !aRuleData->mPresContext->UseDocumentColors(); } @@ -260,6 +260,15 @@ nsCSSCompressedDataBlock::MapRuleInfoInto(nsRuleData *aRuleData) const nsCSSValue* target = aRuleData->ValueFor(iProp); if (target->GetUnit() == eCSSUnit_Null) { const nsCSSValue *val = ValueAtIndex(i); + // In order for variable resolution to have the right information + // about the stylesheet level of a value, that level needs to be + // stored on the token stream. We can't do that at creation time + // because the CSS parser (which creates the object) has no idea + // about the stylesheet level, so we do it here instead, where + // the rule walking will have just updated aRuleData. + if (val->GetUnit() == eCSSUnit_TokenStream) { + val->GetTokenStreamValue()->mLevel = aRuleData->mLevel; + } MapSinglePropertyInto(iProp, val, target, aRuleData); } } diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 52c8cb38bd..b89f3b9787 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -57,11 +57,13 @@ typedef nsCSSProps::KTableValue KTableValue; // pref-backed bool values (hooked up in nsCSSParser::Startup) static bool sOpentypeSVGEnabled; +static bool sWebkitPrefixedAliasesEnabled; static bool sUnprefixingServiceEnabled; #ifdef NIGHTLY_BUILD static bool sUnprefixingServiceGloballyWhitelisted; #endif static bool sMozGradientsEnabled; +static bool sControlCharVisibility; const uint32_t nsCSSProps::kParserVariantTable[eCSSProperty_COUNT_no_shorthands] = { @@ -129,7 +131,8 @@ public: nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules); + bool aAllowUnsafeRules, + LoaderReusableStyleSheets* aReusableSheets); nsresult ParseStyleAttribute(const nsAString& aAttributeValue, nsIURI* aDocURL, @@ -159,6 +162,12 @@ public: bool* aChanged, bool aIsImportant, bool aIsSVGMode); + void ParseLonghandProperty(const nsCSSProperty aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue); void ParseMediaList(const nsSubstring& aBuffer, nsIURI* aURL, // for error reporting @@ -321,6 +330,9 @@ public: if (mUnsafeRulesEnabled) { enabledState |= nsCSSProps::eEnabledInUASheets; } + if (mIsChrome) { + enabledState |= nsCSSProps::eEnabledInChrome; + } return enabledState; } @@ -537,6 +549,15 @@ protected: bool GetToken(bool aSkipWS); void UngetToken(); bool GetNextTokenLocation(bool aSkipWS, uint32_t *linenum, uint32_t *colnum); + void AssertNextTokenAt(uint32_t aLine, uint32_t aCol) + { + // Beware that this method will call GetToken/UngetToken (in + // GetNextTokenLocation) in DEBUG builds, but not in non-DEBUG builds. + DebugOnly lineAfter, colAfter; + MOZ_ASSERT(GetNextTokenLocation(true, &lineAfter, &colAfter) && + lineAfter == aLine && colAfter == aCol, + "shouldn't have consumed any tokens"); + } bool ExpectSymbol(char16_t aSymbol, bool aSkipWS); bool ExpectEndProperty(); @@ -717,8 +738,9 @@ protected: eCSSContext_Page }; - css::Declaration* ParseDeclarationBlock(uint32_t aFlags, - nsCSSContextType aContext = eCSSContext_General); + already_AddRefed + ParseDeclarationBlock(uint32_t aFlags, + nsCSSContextType aContext = eCSSContext_General); bool ParseDeclaration(css::Declaration* aDeclaration, uint32_t aFlags, bool aMustCallValueAppended, @@ -732,23 +754,26 @@ protected: nsCSSKeyword LookupKeywordPrefixAware(nsAString& aKeywordStr, const KTableValue aKeywordTable[]); - bool ShouldUseUnprefixingService(); + bool ShouldUseUnprefixingService() const; bool ParsePropertyWithUnprefixingService(const nsAString& aPropertyName, css::Declaration* aDeclaration, uint32_t aFlags, bool aMustCallValueAppended, bool* aChanged, nsCSSContextType aContext); - // When we detect a webkit-prefixed gradient expression, this function can - // be used to parse its body into outparam |aValue|. Only call if - // ShouldUseUnprefixingService() returns true. - bool ParseWebkitPrefixedGradient(nsAString& aPrefixedFuncName, - nsCSSValue& aValue); + // When we detect a webkit-prefixed gradient expression, this function can be + // used to parse its body into outparam |aValue|, with the help of the + // CSSUnprefixingService. + // Only call if ShouldUseUnprefixingService() returns true. + bool ParseWebkitPrefixedGradientWithService(nsAString& aPrefixedFuncName, + nsCSSValue& aValue); bool ParseProperty(nsCSSProperty aPropID); bool ParsePropertyByFunction(nsCSSProperty aPropID); - bool ParseSingleValueProperty(nsCSSValue& aValue, - nsCSSProperty aPropID); + CSSParseResult ParseSingleValueProperty(nsCSSValue& aValue, + nsCSSProperty aPropID); + bool ParseSingleValuePropertyByFunction(nsCSSValue& aValue, + nsCSSProperty aPropID); // These are similar to ParseSingleValueProperty but only work for // properties that are parsed with ParseBoxProperties or @@ -799,6 +824,7 @@ protected: mPosition(aPosition), mSize(aSize) {}; }; + bool IsFunctionTokenValidForBackgroundImage(const nsCSSToken& aToken) const; bool ParseBackgroundItem(BackgroundParseState& aState); bool ParseValueList(nsCSSProperty aPropID); // a single value prop-id @@ -866,7 +892,7 @@ protected: CSSParseResult ParseGridLineNames(nsCSSValue& aValue); bool ParseGridLineNameListRepeat(nsCSSValueList** aTailPtr); bool ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue); - bool ParseGridTrackBreadth(nsCSSValue& aValue); + CSSParseResult ParseGridTrackBreadth(nsCSSValue& aValue); CSSParseResult ParseGridTrackSize(nsCSSValue& aValue); bool ParseGridAutoColumnsRows(nsCSSProperty aPropID); bool ParseGridTrackListRepeat(nsCSSValueList** aTailPtr); @@ -1047,7 +1073,7 @@ protected: int32_t ParseChoice(nsCSSValue aValues[], const nsCSSProperty aPropIDs[], int32_t aNumIDs); - bool ParseColor(nsCSSValue& aValue); + CSSParseResult ParseColor(nsCSSValue& aValue); bool ParseNumberColorComponent(uint8_t& aComponent, char aStop); bool ParsePercentageColorComponent(float& aComponent, char aStop); // ParseHSLColor parses everything starting with the opening '(' @@ -1060,23 +1086,21 @@ protected: bool ParseColorOpacity(float& aOpacity); bool ParseEnum(nsCSSValue& aValue, const KTableValue aKeywordTable[]); - bool ParseVariant(nsCSSValue& aValue, - int32_t aVariantMask, - const KTableValue aKeywordTable[]); - bool ParseVariantWithRestrictions(nsCSSValue& aValue, - int32_t aVariantMask, - const KTableValue aKeywordTable[], - uint32_t aRestrictions); - bool ParseNonNegativeVariant(nsCSSValue& aValue, - int32_t aVariantMask, - const KTableValue aKeywordTable[]); - bool ParseOneOrLargerVariant(nsCSSValue& aValue, - int32_t aVariantMask, - const KTableValue aKeywordTable[]); - bool ParseNonNegativeInteger(nsCSSValue& aValue) - { - return ParseNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr); - } + + // Variant parsing methods + CSSParseResult ParseVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableValue aKeywordTable[]); + CSSParseResult ParseVariantWithRestrictions(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableValue aKeywordTable[], + uint32_t aRestrictions); + CSSParseResult ParseNonNegativeVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableValue aKeywordTable[]); + CSSParseResult ParseOneOrLargerVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableValue aKeywordTable[]); // Variant parsing methods that are guaranteed to UngetToken any token // consumed on failure @@ -1086,10 +1110,59 @@ protected: { MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), "use ParseVariant for variants in VARIANT_MULTIPLE_TOKENS"); - CSSParseResult result = (CSSParseResult)ParseVariant(aValue, aVariantMask, aKeywordTable); + CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable); MOZ_ASSERT(result != CSSParseResult::Error); return result == CSSParseResult::Ok; } + bool ParseSingleTokenVariantWithRestrictions( + nsCSSValue& aValue, + int32_t aVariantMask, + const KTableValue aKeywordTable[], + uint32_t aRestrictions) + { + MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), + "use ParseVariantWithRestrictions for variants in " + "VARIANT_MULTIPLE_TOKENS"); + CSSParseResult result = + ParseVariantWithRestrictions(aValue, aVariantMask, aKeywordTable, + aRestrictions); + MOZ_ASSERT(result != CSSParseResult::Error); + return result == CSSParseResult::Ok; + } + bool ParseSingleTokenNonNegativeVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableValue aKeywordTable[]) + { + MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), + "use ParseNonNegativeVariant for variants in " + "VARIANT_MULTIPLE_TOKENS"); + CSSParseResult result = + ParseNonNegativeVariant(aValue, aVariantMask, aKeywordTable); + MOZ_ASSERT(result != CSSParseResult::Error); + return result == CSSParseResult::Ok; + } + bool ParseSingleTokenOneOrLargerVariant(nsCSSValue& aValue, + int32_t aVariantMask, + const KTableValue aKeywordTable[]) + { + MOZ_ASSERT(!(aVariantMask & VARIANT_MULTIPLE_TOKENS), + "use ParseOneOrLargerVariant for variants in " + "VARIANT_MULTIPLE_TOKENS"); + CSSParseResult result = + ParseOneOrLargerVariant(aValue, aVariantMask, aKeywordTable); + MOZ_ASSERT(result != CSSParseResult::Error); + return result == CSSParseResult::Ok; + } + + // Helpers for some common ParseSingleTokenNonNegativeVariant calls. + bool ParseNonNegativeInteger(nsCSSValue& aValue) + { + return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_INTEGER, nullptr); + } + bool ParseNonNegativeNumber(nsCSSValue& aValue) + { + return ParseSingleTokenNonNegativeVariant(aValue, VARIANT_NUMBER, nullptr); + } // http://dev.w3.org/csswg/css-values/#custom-idents // Parse an identifier that is none of: @@ -1118,10 +1191,17 @@ protected: bool ParseImageRect(nsCSSValue& aImage); bool ParseElement(nsCSSValue& aValue); bool ParseColorStop(nsCSSValueGradient* aGradient); - bool ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, - bool aIsLegacy); - bool ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, - bool aIsLegacy); + + enum GradientParsingFlags { + eGradient_Repeating = 1 << 0, // repeating-{linear|radial}-gradient + eGradient_MozLegacy = 1 << 1, // -moz-{linear|radial}-gradient + eGradient_WebkitLegacy = 1 << 2, // -webkit-{linear|radial}-gradient + + // Mask to catch both "legacy" flags: + eGradient_AnyLegacy = eGradient_MozLegacy | eGradient_WebkitLegacy + }; + bool ParseLinearGradient(nsCSSValue& aValue, uint8_t aFlags); + bool ParseRadialGradient(nsCSSValue& aValue, uint8_t aFlags); bool IsLegacyGradientLine(const nsCSSTokenType& aType, const nsString& aId); bool ParseGradientColorStops(nsCSSValueGradient* aGradient, @@ -1192,6 +1272,9 @@ protected: // Used for @import rules mozilla::css::Loader* mChildLoader; // not ref counted, it owns us + // Any sheets we may reuse when parsing an @import. + LoaderReusableStyleSheets* mReusableSheets; + // Sheet section we're in. This is used to enforce correct ordering of the // various rule types (eg the fact that a @charset rule must come before // anything else). Note that there are checks of similar things in various @@ -1222,6 +1305,9 @@ protected: // True if unsafe rules should be allowed bool mUnsafeRulesEnabled : 1; + // True if we are in parsing rules for the chrome. + bool mIsChrome : 1; + // True if viewport units should be allowed. bool mViewportUnitsEnabled : 1; @@ -1337,6 +1423,7 @@ CSSParserImpl::CSSParserImpl() mScanner(nullptr), mReporter(nullptr), mChildLoader(nullptr), + mReusableSheets(nullptr), mSection(eCSSSection_Charset), mNameSpaceMap(nullptr), mHavePushBack(false), @@ -1344,6 +1431,7 @@ CSSParserImpl::CSSParserImpl() mHashlessColorQuirk(false), mUnitlessLengthQuirk(false), mUnsafeRulesEnabled(false), + mIsChrome(false), mViewportUnitsEnabled(true), mHTMLMediaMode(false), mParsingCompoundProperty(false), @@ -1449,7 +1537,8 @@ CSSParserImpl::ParseSheet(const nsAString& aInput, nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules) + bool aAllowUnsafeRules, + LoaderReusableStyleSheets* aReusableSheets) { NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); NS_PRECONDITION(aBaseURI, "need base URI"); @@ -1495,6 +1584,8 @@ CSSParserImpl::ParseSheet(const nsAString& aInput, } mUnsafeRulesEnabled = aAllowUnsafeRules; + mIsChrome = nsContentUtils::IsSystemPrincipal(aSheetPrincipal); + mReusableSheets = aReusableSheets; nsCSSToken* tk = &mToken; for (;;) { @@ -1518,6 +1609,8 @@ CSSParserImpl::ParseSheet(const nsAString& aInput, ReleaseScanner(); mUnsafeRulesEnabled = false; + mIsChrome = false; + mReusableSheets = nullptr; // XXX check for low level errors return NS_OK; @@ -1555,7 +1648,7 @@ CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue, uint32_t parseFlags = eParseDeclaration_AllowImportant; - css::Declaration* declaration = ParseDeclarationBlock(parseFlags); + RefPtr declaration = ParseDeclarationBlock(parseFlags); if (declaration) { // Create a style rule for the declaration NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration, 0, 0)); @@ -1663,6 +1756,33 @@ CSSParserImpl::ParseRule(const nsAString& aRule, return rv; } +void +CSSParserImpl::ParseLonghandProperty(const nsCSSProperty aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aValue) +{ + MOZ_ASSERT(aPropID < eCSSProperty_COUNT_no_shorthands, + "ParseLonghandProperty must only take a longhand property"); + + RefPtr declaration = new Declaration; + declaration->InitializeEmpty(); + + bool changed; + ParseProperty(aPropID, aPropValue, aSheetURL, aBaseURL, aSheetPrincipal, + declaration, &changed, + /* aIsImportant */ false, + /* aIsSVGMode */ false); + + if (changed) { + aValue = *declaration->GetNormalBlock()->ValueFor(aPropID); + } else { + aValue.Reset(); + } +} + void CSSParserImpl::ParseProperty(const nsCSSProperty aPropID, const nsAString& aPropValue, @@ -1693,10 +1813,7 @@ CSSParserImpl::ParseProperty(const nsCSSProperty aPropID, // Check for unknown or preffed off properties if (eCSSProperty_UNKNOWN == aPropID || - !(nsCSSProps::IsEnabled(aPropID) || - (mUnsafeRulesEnabled && - nsCSSProps::PropHasFlags(aPropID, - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)))) { + !nsCSSProps::IsEnabled(aPropID, PropertyEnabledState())) { NS_ConvertASCIItoUTF16 propName(nsCSSProps::GetStringValue(aPropID)); REPORT_UNEXPECTED_P(PEUnknownProperty, propName); REPORT_UNEXPECTED(PEDeclDropped); @@ -1888,7 +2005,8 @@ CSSParserImpl::ParseSourceSizeList(const nsAString& aBuffer, query->SetNegated(); } - if (!ParseNonNegativeVariant(value, VARIANT_LPCALC, nullptr)) { + if (ParseNonNegativeVariant(value, VARIANT_LPCALC, nullptr) != + CSSParseResult::Ok) { hitError = true; break; } @@ -1937,7 +2055,8 @@ CSSParserImpl::ParseColorString(const nsSubstring& aBuffer, nsAutoSuppressErrors suppressErrors(this, aSuppressErrors); // Parse a color, and check that there's nothing else after it. - bool colorParsed = ParseColor(aValue) && !GetToken(true); + bool colorParsed = ParseColor(aValue) == CSSParseResult::Ok && + !GetToken(true); if (aSuppressErrors) { CLEAR_ERROR(); @@ -3000,7 +3119,8 @@ CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc, parseFunc = &CSSParserImpl::ParsePageRule; newSection = eCSSSection_General; - } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation) && + } else if ((nsCSSProps::IsEnabled(eCSSPropertyAlias_MozAnimation, + PropertyEnabledState()) && mToken.mIdent.LowerCaseEqualsLiteral("-moz-keyframes")) || mToken.mIdent.LowerCaseEqualsLiteral("keyframes")) { parseFunc = &CSSParserImpl::ParseKeyframesRule; @@ -3330,11 +3450,12 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) bool rv = false; switch (feature->mValueType) { case nsMediaFeature::eLength: - rv = ParseNonNegativeVariant(expr->mValue, VARIANT_LENGTH, nullptr); + rv = ParseSingleTokenNonNegativeVariant(expr->mValue, VARIANT_LENGTH, + nullptr); break; case nsMediaFeature::eInteger: case nsMediaFeature::eBoolInteger: - rv = ParseNonNegativeVariant(expr->mValue, VARIANT_INTEGER, nullptr); + rv = ParseNonNegativeInteger(expr->mValue); // Enforce extra restrictions for eBoolInteger if (rv && feature->mValueType == nsMediaFeature::eBoolInteger && @@ -3342,7 +3463,7 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) rv = false; break; case nsMediaFeature::eFloat: - rv = ParseNonNegativeVariant(expr->mValue, VARIANT_NUMBER, nullptr); + rv = ParseNonNegativeNumber(expr->mValue); break; case nsMediaFeature::eIntRatio: { @@ -3353,10 +3474,10 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) // We don't bother with ParseNonNegativeVariant since we have to // check for != 0 as well; no need to worry about the UngetToken // since we're throwing out up to the next ')' anyway. - rv = ParseVariant(a->Item(0), VARIANT_INTEGER, nullptr) && + rv = ParseSingleTokenVariant(a->Item(0), VARIANT_INTEGER, nullptr) && a->Item(0).GetIntValue() > 0 && ExpectSymbol('/', true) && - ParseVariant(a->Item(1), VARIANT_INTEGER, nullptr) && + ParseSingleTokenVariant(a->Item(1), VARIANT_INTEGER, nullptr) && a->Item(1).GetIntValue() > 0; } break; @@ -3383,11 +3504,11 @@ CSSParserImpl::ParseMediaQueryExpression(nsMediaQuery* aQuery) } break; case nsMediaFeature::eEnumerated: - rv = ParseVariant(expr->mValue, VARIANT_KEYWORD, - feature->mData.mKeywordTable); + rv = ParseSingleTokenVariant(expr->mValue, VARIANT_KEYWORD, + feature->mData.mKeywordTable); break; case nsMediaFeature::eIdent: - rv = ParseVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr); + rv = ParseSingleTokenVariant(expr->mValue, VARIANT_IDENTIFIER, nullptr); break; } if (!rv || !ExpectSymbol(')', true)) { @@ -3458,7 +3579,7 @@ CSSParserImpl::ProcessImport(const nsString& aURLSpec, } if (mChildLoader) { - mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule); + mChildLoader->LoadChildSheet(mSheet, url, aMedia, rule, mReusableSheets); } } @@ -3950,7 +4071,7 @@ CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule nsAutoTArray featureSelectors; nsCSSValue intValue; - while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) { + while (ParseNonNegativeInteger(intValue)) { featureSelectors.AppendElement(uint32_t(intValue.GetIntValue())); } @@ -4058,18 +4179,16 @@ CSSParserImpl::ParsePageRule(RuleAppendFunc aAppendFunc, void* aData) MOZ_ASSERT(mViewportUnitsEnabled, "Viewport units should be enabled outside of @page rules."); mViewportUnitsEnabled = false; - nsAutoPtr declaration( - ParseDeclarationBlock(parseFlags, - eCSSContext_Page)); + RefPtr declaration = + ParseDeclarationBlock(parseFlags, eCSSContext_Page); mViewportUnitsEnabled = true; if (!declaration) { return false; } - // Takes ownership of declaration. - RefPtr rule = new nsCSSPageRule(Move(declaration), - linenum, colnum); + RefPtr rule = + new nsCSSPageRule(declaration, linenum, colnum); (*aAppendFunc)(rule, aData); return true; @@ -4088,14 +4207,14 @@ CSSParserImpl::ParseKeyframeRule() // Ignore !important in keyframe rules uint32_t parseFlags = eParseDeclaration_InBraces; - nsAutoPtr declaration(ParseDeclarationBlock(parseFlags)); + RefPtr declaration(ParseDeclarationBlock(parseFlags)); if (!declaration) { return nullptr; } // Takes ownership of declaration, and steals contents of selectorList. RefPtr rule = - new nsCSSKeyframeRule(selectorList, Move(declaration), linenum, colnum); + new nsCSSKeyframeRule(selectorList, declaration, linenum, colnum); return rule.forget(); } @@ -4625,7 +4744,7 @@ CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, switch (system.GetIntValue()) { case NS_STYLE_COUNTER_SYSTEM_FIXED: { nsCSSValue start; - if (!ParseVariant(start, VARIANT_INTEGER, nullptr)) { + if (!ParseSingleTokenVariant(start, VARIANT_INTEGER, nullptr)) { start.SetIntValue(1, eCSSUnit_Integer); } aValue.SetPairValue(system, start); @@ -4648,10 +4767,10 @@ CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, case eCSSCounterDesc_Negative: { nsCSSValue first, second; - if (!ParseVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) { + if (!ParseSingleTokenVariant(first, VARIANT_COUNTER_SYMBOL, nullptr)) { return false; } - if (!ParseVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) { + if (!ParseSingleTokenVariant(second, VARIANT_COUNTER_SYMBOL, nullptr)) { aValue = first; } else { aValue.SetPairValue(first, second); @@ -4661,10 +4780,10 @@ CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, case eCSSCounterDesc_Prefix: case eCSSCounterDesc_Suffix: - return ParseVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr); + return ParseSingleTokenVariant(aValue, VARIANT_COUNTER_SYMBOL, nullptr); case eCSSCounterDesc_Range: { - if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) { return true; } nsCSSValuePairList* item = aValue.SetPairListValue(); @@ -4687,7 +4806,7 @@ CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, case eCSSCounterDesc_Pad: { nsCSSValue width, symbol; bool hasWidth = ParseNonNegativeInteger(width); - if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) || + if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) || (!hasWidth && !ParseNonNegativeInteger(width))) { return false; } @@ -4702,7 +4821,7 @@ CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, nsCSSValueList* item = nullptr; for (;;) { nsCSSValue value; - if (!ParseVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_COUNTER_SYMBOL, nullptr)) { return !!item; } if (!item) { @@ -4722,7 +4841,7 @@ CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, for (;;) { nsCSSValue weight, symbol; bool hasWeight = ParseNonNegativeInteger(weight); - if (!ParseVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) || + if (!ParseSingleTokenVariant(symbol, VARIANT_COUNTER_SYMBOL, nullptr) || (!hasWeight && !ParseNonNegativeInteger(weight))) { return false; } @@ -4747,8 +4866,8 @@ CSSParserImpl::ParseCounterDescriptorValue(nsCSSCounterDesc aDescID, } case eCSSCounterDesc_SpeakAs: - if (ParseVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD, - nsCSSProps::kCounterSpeakAsKTable)) { + if (ParseSingleTokenVariant(aValue, VARIANT_AUTO | VARIANT_KEYWORD, + nsCSSProps::kCounterSpeakAsKTable)) { if (aValue.GetUnit() == eCSSUnit_Enumerated && aValue.GetIntValue() == NS_STYLE_COUNTER_SPEAKAS_SPELL_OUT) { // Currently spell-out is not supported, so it is explicitly @@ -4771,8 +4890,10 @@ CSSParserImpl::ParseCounterRange(nsCSSValuePair& aPair) { static const int32_t VARIANT_BOUND = VARIANT_INTEGER | VARIANT_KEYWORD; nsCSSValue lower, upper; - if (!ParseVariant(lower, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable) || - !ParseVariant(upper, VARIANT_BOUND, nsCSSProps::kCounterRangeKTable)) { + if (!ParseSingleTokenVariant(lower, VARIANT_BOUND, + nsCSSProps::kCounterRangeKTable) || + !ParseSingleTokenVariant(upper, VARIANT_BOUND, + nsCSSProps::kCounterRangeKTable)) { return false; } if (lower.GetUnit() != eCSSUnit_Enumerated && @@ -5011,7 +5132,7 @@ CSSParserImpl::ParseRuleSet(RuleAppendFunc aAppendFunc, void* aData, // Next parse the declaration block uint32_t parseFlags = eParseDeclaration_InBraces | eParseDeclaration_AllowImportant; - css::Declaration* declaration = ParseDeclarationBlock(parseFlags); + RefPtr declaration = ParseDeclarationBlock(parseFlags); if (nullptr == declaration) { delete slist; return false; @@ -6236,7 +6357,7 @@ CSSParserImpl::ParseSelector(nsCSSSelectorList* aList, return true; } -css::Declaration* +already_AddRefed CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext) { bool checkForBraces = (aFlags & eParseDeclaration_InBraces) != 0; @@ -6253,7 +6374,7 @@ CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext) return nullptr; } } - css::Declaration* declaration = new css::Declaration(); + RefPtr declaration = new css::Declaration(); mData.AssertInitialState(); for (;;) { bool changed; @@ -6271,15 +6392,15 @@ CSSParserImpl::ParseDeclarationBlock(uint32_t aFlags, nsCSSContextType aContext) } } declaration->CompressFrom(&mData); - return declaration; + return declaration.forget(); } -bool +CSSParseResult CSSParserImpl::ParseColor(nsCSSValue& aValue) { if (!GetToken(true)) { REPORT_UNEXPECTED_EOF(PEColorEOF); - return false; + return CSSParseResult::NotFound; } nsCSSToken* tk = &mToken; @@ -6295,14 +6416,14 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) eCSSUnit_ShortHexColor : eCSSUnit_HexColor; aValue.SetIntegerColorValue(rgba, unit); - return true; + return CSSParseResult::Ok; } break; case eCSSToken_Ident: if (NS_ColorNameToRGB(tk->mIdent, &rgba)) { aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident); - return true; + return CSSParseResult::Ok; } else { nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent); @@ -6310,7 +6431,7 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) int32_t value; if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kColorKTable, value)) { aValue.SetIntValue(value, eCSSUnit_EnumColor); - return true; + return CSSParseResult::Ok; } } } @@ -6327,7 +6448,7 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) ParseNumberColorComponent(g, ',') && ParseNumberColorComponent(b, ')')) { aValue.SetIntegerColorValue(NS_RGB(r, g, b), eCSSUnit_RGBColor); - return true; + return CSSParseResult::Ok; } } else { float r, g, b; @@ -6336,11 +6457,11 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) ParsePercentageColorComponent(b, ')')) { aValue.SetFloatColorValue(r, g, b, 1.0f, eCSSUnit_PercentageRGBColor); - return true; + return CSSParseResult::Ok; } } SkipUntil(')'); - return false; + return CSSParseResult::Error; } else if (mToken.mIdent.LowerCaseEqualsLiteral("rgba")) { // rgba ( component , component , component , opacity ) @@ -6355,7 +6476,7 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) ParseColorOpacity(a)) { aValue.SetIntegerColorValue(NS_RGBA(r, g, b, a), eCSSUnit_RGBAColor); - return true; + return CSSParseResult::Ok; } } else { float r, g, b, a; @@ -6364,11 +6485,11 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) ParsePercentageColorComponent(b, ',') && ParseColorOpacity(a)) { aValue.SetFloatColorValue(r, g, b, a, eCSSUnit_PercentageRGBAColor); - return true; + return CSSParseResult::Ok; } } SkipUntil(')'); - return false; + return CSSParseResult::Error; } else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) { // hsl ( hue , saturation , lightness ) @@ -6376,10 +6497,10 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) float h, s, l; if (ParseHSLColor(h, s, l, ')')) { aValue.SetFloatColorValue(h, s, l, 1.0f, eCSSUnit_HSLColor); - return true; + return CSSParseResult::Ok; } SkipUntil(')'); - return false; + return CSSParseResult::Error; } else if (mToken.mIdent.LowerCaseEqualsLiteral("hsla")) { // hsla ( hue , saturation , lightness , opacity ) @@ -6389,10 +6510,10 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) if (ParseHSLColor(h, s, l, ',') && ParseColorOpacity(a)) { aValue.SetFloatColorValue(h, s, l, a, eCSSUnit_HSLAColor); - return true; + return CSSParseResult::Ok; } SkipUntil(')'); - return false; + return CSSParseResult::Error; } break; default: @@ -6444,14 +6565,14 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue) } if (NS_HexToRGB(str, &rgba)) { aValue.SetIntegerColorValue(rgba, eCSSUnit_HexColor); - return true; + return CSSParseResult::Ok; } } // It's not a color REPORT_UNEXPECTED_TOKEN(PEColorNotColor); UngetToken(); - return false; + return CSSParseResult::NotFound; } bool @@ -6692,12 +6813,17 @@ CSSParserImpl::LookupKeywordPrefixAware(nsAString& aKeywordStr, } bool -CSSParserImpl::ShouldUseUnprefixingService() +CSSParserImpl::ShouldUseUnprefixingService() const { if (!sUnprefixingServiceEnabled) { // Unprefixing is globally disabled. return false; } + if (sWebkitPrefixedAliasesEnabled) { + // Native webkit-prefix support is enabled, which trumps the unprefixing + // service for handling prefixed CSS. Don't try to use both at once. + return false; + } #ifdef NIGHTLY_BUILD if (sUnprefixingServiceGloballyWhitelisted) { @@ -6767,8 +6893,9 @@ CSSParserImpl::ParsePropertyWithUnprefixingService( } bool -CSSParserImpl::ParseWebkitPrefixedGradient(nsAString& aPrefixedFuncName, - nsCSSValue& aValue) +CSSParserImpl::ParseWebkitPrefixedGradientWithService( + nsAString& aPrefixedFuncName, + nsCSSValue& aValue) { MOZ_ASSERT(ShouldUseUnprefixingService(), "Should only call if we're allowed to use unprefixing service"); @@ -6820,10 +6947,10 @@ CSSParserImpl::ParseWebkitPrefixedGradient(nsAString& aPrefixedFuncName, nsAutoScannerChanger scannerChanger(this, unprefixedFuncBody); if (unprefixedFuncName.EqualsLiteral("linear-gradient")) { - return ParseLinearGradient(aValue, false, false); + return ParseLinearGradient(aValue, 0); } if (unprefixedFuncName.EqualsLiteral("radial-gradient")) { - return ParseRadialGradient(aValue, false, false); + return ParseRadialGradient(aValue, 0); } NS_ERROR("CSSUnprefixingService returned an unrecognized type of " @@ -7204,7 +7331,7 @@ CSSParserImpl::TranslateDimension(nsCSSValue& aValue, VARIANT_CALC | \ VARIANT_OPENTYPE_SVG_KEYWORD -bool +CSSParseResult CSSParserImpl::ParseVariantWithRestrictions(nsCSSValue& aValue, int32_t aVariantMask, const KTableValue aKeywordTable[], @@ -7226,7 +7353,7 @@ CSSParserImpl::ParseVariantWithRestrictions(nsCSSValue& aValue, // full-range parsing inside the calc() expression, and the code that // computes the calc will be required to clamp the resulting value to an // appropriate range. -bool +CSSParseResult CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue, int32_t aVariantMask, const KTableValue aKeywordTable[]) @@ -7240,35 +7367,35 @@ CSSParserImpl::ParseNonNegativeVariant(nsCSSValue& aValue, VARIANT_INTEGER)) == 0, "need to update code below to handle additional variants"); - if (ParseVariant(aValue, aVariantMask, aKeywordTable)) { + CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable); + if (result == CSSParseResult::Ok) { if (eCSSUnit_Number == aValue.GetUnit() || aValue.IsLengthUnit()){ if (aValue.GetFloatValue() < 0) { UngetToken(); - return false; + return CSSParseResult::NotFound; } } else if (aValue.GetUnit() == eCSSUnit_Percent) { if (aValue.GetPercentValue() < 0) { UngetToken(); - return false; + return CSSParseResult::NotFound; } } else if (aValue.GetUnit() == eCSSUnit_Integer) { if (aValue.GetIntValue() < 0) { UngetToken(); - return false; + return CSSParseResult::NotFound; } } - return true; } - return false; + return result; } // Note that callers passing VARIANT_CALC in aVariantMask will get // full-range parsing inside the calc() expression, and the code that // computes the calc will be required to clamp the resulting value to an // appropriate range. -bool +CSSParseResult CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue, int32_t aVariantMask, const KTableValue aKeywordTable[]) @@ -7280,25 +7407,25 @@ CSSParserImpl::ParseOneOrLargerVariant(nsCSSValue& aValue, VARIANT_INTEGER)) == 0, "need to update code below to handle additional variants"); - if (ParseVariant(aValue, aVariantMask, aKeywordTable)) { + CSSParseResult result = ParseVariant(aValue, aVariantMask, aKeywordTable); + if (result == CSSParseResult::Ok) { if (aValue.GetUnit() == eCSSUnit_Integer) { if (aValue.GetIntValue() < 1) { UngetToken(); - return false; + return CSSParseResult::NotFound; } } else if (eCSSUnit_Number == aValue.GetUnit()) { if (aValue.GetFloatValue() < 1.0f) { UngetToken(); - return false; + return CSSParseResult::NotFound; } } - return true; } - return false; + return result; } -// Assigns to aValue iff it returns true. -bool +// Assigns to aValue iff it returns CSSParseResult::Ok. +CSSParseResult CSSParserImpl::ParseVariant(nsCSSValue& aValue, int32_t aVariantMask, const KTableValue aKeywordTable[]) @@ -7317,9 +7444,13 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, "must not set both VARIANT_IDENTIFIER and " "VARIANT_IDENTIFIER_NO_INHERIT"); - if (!GetToken(true)) { - return false; + uint32_t lineBefore, colBefore; + if (!GetNextTokenLocation(true, &lineBefore, &colBefore) || + !GetToken(true)) { + // Must be at EOF. + return CSSParseResult::NotFound; } + nsCSSToken* tk = &mToken; if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE | VARIANT_ALL)) != 0) && (eCSSToken_Ident == tk->mType)) { @@ -7330,7 +7461,7 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, if ((aVariantMask & VARIANT_AUTO) != 0) { if (eCSSKeyword_auto == keyword) { aValue.SetAutoValue(); - return true; + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_INHERIT) != 0) { @@ -7342,41 +7473,41 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, // they probably need to be added in ParseCustomIdent as well. if (eCSSKeyword_inherit == keyword) { aValue.SetInheritValue(); - return true; + return CSSParseResult::Ok; } else if (eCSSKeyword_initial == keyword) { aValue.SetInitialValue(); - return true; + return CSSParseResult::Ok; } else if (eCSSKeyword_unset == keyword && nsLayoutUtils::UnsetValueEnabled()) { aValue.SetUnsetValue(); - return true; + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_NONE) != 0) { if (eCSSKeyword_none == keyword) { aValue.SetNoneValue(); - return true; + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_ALL) != 0) { if (eCSSKeyword_all == keyword) { aValue.SetAllValue(); - return true; + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_NORMAL) != 0) { if (eCSSKeyword_normal == keyword) { aValue.SetNormalValue(); - return true; + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_SYSFONT) != 0) { if (eCSSKeyword__moz_use_system_font == keyword && !IsParsingCompoundProperty()) { aValue.SetSystemFontValue(); - return true; + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_OPENTYPE_SVG_KEYWORD) != 0) { @@ -7388,7 +7519,7 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, int32_t value; if (nsCSSProps::FindKeyword(keyword, aKeywordTable, value)) { aValue.SetIntValue(value, eCSSUnit_Enumerated); - return true; + return CSSParseResult::Ok; } } } @@ -7398,12 +7529,12 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, if (((aVariantMask & VARIANT_NUMBER) != 0) && (eCSSToken_Number == tk->mType)) { aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number); - return true; + return CSSParseResult::Ok; } if (((aVariantMask & VARIANT_INTEGER) != 0) && (eCSSToken_Number == tk->mType) && tk->mIntegerValid) { aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer); - return true; + return CSSParseResult::Ok; } if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0 && @@ -7416,25 +7547,27 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, ((aVariantMask & VARIANT_NONNEGATIVE_DIMENSION) != 0 && tk->mNumber < 0.0)) { UngetToken(); - return false; + AssertNextTokenAt(lineBefore, colBefore); + return CSSParseResult::NotFound; } if (TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent)) { - return true; + return CSSParseResult::Ok; } // Put the token back; we didn't parse it, so we shouldn't consume it UngetToken(); - return false; + AssertNextTokenAt(lineBefore, colBefore); + return CSSParseResult::NotFound; } if (((aVariantMask & VARIANT_PERCENT) != 0) && (eCSSToken_Percentage == tk->mType)) { aValue.SetPercentValue(tk->mNumber); - return true; + return CSSParseResult::Ok; } if (mUnitlessLengthQuirk) { // NONSTANDARD: Nav interprets unitless numbers as px if (((aVariantMask & VARIANT_LENGTH) != 0) && (eCSSToken_Number == tk->mType)) { aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel); - return true; + return CSSParseResult::Ok; } } @@ -7444,55 +7577,73 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, if (((aVariantMask & VARIANT_LENGTH) != 0) && (eCSSToken_Number == tk->mType)) { aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel); - return true; + return CSSParseResult::Ok; } } if (((aVariantMask & VARIANT_URL) != 0) && eCSSToken_URL == tk->mType) { SetValueToURL(aValue, tk->mIdent); - return true; + return CSSParseResult::Ok; } if ((aVariantMask & VARIANT_GRADIENT) != 0 && eCSSToken_Function == tk->mType) { // a generated gradient nsDependentString tmp(tk->mIdent, 0); - bool isLegacy = false; + uint8_t gradientFlags = 0; if (sMozGradientsEnabled && StringBeginsWith(tmp, NS_LITERAL_STRING("-moz-"))) { tmp.Rebind(tmp, 5); - isLegacy = true; + gradientFlags |= eGradient_MozLegacy; + } else if (sWebkitPrefixedAliasesEnabled && + StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) { + tmp.Rebind(tmp, 8); + gradientFlags |= eGradient_WebkitLegacy; } - bool isRepeating = false; if (StringBeginsWith(tmp, NS_LITERAL_STRING("repeating-"))) { tmp.Rebind(tmp, 10); - isRepeating = true; + gradientFlags |= eGradient_Repeating; } if (tmp.LowerCaseEqualsLiteral("linear-gradient")) { - return ParseLinearGradient(aValue, isRepeating, isLegacy); + if (!ParseLinearGradient(aValue, gradientFlags)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; } if (tmp.LowerCaseEqualsLiteral("radial-gradient")) { - return ParseRadialGradient(aValue, isRepeating, isLegacy); + if (!ParseRadialGradient(aValue, gradientFlags)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; } if (ShouldUseUnprefixingService() && - !isRepeating && !isLegacy && + !gradientFlags && StringBeginsWith(tmp, NS_LITERAL_STRING("-webkit-"))) { // Copy 'tmp' into a string on the stack, since as soon as we // start parsing, its backing store (in "tk") will be overwritten nsAutoString prefixedFuncName(tmp); - return ParseWebkitPrefixedGradient(prefixedFuncName, aValue); + if (!ParseWebkitPrefixedGradientWithService(prefixedFuncName, aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_IMAGE_RECT) != 0 && eCSSToken_Function == tk->mType && tk->mIdent.LowerCaseEqualsLiteral("-moz-image-rect")) { - return ParseImageRect(aValue); + if (!ParseImageRect(aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; } if ((aVariantMask & VARIANT_ELEMENT) != 0 && eCSSToken_Function == tk->mType && tk->mIdent.LowerCaseEqualsLiteral("-moz-element")) { - return ParseElement(aValue); + if (!ParseElement(aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; } if ((aVariantMask & VARIANT_COLOR) != 0) { if (mHashlessColorQuirk || // NONSTANDARD: Nav interprets 'xxyyzz' values even without '#' prefix @@ -7507,10 +7658,7 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, { // Put token back so that parse color can get it UngetToken(); - if (ParseColor(aValue)) { - return true; - } - return false; + return ParseColor(aValue); } } if (((aVariantMask & VARIANT_STRING) != 0) && @@ -7518,7 +7666,7 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, nsAutoString buffer; buffer.Append(tk->mIdent); aValue.SetStringValue(buffer, eCSSUnit_String); - return true; + return CSSParseResult::Ok; } if (((aVariantMask & (VARIANT_IDENTIFIER | VARIANT_IDENTIFIER_NO_INHERIT)) != 0) && @@ -7529,38 +7677,41 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, (tk->mIdent.LowerCaseEqualsLiteral("unset") && nsLayoutUtils::UnsetValueEnabled())))) { aValue.SetStringValue(tk->mIdent, eCSSUnit_Ident); - return true; + return CSSParseResult::Ok; } if (((aVariantMask & VARIANT_COUNTER) != 0) && (eCSSToken_Function == tk->mType) && (tk->mIdent.LowerCaseEqualsLiteral("counter") || tk->mIdent.LowerCaseEqualsLiteral("counters"))) { - return ParseCounter(aValue); + if (!ParseCounter(aValue)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; } if (((aVariantMask & VARIANT_ATTR) != 0) && (eCSSToken_Function == tk->mType) && tk->mIdent.LowerCaseEqualsLiteral("attr")) { if (!ParseAttr(aValue)) { SkipUntil(')'); - return false; + return CSSParseResult::Error; } - return true; + return CSSParseResult::Ok; } if (((aVariantMask & VARIANT_TIMING_FUNCTION) != 0) && (eCSSToken_Function == tk->mType)) { if (tk->mIdent.LowerCaseEqualsLiteral("cubic-bezier")) { if (!ParseTransitionTimingFunctionValues(aValue)) { SkipUntil(')'); - return false; + return CSSParseResult::Error; } - return true; + return CSSParseResult::Ok; } if (tk->mIdent.LowerCaseEqualsLiteral("steps")) { if (!ParseTransitionStepTimingFunctionValues(aValue)) { SkipUntil(')'); - return false; + return CSSParseResult::Error; } - return true; + return CSSParseResult::Ok; } } if ((aVariantMask & VARIANT_CALC) && @@ -7568,11 +7719,15 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue, (tk->mIdent.LowerCaseEqualsLiteral("calc") || tk->mIdent.LowerCaseEqualsLiteral("-moz-calc"))) { // calc() currently allows only lengths and percents inside it. - return ParseCalc(aValue, aVariantMask & VARIANT_LP); + if (!ParseCalc(aValue, aVariantMask & VARIANT_LP)) { + return CSSParseResult::Error; + } + return CSSParseResult::Ok; } UngetToken(); - return false; + AssertNextTokenAt(lineBefore, colBefore); + return CSSParseResult::NotFound; } bool @@ -7756,7 +7911,7 @@ CSSParserImpl::ParseSymbols(nsCSSValue& aValue) nsCSSValueList* item = symbols.SetListValue(); for (;;) { // FIXME Should also include VARIANT_IMAGE. See bug 1071436. - if (!ParseVariant(item->mValue, VARIANT_STRING, nullptr)) { + if (!ParseSingleTokenVariant(item->mValue, VARIANT_STRING, nullptr)) { break; } if (ExpectSymbol(')', true)) { @@ -7810,17 +7965,18 @@ CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL) bool CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { // 'inherit', 'initial' and 'unset' must be alone return true; } // Check for an angle with optional 'flip'. nsCSSValue angle; - if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) { + if (ParseSingleTokenVariant(angle, VARIANT_ANGLE, nullptr)) { nsCSSValue flip; - if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) { + if (ParseSingleTokenVariant(flip, VARIANT_KEYWORD, + nsCSSProps::kImageOrientationFlipKTable)) { RefPtr array = nsCSSValue::Array::Create(2); array->Item(0) = angle; array->Item(1) = flip; @@ -7835,7 +7991,8 @@ CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue) // The remaining possibilities (bare 'flip' and 'from-image') are both // keywords, so we can handle them at the same time. nsCSSValue keyword; - if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) { + if (ParseSingleTokenVariant(keyword, VARIANT_KEYWORD, + nsCSSProps::kImageOrientationKTable)) { aValue = keyword; return true; } @@ -7873,13 +8030,13 @@ CSSParserImpl::ParseImageRect(nsCSSValue& aImage) } static const int32_t VARIANT_SIDE = VARIANT_NUMBER | VARIANT_PERCENT; - if (!ParseNonNegativeVariant(top, VARIANT_SIDE, nullptr) || + if (!ParseSingleTokenNonNegativeVariant(top, VARIANT_SIDE, nullptr) || !ExpectSymbol(',', true) || - !ParseNonNegativeVariant(right, VARIANT_SIDE, nullptr) || + !ParseSingleTokenNonNegativeVariant(right, VARIANT_SIDE, nullptr) || !ExpectSymbol(',', true) || - !ParseNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) || + !ParseSingleTokenNonNegativeVariant(bottom, VARIANT_SIDE, nullptr) || !ExpectSymbol(',', true) || - !ParseNonNegativeVariant(left, VARIANT_SIDE, nullptr) || + !ParseSingleTokenNonNegativeVariant(left, VARIANT_SIDE, nullptr) || !ExpectSymbol(')', true)) break; @@ -7925,7 +8082,7 @@ CSSParserImpl::ParseFlex() { // First check for inherit / initial / unset nsCSSValue tmpVal; - if (ParseVariant(tmpVal, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(tmpVal, VARIANT_INHERIT, nullptr)) { AppendValue(eCSSProperty_flex_grow, tmpVal); AppendValue(eCSSProperty_flex_shrink, tmpVal); AppendValue(eCSSProperty_flex_basis, tmpVal); @@ -7933,7 +8090,7 @@ CSSParserImpl::ParseFlex() } // Next, check for 'none' == '0 0 auto' - if (ParseVariant(tmpVal, VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(tmpVal, VARIANT_NONE, nullptr)) { AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number)); AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number)); AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto)); @@ -7975,8 +8132,8 @@ CSSParserImpl::ParseFlex() // it'll treat unitless 0 as a number. The flexbox spec requires this: // "a unitless zero that is not already preceded by two flex factors must be // interpreted as a flex factor. - if (!ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER, - nsCSSProps::kWidthKTable)) { + if (ParseNonNegativeVariant(tmpVal, flexBasisVariantMask | VARIANT_NUMBER, + nsCSSProps::kWidthKTable) != CSSParseResult::Ok) { // First component was not a valid flex-basis or flex-grow value. Fail. return false; } @@ -7988,7 +8145,7 @@ CSSParserImpl::ParseFlex() // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow. bool doneParsing = false; if (wasFirstComponentFlexBasis) { - if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) { + if (ParseNonNegativeNumber(tmpVal)) { flexGrow = tmpVal; } else { // Failed to parse anything after our flex-basis -- that's fine. We can @@ -8000,7 +8157,7 @@ CSSParserImpl::ParseFlex() if (!doneParsing) { // (c) OK -- the last thing we parsed was flex-grow, so look for a // flex-shrink in the next position. - if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nullptr)) { + if (ParseNonNegativeNumber(tmpVal)) { flexShrink = tmpVal; } @@ -8022,10 +8179,15 @@ CSSParserImpl::ParseFlex() // by *no* flex factors (if it were the first token), we would've already // parsed it in our very first call to ParseNonNegativeVariant(). So, any // unitless 0 encountered here *must* have been preceded by 2 flex factors. - if (!wasFirstComponentFlexBasis && + if (!wasFirstComponentFlexBasis) { + CSSParseResult result = ParseNonNegativeVariant(tmpVal, flexBasisVariantMask, - nsCSSProps::kWidthKTable)) { - flexBasis = tmpVal; + nsCSSProps::kWidthKTable); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { + flexBasis = tmpVal; + } } } @@ -8073,7 +8235,7 @@ bool CSSParserImpl::ParseGridAutoFlow() { nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { AppendValue(eCSSProperty_grid_auto_flow, value); return true; } @@ -8233,27 +8395,30 @@ CSSParserImpl::ParseOptionalLineNameListAfterSubgrid(nsCSSValue& aValue) } // Parse a . -bool +CSSParseResult CSSParserImpl::ParseGridTrackBreadth(nsCSSValue& aValue) { - if (ParseNonNegativeVariant(aValue, + CSSParseResult result = + ParseNonNegativeVariant(aValue, VARIANT_AUTO | VARIANT_LPCALC | VARIANT_KEYWORD, - nsCSSProps::kGridTrackBreadthKTable)) { - return true; + nsCSSProps::kGridTrackBreadthKTable); + if (result == CSSParseResult::Ok || + result == CSSParseResult::Error) { + return result; } // Attempt to parse (a dimension with the "fr" unit). if (!GetToken(true)) { - return false; + return CSSParseResult::NotFound; } if (!(eCSSToken_Dimension == mToken.mType && mToken.mIdent.LowerCaseEqualsLiteral("fr") && mToken.mNumber >= 0)) { UngetToken(); - return false; + return CSSParseResult::NotFound; } aValue.SetFloatValue(mToken.mNumber, eCSSUnit_FlexFraction); - return true; + return CSSParseResult::Ok; } // Parse a . @@ -8261,8 +8426,10 @@ CSSParseResult CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue) { // Attempt to parse a single . - if (ParseGridTrackBreadth(aValue)) { - return CSSParseResult::Ok; + CSSParseResult result = ParseGridTrackBreadth(aValue); + if (result == CSSParseResult::Ok || + result == CSSParseResult::Error) { + return result; } // Attempt to parse a minmax() function. @@ -8275,9 +8442,9 @@ CSSParserImpl::ParseGridTrackSize(nsCSSValue& aValue) return CSSParseResult::NotFound; } nsCSSValue::Array* func = aValue.InitFunction(eCSSKeyword_minmax, 2); - if (ParseGridTrackBreadth(func->Item(1)) && + if (ParseGridTrackBreadth(func->Item(1)) == CSSParseResult::Ok && ExpectSymbol(',', true) && - ParseGridTrackBreadth(func->Item(2)) && + ParseGridTrackBreadth(func->Item(2)) == CSSParseResult::Ok && ExpectSymbol(')', true)) { return CSSParseResult::Ok; } @@ -8289,7 +8456,7 @@ bool CSSParserImpl::ParseGridAutoColumnsRows(nsCSSProperty aPropID) { nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT, nullptr) || + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) || ParseGridTrackSize(value) == CSSParseResult::Ok) { AppendValue(aPropID, value); return true; @@ -8570,7 +8737,7 @@ bool CSSParserImpl::ParseGridTemplateColumnsRows(nsCSSProperty aPropID) { nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { AppendValue(aPropID, value); return true; } @@ -8686,7 +8853,7 @@ bool CSSParserImpl::ParseGridTemplateAreas() { nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { AppendValue(eCSSProperty_grid_template_areas, value); return true; } @@ -8723,7 +8890,7 @@ CSSParserImpl::ParseGridTemplate() // <'grid-template-columns'> / <'grid-template-rows'> | // [ / ]? [ ? ? ? ]+ nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { AppendValue(eCSSProperty_grid_template_areas, value); AppendValue(eCSSProperty_grid_template_columns, value); AppendValue(eCSSProperty_grid_template_rows, value); @@ -8734,7 +8901,7 @@ CSSParserImpl::ParseGridTemplate() // 'none' can appear either by itself, // or as the beginning of <'grid-template-columns'> / <'grid-template-rows'> - if (ParseVariant(value, VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_NONE, nullptr)) { AppendValue(eCSSProperty_grid_template_columns, value); if (ExpectSymbol('/', true)) { return ParseGridTemplateAfterSlash(/* aColumnsIsTrackList = */ false); @@ -8811,7 +8978,7 @@ bool CSSParserImpl::ParseGridTemplateAfterSlash(bool aColumnsIsTrackList) { nsCSSValue rowsValue; - if (ParseVariant(rowsValue, VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(rowsValue, VARIANT_NONE, nullptr)) { // <'grid-template-columns'> / <'grid-template-rows'> AppendValue(eCSSProperty_grid_template_rows, rowsValue); nsCSSValue areasValue(eCSSUnit_None); // implied @@ -8936,7 +9103,7 @@ bool CSSParserImpl::ParseGrid() { nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { for (const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_grid); *subprops != eCSSProperty_UNKNOWN; ++subprops) { @@ -9028,7 +9195,7 @@ CSSParserImpl::ParseGridLine(nsCSSValue& aValue) // auto | // [ span? && [ || ] ] - if (ParseVariant(aValue, VARIANT_AUTO, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_AUTO, nullptr)) { return true; } @@ -9111,7 +9278,7 @@ bool CSSParserImpl::ParseGridColumnRowStartEnd(nsCSSProperty aPropID) { nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT, nullptr) || + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) || ParseGridLine(value)) { AppendValue(aPropID, value); return true; @@ -9140,7 +9307,7 @@ CSSParserImpl::ParseGridColumnRow(nsCSSProperty aStartPropID, { nsCSSValue value; nsCSSValue secondValue; - if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { AppendValue(aStartPropID, value); AppendValue(aEndPropID, value); return true; @@ -9175,7 +9342,7 @@ bool CSSParserImpl::ParseGridArea() { nsCSSValue values[4]; - if (ParseVariant(values[0], VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(values[0], VARIANT_INHERIT, nullptr)) { AppendValue(eCSSProperty_grid_row_start, values[0]); AppendValue(eCSSProperty_grid_column_start, values[0]); AppendValue(eCSSProperty_grid_row_end, values[0]); @@ -9287,13 +9454,19 @@ bool CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient) { nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement(); - if (!ParseVariant(stop->mColor, VARIANT_COLOR, nullptr)) { + CSSParseResult result = ParseVariant(stop->mColor, VARIANT_COLOR, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { stop->mIsInterpolationHint = true; } // Stop positions do not have to fall between the starting-point and // ending-point, so we don't use ParseNonNegativeVariant. - if (!ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr)) { + result = ParseVariant(stop->mLocation, VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { if (stop->mIsInterpolationHint) { return false; } @@ -9302,6 +9475,31 @@ CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient) return true; } +// Helper for ParseLinearGradient -- returns true iff aPosition represents a +// box-position value which was parsed with only edge keywords. +// e.g. "left top", or "bottom", but not "left 10px" +// +// (NOTE: Even though callers may want to exclude explicit "center", we still +// need to allow for _CENTER here, because omitted position-values (e.g. the +// x-component of a value like "top") will have been parsed as being *implicit* +// center. The correct way to disallow *explicit* center is to pass "false" for +// ParseBoxPositionValues()'s "aAllowExplicitCenter" parameter, before you +// call this function.) +static bool +IsBoxPositionStrictlyEdgeKeywords(nsCSSValuePair& aPosition) +{ + const nsCSSValue& xValue = aPosition.mXValue; + const nsCSSValue& yValue = aPosition.mYValue; + return (xValue.GetUnit() == eCSSUnit_Enumerated && + (xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT | + NS_STYLE_BG_POSITION_CENTER | + NS_STYLE_BG_POSITION_RIGHT)) && + yValue.GetUnit() == eCSSUnit_Enumerated && + (yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP | + NS_STYLE_BG_POSITION_CENTER | + NS_STYLE_BG_POSITION_BOTTOM))); +} + // // : linear-gradient( ? ')' // | radial-gradient( ? ')' @@ -9323,17 +9521,19 @@ CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient) // // : , [, ]* bool -CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, - bool aIsLegacy) +CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, + uint8_t aFlags) { RefPtr cssGradient - = new nsCSSValueGradient(false, aIsRepeating); + = new nsCSSValueGradient(false, aFlags & eGradient_Repeating); if (!GetToken(true)) { return false; } - if (mToken.mType == eCSSToken_Ident && + // Check for "to" syntax (but not if parsing a -webkit-linear-gradient) + if (!(aFlags & eGradient_WebkitLegacy) && + mToken.mType == eCSSToken_Ident && mToken.mIdent.LowerCaseEqualsLiteral("to")) { // "to" syntax doesn't allow explicit "center" @@ -9343,16 +9543,7 @@ CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, } // [ to [left | right] || [top | bottom] ] , - const nsCSSValue& xValue = cssGradient->mBgPos.mXValue; - const nsCSSValue& yValue = cssGradient->mBgPos.mYValue; - if (xValue.GetUnit() != eCSSUnit_Enumerated || - !(xValue.GetIntValue() & (NS_STYLE_BG_POSITION_LEFT | - NS_STYLE_BG_POSITION_CENTER | - NS_STYLE_BG_POSITION_RIGHT)) || - yValue.GetUnit() != eCSSUnit_Enumerated || - !(yValue.GetIntValue() & (NS_STYLE_BG_POSITION_TOP | - NS_STYLE_BG_POSITION_CENTER | - NS_STYLE_BG_POSITION_BOTTOM))) { + if (!IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) { SkipUntil(')'); return false; } @@ -9365,11 +9556,14 @@ CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, return ParseGradientColorStops(cssGradient, aValue); } - if (!aIsLegacy) { + if (!(aFlags & eGradient_AnyLegacy)) { + // We're parsing an unprefixed linear-gradient, and we tried & failed to + // parse a 'to' token above. Put the token back & try to re-parse our + // expression as ? UngetToken(); // , - if (ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) && + if (ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) && !ExpectSymbol(',', true)) { SkipUntil(')'); return false; @@ -9378,29 +9572,48 @@ CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, return ParseGradientColorStops(cssGradient, aValue); } - nsCSSTokenType ty = mToken.mType; - nsString id = mToken.mIdent; + // If we get here, we're parsing a prefixed linear-gradient expression. Put + // back the first token (which we may have checked for "to" above) and try to + // parse expression as ? + bool haveGradientLine = IsLegacyGradientLine(mToken.mType, mToken.mIdent); UngetToken(); - // - bool haveGradientLine = IsLegacyGradientLine(ty, id); if (haveGradientLine) { + // Parse a cssGradient->mIsLegacySyntax = true; bool haveAngle = - ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr); + ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr); // if we got an angle, we might now have a comma, ending the gradient-line - if (!haveAngle || !ExpectSymbol(',', true)) { - if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) { + bool haveAngleComma = haveAngle && ExpectSymbol(',', true); + + // If we're webkit-prefixed & didn't get an angle, + // OR if we're moz-prefixed & didn't get an angle+comma, + // then proceed to parse a box-position. + if (((aFlags & eGradient_WebkitLegacy) && !haveAngle) || + ((aFlags & eGradient_MozLegacy) && !haveAngleComma)) { + // (Note: 3rd arg controls whether the "center" keyword is allowed. + // -moz-linear-gradient allows it; -webkit-linear-gradient does not.) + if (!ParseBoxPositionValues(cssGradient->mBgPos, false, + (aFlags & eGradient_MozLegacy))) { + SkipUntil(')'); + return false; + } + + // -webkit-linear-gradient only supports edge keywords here. + if ((aFlags & eGradient_WebkitLegacy) && + !IsBoxPositionStrictlyEdgeKeywords(cssGradient->mBgPos)) { SkipUntil(')'); return false; } if (!ExpectSymbol(',', true) && - // if we didn't already get an angle, we might have one now, - // otherwise it's an error + // If we didn't already get an angle, and we're not -webkit prefixed, + // we can parse an angle+comma now. Otherwise it's an error. (haveAngle || - !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) || + (aFlags & eGradient_WebkitLegacy) || + !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, + nullptr) || // now we better have a comma !ExpectSymbol(',', true))) { SkipUntil(')'); @@ -9413,28 +9626,30 @@ CSSParserImpl::ParseLinearGradient(nsCSSValue& aValue, bool aIsRepeating, } bool -CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, - bool aIsLegacy) +CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, + uint8_t aFlags) { RefPtr cssGradient - = new nsCSSValueGradient(true, aIsRepeating); + = new nsCSSValueGradient(true, aFlags & eGradient_Repeating); // [ || ] bool haveShape = - ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, - nsCSSProps::kRadialGradientShapeKTable); + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); - bool haveSize = ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, - aIsLegacy ? - nsCSSProps::kRadialGradientLegacySizeKTable : - nsCSSProps::kRadialGradientSizeKTable); + bool haveSize = + ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, + (aFlags & eGradient_AnyLegacy) ? + nsCSSProps::kRadialGradientLegacySizeKTable : + nsCSSProps::kRadialGradientSizeKTable); if (haveSize) { if (!haveShape) { // - haveShape = ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, - nsCSSProps::kRadialGradientShapeKTable); + haveShape = + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); } - } else if (!aIsLegacy) { + } else if (!(aFlags & eGradient_AnyLegacy)) { // Save RadialShape before parsing RadiusX because RadialShape and // RadiusX share the storage. int32_t shape = @@ -9443,7 +9658,8 @@ CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, // | [ | ]{2} cssGradient->mIsExplicitSize = true; haveSize = - ParseNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, nullptr); + ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusX(), VARIANT_LP, + nullptr); if (!haveSize) { // It was not an explicit size after all. // Note that ParseNonNegativeVariant may have put something @@ -9457,11 +9673,13 @@ CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, } else { // vertical extent is optional bool haveYSize = - ParseNonNegativeVariant(cssGradient->GetRadiusY(), VARIANT_LP, nullptr); + ParseSingleTokenNonNegativeVariant(cssGradient->GetRadiusY(), + VARIANT_LP, nullptr); if (!haveShape) { nsCSSValue shapeValue; - haveShape = ParseVariant(shapeValue, VARIANT_KEYWORD, - nsCSSProps::kRadialGradientShapeKTable); + haveShape = + ParseSingleTokenVariant(shapeValue, VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); if (haveShape) { shape = shapeValue.GetIntValue(); } @@ -9485,7 +9703,7 @@ CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, return false; } - if (!aIsLegacy) { + if (!(aFlags & eGradient_AnyLegacy)) { if (mToken.mType == eCSSToken_Ident && mToken.mIdent.LowerCaseEqualsLiteral("at")) { // [ || ]? at , @@ -9516,10 +9734,12 @@ CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, haveGradientLine = IsLegacyGradientLine(ty, id); } if (haveGradientLine) { - bool haveAngle = - ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr); + // Note: -webkit-radial-gradient() doesn't accept angles. + bool haveAngle = (aFlags & eGradient_WebkitLegacy) + ? false + : ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr); - // if we got an angle, we might now have a comma, ending the gradient-line + // If we got an angle, we might now have a comma, ending the gradient-line if (!haveAngle || !ExpectSymbol(',', true)) { if (!ParseBoxPositionValues(cssGradient->mBgPos, false)) { SkipUntil(')'); @@ -9527,10 +9747,12 @@ CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, } if (!ExpectSymbol(',', true) && - // if we didn't already get an angle, we might have one now, - // otherwise it's an error + // If we didn't already get an angle, and we're not -webkit prefixed, + // can parse an angle+comma now. Otherwise it's an error. (haveAngle || - !ParseVariant(cssGradient->mAngle, VARIANT_ANGLE, nullptr) || + (aFlags & eGradient_WebkitLegacy) || + !ParseSingleTokenVariant(cssGradient->mAngle, VARIANT_ANGLE, + nullptr) || // now we better have a comma !ExpectSymbol(',', true))) { SkipUntil(')'); @@ -9546,17 +9768,17 @@ CSSParserImpl::ParseRadialGradient(nsCSSValue& aValue, bool aIsRepeating, // radial gradients might have a shape and size here for legacy syntax if (!haveShape && !haveSize) { haveShape = - ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, - nsCSSProps::kRadialGradientShapeKTable); + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); haveSize = - ParseVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, - nsCSSProps::kRadialGradientLegacySizeKTable); + ParseSingleTokenVariant(cssGradient->GetRadialSize(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientLegacySizeKTable); // could be in either order if (!haveShape) { haveShape = - ParseVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, - nsCSSProps::kRadialGradientShapeKTable); + ParseSingleTokenVariant(cssGradient->GetRadialShape(), VARIANT_KEYWORD, + nsCSSProps::kRadialGradientShapeKTable); } } @@ -9677,7 +9899,12 @@ CSSParserImpl::ParseChoice(nsCSSValue aValues[], for (index = 0; index < aNumIDs; index++) { int32_t bit = 1 << index; if ((found & bit) == 0) { - if (ParseSingleValueProperty(aValues[index], aPropIDs[index])) { + CSSParseResult result = + ParseSingleValueProperty(aValues[index], aPropIDs[index]); + if (result == CSSParseResult::Error) { + return -1; + } + if (result == CSSParseResult::Ok) { found |= bit; // It's more efficient to break since it will reset |hadFound| // to |found|. Furthermore, ParseListStyle depends on our going @@ -9838,14 +10065,19 @@ CSSParserImpl::ParseBoxCornerRadius(nsCSSProperty aPropID) { nsCSSValue dimenX, dimenY; // required first value - if (! ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr)) + if (ParseNonNegativeVariant(dimenX, VARIANT_HLP | VARIANT_CALC, nullptr) != + CSSParseResult::Ok) { return false; + } // optional second value (forbidden if first value is inherit/initial/unset) if (dimenX.GetUnit() != eCSSUnit_Inherit && dimenX.GetUnit() != eCSSUnit_Initial && dimenX.GetUnit() != eCSSUnit_Unset) { - ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr); + if (ParseNonNegativeVariant(dimenY, VARIANT_LP | VARIANT_CALC, nullptr) == + CSSParseResult::Error) { + return false; + } } if (dimenX == dimenY || dimenY.GetUnit() == eCSSUnit_Null) { @@ -9868,11 +10100,16 @@ CSSParserImpl::ParseBoxCornerRadiiInternals(nsCSSValue array[]) int32_t countX = 0, countY = 0; NS_FOR_CSS_SIDES (side) { - if (! ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side], - (side > 0 ? 0 : VARIANT_INHERIT) | - VARIANT_LP | VARIANT_CALC, - nullptr)) + CSSParseResult result = + ParseNonNegativeVariant(dimenX.*nsCSSRect::sides[side], + (side > 0 ? 0 : VARIANT_INHERIT) | + VARIANT_LP | VARIANT_CALC, + nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { break; + } countX++; } if (countX == 0) @@ -9880,9 +10117,14 @@ CSSParserImpl::ParseBoxCornerRadiiInternals(nsCSSValue array[]) if (ExpectSymbol('/', true)) { NS_FOR_CSS_SIDES (side) { - if (! ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side], - VARIANT_LP | VARIANT_CALC, nullptr)) + CSSParseResult result = + ParseNonNegativeVariant(dimenY.*nsCSSRect::sides[side], + VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { break; + } countY++; } if (countY == 0) @@ -10036,7 +10278,7 @@ CSSParserImpl::ParseProperty(nsCSSProperty aPropID) case CSS_PROPERTY_PARSE_VALUE: { result = false; nsCSSValue value; - if (ParseSingleValueProperty(value, aPropID)) { + if (ParseSingleValueProperty(value, aPropID) == CSSParseResult::Ok) { AppendValue(aPropID, value); result = true; } @@ -10408,29 +10650,23 @@ CSSParserImpl::ParseBoxPropertyVariant(nsCSSValue& aValue, uint32_t aRestrictions, bool& aConsumedTokens) { - aConsumedTokens = false; - - uint32_t lineBefore, colBefore; - if (!GetNextTokenLocation(true, &lineBefore, &colBefore)) { - return false; - } - - if (!ParseVariantWithRestrictions(aValue, aVariantMask, aKeywordTable, - aRestrictions)) { - uint32_t lineAfter, colAfter; - if (!GetNextTokenLocation(true, &lineAfter, &colAfter)) { - // any single token value that was invalid will have been pushed back, - // so GetNextTokenLocation encountering EOF means we failed while - // parsing a multi-token value + CSSParseResult result = + ParseVariantWithRestrictions(aValue, aVariantMask, aKeywordTable, + aRestrictions); + switch (result) { + case CSSParseResult::Ok: aConsumedTokens = true; - } else if (lineAfter != lineBefore || colAfter != colBefore) { + return true; + case CSSParseResult::NotFound: + aConsumedTokens = false; + return false; + default: + MOZ_ASSERT_UNREACHABLE("invalid CSSParseResult value"); + // fall through + case CSSParseResult::Error: aConsumedTokens = true; - } - return false; + return false; } - - aConsumedTokens = true; - return true; } bool @@ -10455,12 +10691,6 @@ CSSParserImpl::ParseBoxProperty(nsCSSValue& aValue, return false; } - if (aPropID == eCSSProperty_script_level || - aPropID == eCSSProperty_math_display) { - MOZ_ASSERT(false, "must not be called for unsafe properties"); - return false; - } - if (variant & ~(VARIANT_AHKLP | VARIANT_COLOR | VARIANT_CALC)) { MOZ_ASSERT(false, "must only be called for properties that take certain " "variants"); @@ -10475,6 +10705,61 @@ CSSParserImpl::ParseBoxProperty(nsCSSValue& aValue, } bool +CSSParserImpl::ParseSingleValuePropertyByFunction(nsCSSValue& aValue, + nsCSSProperty aPropID) +{ + switch (aPropID) { + case eCSSProperty_font_family: + return ParseFamily(aValue); + case eCSSProperty_font_synthesis: + return ParseFontSynthesis(aValue); + case eCSSProperty_font_variant_alternates: + return ParseFontVariantAlternates(aValue); + case eCSSProperty_font_variant_east_asian: + return ParseFontVariantEastAsian(aValue); + case eCSSProperty_font_variant_ligatures: + return ParseFontVariantLigatures(aValue); + case eCSSProperty_font_variant_numeric: + return ParseFontVariantNumeric(aValue); + case eCSSProperty_font_feature_settings: + return ParseFontFeatureSettings(aValue); + case eCSSProperty_font_weight: + return ParseFontWeight(aValue); + case eCSSProperty_image_orientation: + return ParseImageOrientation(aValue); + case eCSSProperty_list_style_type: + return ParseListStyleType(aValue); + case eCSSProperty_marks: + return ParseMarks(aValue); + case eCSSProperty_scroll_snap_points_x: + return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_x); + case eCSSProperty_scroll_snap_points_y: + return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_y); + case eCSSProperty_scroll_snap_destination: + return ParseScrollSnapDestination(aValue); + case eCSSProperty_scroll_snap_coordinate: + return ParseScrollSnapCoordinate(aValue); + case eCSSProperty_text_align: + return ParseTextAlign(aValue); + case eCSSProperty_text_align_last: + return ParseTextAlignLast(aValue); + case eCSSProperty_text_decoration_line: + return ParseTextDecorationLine(aValue); + case eCSSProperty_text_combine_upright: + return ParseTextCombineUpright(aValue); + case eCSSProperty_text_overflow: + return ParseTextOverflow(aValue); + case eCSSProperty_touch_action: + return ParseTouchAction(aValue); + case eCSSProperty_contain: + return ParseContain(aValue); + default: + MOZ_ASSERT(false, "should not reach here"); + return false; + } +} + +CSSParseResult CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, nsCSSProperty aPropID) { @@ -10488,76 +10773,39 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, if (aPropID < 0 || aPropID >= eCSSProperty_COUNT_no_shorthands) { MOZ_ASSERT(false, "not a single value property"); - return false; + return CSSParseResult::NotFound; } if (nsCSSProps::PropHasFlags(aPropID, CSS_PROPERTY_VALUE_PARSER_FUNCTION)) { - switch (aPropID) { - case eCSSProperty_font_family: - return ParseFamily(aValue); - case eCSSProperty_font_synthesis: - return ParseFontSynthesis(aValue); - case eCSSProperty_font_variant_alternates: - return ParseFontVariantAlternates(aValue); - case eCSSProperty_font_variant_east_asian: - return ParseFontVariantEastAsian(aValue); - case eCSSProperty_font_variant_ligatures: - return ParseFontVariantLigatures(aValue); - case eCSSProperty_font_variant_numeric: - return ParseFontVariantNumeric(aValue); - case eCSSProperty_font_feature_settings: - return ParseFontFeatureSettings(aValue); - case eCSSProperty_font_weight: - return ParseFontWeight(aValue); - case eCSSProperty_image_orientation: - return ParseImageOrientation(aValue); - case eCSSProperty_list_style_type: - return ParseListStyleType(aValue); - case eCSSProperty_marks: - return ParseMarks(aValue); - case eCSSProperty_scroll_snap_points_x: - return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_x); - case eCSSProperty_scroll_snap_points_y: - return ParseScrollSnapPoints(aValue, eCSSProperty_scroll_snap_points_y); - case eCSSProperty_scroll_snap_destination: - return ParseScrollSnapDestination(aValue); - case eCSSProperty_scroll_snap_coordinate: - return ParseScrollSnapCoordinate(aValue); - case eCSSProperty_text_align: - return ParseTextAlign(aValue); - case eCSSProperty_text_align_last: - return ParseTextAlignLast(aValue); - case eCSSProperty_text_decoration_line: - return ParseTextDecorationLine(aValue); - case eCSSProperty_text_combine_upright: - return ParseTextCombineUpright(aValue); - case eCSSProperty_text_overflow: - return ParseTextOverflow(aValue); - case eCSSProperty_touch_action: - return ParseTouchAction(aValue); - case eCSSProperty_contain: - return ParseContain(aValue); - default: - MOZ_ASSERT(false, "should not reach here"); - return false; + uint32_t lineBefore, colBefore; + if (!GetNextTokenLocation(true, &lineBefore, &colBefore)) { + // We're at EOF before parsing. + return CSSParseResult::NotFound; } + + if (ParseSingleValuePropertyByFunction(aValue, aPropID)) { + return CSSParseResult::Ok; + } + + uint32_t lineAfter, colAfter; + if (!GetNextTokenLocation(true, &lineAfter, &colAfter) || + lineAfter != lineBefore || + colAfter != colBefore) { + // Any single token value that was invalid will have been pushed back, + // so GetNextTokenLocation encountering EOF means we failed while + // parsing a multi-token value. + return CSSParseResult::Error; + } + + return CSSParseResult::NotFound; } uint32_t variant = nsCSSProps::ParserVariant(aPropID); if (variant == 0) { MOZ_ASSERT(false, "not a single value property"); - return false; + return CSSParseResult::NotFound; } - // We only allow 'script-level' when unsafe rules are enabled, because - // otherwise it could interfere with rulenode optimizations if used in - // a non-MathML-enabled document. We also only allow math-display when - // unsafe rules are enabled. - if (!mUnsafeRulesEnabled && - (aPropID == eCSSProperty_script_level || - aPropID == eCSSProperty_math_display)) - return false; - const KTableValue* kwtable = nsCSSProps::kKeywordTableTable[aPropID]; uint32_t restrictions = nsCSSProps::ValueRestrictions(aPropID); return ParseVariantWithRestrictions(aValue, variant, kwtable, restrictions); @@ -10592,8 +10840,8 @@ CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID, case eCSSFontDesc_Style: // property is VARIANT_HMK|VARIANT_SYSFONT - return ParseVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL, - nsCSSProps::kFontStyleKTable); + return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD | VARIANT_NORMAL, + nsCSSProps::kFontStyleKTable); case eCSSFontDesc_Weight: return (ParseFontWeight(aValue) && @@ -10606,8 +10854,8 @@ CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID, case eCSSFontDesc_Stretch: // property is VARIANT_HK|VARIANT_SYSFONT - return ParseVariant(aValue, VARIANT_KEYWORD, - nsCSSProps::kFontStretchKTable); + return ParseSingleTokenVariant(aValue, VARIANT_KEYWORD, + nsCSSProps::kFontStretchKTable); // These two are unique to @font-face and have their own special grammar. case eCSSFontDesc_Src: @@ -10620,7 +10868,8 @@ CSSParserImpl::ParseFontDescriptorValue(nsCSSFontDesc aDescID, return ParseFontFeatureSettings(aValue); case eCSSFontDesc_FontLanguageOverride: - return ParseVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, nullptr); + return ParseSingleTokenVariant(aValue, VARIANT_NORMAL | VARIANT_STRING, + nullptr); case eCSSFontDesc_UNKNOWN: case eCSSFontDesc_COUNT: @@ -10664,7 +10913,7 @@ CSSParserImpl::ParseBackground() nsCSSValue color; // Check first for inherit/initial/unset. - if (ParseVariant(color, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(color, VARIANT_INHERIT, nullptr)) { // must be alone for (const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(eCSSProperty_background); @@ -10726,6 +10975,35 @@ CSSParserImpl::ParseBackground() return true; } +// Helper for ParseBackgroundItem. Returns true if the passed-in nsCSSToken is +// a function which is accepted for background-image. +bool +CSSParserImpl::IsFunctionTokenValidForBackgroundImage( + const nsCSSToken& aToken) const +{ + MOZ_ASSERT(aToken.mType == eCSSToken_Function, + "Should only be called for function-typed tokens"); + + const nsAString& funcName = aToken.mIdent; + + return funcName.LowerCaseEqualsLiteral("linear-gradient") || + funcName.LowerCaseEqualsLiteral("radial-gradient") || + funcName.LowerCaseEqualsLiteral("repeating-linear-gradient") || + funcName.LowerCaseEqualsLiteral("repeating-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-moz-image-rect") || + funcName.LowerCaseEqualsLiteral("-moz-element") || + ((sWebkitPrefixedAliasesEnabled || ShouldUseUnprefixingService()) && + (funcName.LowerCaseEqualsLiteral("-webkit-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-radial-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-repeating-linear-gradient") || + funcName.LowerCaseEqualsLiteral("-webkit-repeating-radial-gradient"))); +} + // Parse one item of the background shorthand property. bool CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) @@ -10778,8 +11056,9 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) if (haveImage) return false; haveImage = true; - if (!ParseSingleValueProperty(aState.mImage->mValue, - eCSSProperty_background_image)) { + if (ParseSingleValueProperty(aState.mImage->mValue, + eCSSProperty_background_image) != + CSSParseResult::Ok) { NS_NOTREACHED("should be able to parse"); return false; } @@ -10788,8 +11067,9 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) if (haveAttach) return false; haveAttach = true; - if (!ParseSingleValueProperty(aState.mAttachment->mValue, - eCSSProperty_background_attachment)) { + if (ParseSingleValueProperty(aState.mAttachment->mValue, + eCSSProperty_background_attachment) != + CSSParseResult::Ok) { NS_NOTREACHED("should be able to parse"); return false; } @@ -10826,8 +11106,9 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) if (haveOrigin) return false; haveOrigin = true; - if (!ParseSingleValueProperty(aState.mOrigin->mValue, - eCSSProperty_background_origin)) { + if (ParseSingleValueProperty(aState.mOrigin->mValue, + eCSSProperty_background_origin) != + CSSParseResult::Ok) { NS_NOTREACHED("should be able to parse"); return false; } @@ -10850,8 +11131,13 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) NS_STYLE_BG_ORIGIN_CONTENT, "bg-clip and bg-origin style constants must agree"); - if (!ParseSingleValueProperty(aState.mClip->mValue, - eCSSProperty_background_clip)) { + CSSParseResult result = + ParseSingleValueProperty(aState.mClip->mValue, + eCSSProperty_background_clip); + MOZ_ASSERT(result != CSSParseResult::Error, + "how can failing to parse a single background-clip value " + "consume tokens?"); + if (result == CSSParseResult::NotFound) { // When exactly one value is set, it is used for both // 'background-origin' and 'background-clip'. // See assertions above showing these values are compatible. @@ -10861,32 +11147,21 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) if (haveColor) return false; haveColor = true; - if (!ParseSingleValueProperty(aState.mColor, - eCSSProperty_background_color)) { + if (ParseSingleValueProperty(aState.mColor, + eCSSProperty_background_color) != + CSSParseResult::Ok) { return false; } } } else if (tt == eCSSToken_URL || (tt == eCSSToken_Function && - (mToken.mIdent.LowerCaseEqualsLiteral("linear-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("radial-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("repeating-linear-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("repeating-radial-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("-moz-linear-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("-moz-radial-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-linear-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("-moz-repeating-radial-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("-moz-image-rect") || - mToken.mIdent.LowerCaseEqualsLiteral("-moz-element") || - (ShouldUseUnprefixingService() && - (mToken.mIdent.LowerCaseEqualsLiteral("-webkit-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("-webkit-linear-gradient") || - mToken.mIdent.LowerCaseEqualsLiteral("-webkit-radial-gradient")))))) { + IsFunctionTokenValidForBackgroundImage(mToken))) { if (haveImage) return false; haveImage = true; - if (!ParseSingleValueProperty(aState.mImage->mValue, - eCSSProperty_background_image)) { + if (ParseSingleValueProperty(aState.mImage->mValue, + eCSSProperty_background_image) != + CSSParseResult::Ok) { return false; } } else if (tt == eCSSToken_Dimension || @@ -10915,8 +11190,9 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundParseState& aState) haveColor = true; // Note: This parses 'inherit', 'initial' and 'unset', but // we've already checked for them, so it's ok. - if (!ParseSingleValueProperty(aState.mColor, - eCSSProperty_background_color)) { + if (ParseSingleValueProperty(aState.mColor, + eCSSProperty_background_color) != + CSSParseResult::Ok) { return false; } } @@ -10934,10 +11210,11 @@ CSSParserImpl::ParseValueList(nsCSSProperty aPropID) // aPropID is a single value prop-id nsCSSValue value; // 'initial', 'inherit' and 'unset' stand alone, no list permitted. - if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { nsCSSValueList* item = value.SetListValue(); for (;;) { - if (!ParseSingleValueProperty(item->mValue, aPropID)) { + if (ParseSingleValueProperty(item->mValue, aPropID) != + CSSParseResult::Ok) { return false; } if (!ExpectSymbol(',', true)) { @@ -10956,7 +11233,7 @@ CSSParserImpl::ParseBackgroundRepeat() { nsCSSValue value; // 'initial', 'inherit' and 'unset' stand alone, no list permitted. - if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { nsCSSValuePair valuePair; if (!ParseBackgroundRepeatValues(valuePair)) { return false; @@ -11009,7 +11286,7 @@ CSSParserImpl::ParseBackgroundPosition() { nsCSSValue value; // 'initial', 'inherit' and 'unset' stand alone, no list permitted. - if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { nsCSSValue itemValue; if (!ParsePositionValue(itemValue)) { return false; @@ -11057,7 +11334,10 @@ bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut, &yValue = aOut.mYValue; int32_t variantMask = (aAcceptsInherit ? VARIANT_INHERIT : 0) | VARIANT_LP | VARIANT_CALC; - if (ParseVariant(xValue, variantMask, nullptr)) { + CSSParseResult result = ParseVariant(xValue, variantMask, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { if (eCSSUnit_Inherit == xValue.GetUnit() || eCSSUnit_Initial == xValue.GetUnit() || eCSSUnit_Unset == xValue.GetUnit()) { // both are inherit, initial or unset @@ -11066,7 +11346,10 @@ bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut, } // We have one percentage/length/calc. Get the optional second // percentage/length/calc/keyword. - if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) { + result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { // We have two numbers return true; } @@ -11107,7 +11390,10 @@ bool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut, } else { // Only one keyword. See if we have a length, percentage, or calc. - if (ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr)) { + result = ParseVariant(yValue, VARIANT_LP | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { if (!(mask & BG_CLR)) { // The first keyword can only be 'center', 'left', or 'right' return false; @@ -11150,8 +11436,12 @@ CSSParserImpl::ParsePositionValue(nsCSSValue& aOut) // Parse all the values into the array. uint32_t valueCount = 0; for (int32_t i = 0; i < 4; i++) { - if (!ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD, - nsCSSProps::kBackgroundPositionKTable)) { + CSSParseResult result = + ParseVariant(value->Item(i), VARIANT_LPCALC | VARIANT_KEYWORD, + nsCSSProps::kBackgroundPositionKTable); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { break; } ++valueCount; @@ -11314,7 +11604,7 @@ CSSParserImpl::ParseBackgroundSize() { nsCSSValue value; // 'initial', 'inherit' and 'unset' stand alone, no list permitted. - if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { nsCSSValuePair valuePair; if (!ParseBackgroundSizeValues(valuePair)) { return false; @@ -11353,10 +11643,17 @@ bool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut) // First try a percentage or a length value nsCSSValue &xValue = aOut.mXValue, &yValue = aOut.mYValue; - if (ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr)) { + CSSParseResult result = + ParseNonNegativeVariant(xValue, BG_SIZE_VARIANT, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { // We have one percentage/length/calc/auto. Get the optional second // percentage/length/calc/keyword. - if (ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr)) { + result = ParseNonNegativeVariant(yValue, BG_SIZE_VARIANT, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { // We have a second percentage/length/calc/auto. return true; } @@ -11430,7 +11727,8 @@ CSSParserImpl::ParseBorderImageSlice(bool aAcceptsInherit, *aConsumedTokens = true; } - if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { // Keywords "inherit", "initial" and "unset" can not be mixed, so we // are done. AppendValue(eCSSProperty_border_image_slice, value); @@ -11479,7 +11777,8 @@ CSSParserImpl::ParseBorderImageWidth(bool aAcceptsInherit) // border-image-width: initial | [|||auto]{1,4} nsCSSValue value; - if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { // Keywords "inherit", "initial" and "unset" can not be mixed, so we // are done. AppendValue(eCSSProperty_border_image_width, value); @@ -11501,7 +11800,8 @@ CSSParserImpl::ParseBorderImageOutset(bool aAcceptsInherit) // border-image-outset: initial | [|]{1,4} nsCSSValue value; - if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { // Keywords "inherit", "initial" and "unset" can not be mixed, so we // are done. AppendValue(eCSSProperty_border_image_outset, value); @@ -11521,7 +11821,8 @@ bool CSSParserImpl::ParseBorderImageRepeat(bool aAcceptsInherit) { nsCSSValue value; - if (aAcceptsInherit && ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (aAcceptsInherit && + ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { // Keywords "inherit", "initial" and "unset" can not be mixed, so we // are done. AppendValue(eCSSProperty_border_image_repeat, value); @@ -11556,7 +11857,7 @@ CSSParserImpl::ParseBorderImage() // nsCSSValue value; - if (ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { AppendValue(eCSSProperty_border_image_source, value); AppendValue(eCSSProperty_border_image_slice, value); AppendValue(eCSSProperty_border_image_width, value); @@ -11584,11 +11885,13 @@ CSSParserImpl::ParseBorderImage() while (!CheckEndProperty()) { // if (!foundSource) { - nsAutoCSSParserInputStateRestorer stateRestorer(this); - if (ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr)) { + CSSParseResult result = + ParseVariant(imageSourceValue, VARIANT_IMAGE, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { AppendValue(eCSSProperty_border_image_source, imageSourceValue); foundSource = true; - stateRestorer.DoNotRestore(); continue; } } @@ -11645,14 +11948,18 @@ bool CSSParserImpl::ParseBorderSpacing() { nsCSSValue xValue, yValue; - if (!ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr)) { + if (ParseNonNegativeVariant(xValue, VARIANT_HL | VARIANT_CALC, nullptr) != + CSSParseResult::Ok) { return false; } // If we have one length, get the optional second length. // set the second value equal to the first. if (xValue.IsLengthUnit() || xValue.IsCalcUnit()) { - ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, nullptr); + if (ParseNonNegativeVariant(yValue, VARIANT_LENGTH | VARIANT_CALC, + nullptr) == CSSParseResult::Error) { + return false; + } } if (yValue == xValue || yValue.GetUnit() == eCSSUnit_Null) { @@ -11753,11 +12060,12 @@ CSSParserImpl::ParseBorderColors(nsCSSProperty aProperty) { nsCSSValue value; // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own - if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { nsCSSValueList *cur = value.SetListValue(); for (;;) { - if (!ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD, - nsCSSProps::kBorderColorKTable)) { + if (ParseVariant(cur->mValue, VARIANT_COLOR | VARIANT_KEYWORD, + nsCSSProps::kBorderColorKTable) != CSSParseResult::Ok) { return false; } if (CheckEndProperty()) { @@ -11996,7 +12304,8 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask) UngetToken(); // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero // always gets picked up - if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) { + if (ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr) != + CSSParseResult::Ok) { return false; } // ...and do the VARIANT_NUMBER check ourselves. @@ -12037,7 +12346,7 @@ bool CSSParserImpl::ParseRect(nsCSSProperty aPropID) { nsCSSValue val; - if (ParseVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) { + if (ParseSingleTokenVariant(val, VARIANT_INHERIT | VARIANT_AUTO, nullptr)) { AppendValue(aPropID, val); return true; } @@ -12051,8 +12360,8 @@ CSSParserImpl::ParseRect(nsCSSProperty aPropID) nsCSSRect& rect = val.SetRectValue(); bool useCommas; NS_FOR_CSS_SIDES(side) { - if (! ParseVariant(rect.*(nsCSSRect::sides[side]), - VARIANT_AL, nullptr)) { + if (!ParseSingleTokenVariant(rect.*(nsCSSRect::sides[side]), + VARIANT_AL, nullptr)) { return false; } if (side == 0) { @@ -12152,11 +12461,12 @@ CSSParserImpl::ParseContent() nsCSSValue value; // 'inherit', 'initial', 'unset', 'normal', 'none', and 'alt-content' must // be alone - if (!ParseVariant(value, VARIANT_HMK | VARIANT_NONE, - kContentSolitaryKWs)) { + if (!ParseSingleTokenVariant(value, VARIANT_HMK | VARIANT_NONE, + kContentSolitaryKWs)) { nsCSSValueList* cur = value.SetListValue(); for (;;) { - if (!ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs)) { + if (ParseVariant(cur->mValue, VARIANT_CONTENT, kContentListKWs) != + CSSParseResult::Ok) { return false; } if (CheckEndProperty()) { @@ -12178,7 +12488,8 @@ CSSParserImpl::ParseCounterData(nsCSSProperty aPropID) eCSSKeyword_UNKNOWN }; nsCSSValue value; - if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { if (!GetToken(true)) { return false; } @@ -12220,10 +12531,11 @@ CSSParserImpl::ParseCursor() { nsCSSValue value; // 'inherit', 'initial' and 'unset' must be alone - if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { nsCSSValueList* cur = value.SetListValue(); for (;;) { - if (!ParseVariant(cur->mValue, VARIANT_UK, nsCSSProps::kCursorKTable)) { + if (!ParseSingleTokenVariant(cur->mValue, VARIANT_UK, + nsCSSProps::kCursorKTable)) { return false; } if (cur->mValue.GetUnit() != eCSSUnit_URL) { // keyword must be last @@ -12235,9 +12547,9 @@ CSSParserImpl::ParseCursor() val->Item(0) = cur->mValue; // Parse optional x and y position of cursor hotspot (css3-ui). - if (ParseVariant(val->Item(1), VARIANT_NUMBER, nullptr)) { + if (ParseSingleTokenVariant(val->Item(1), VARIANT_NUMBER, nullptr)) { // If we have one number, we must have two. - if (!ParseVariant(val->Item(2), VARIANT_NUMBER, nullptr)) { + if (!ParseSingleTokenVariant(val->Item(2), VARIANT_NUMBER, nullptr)) { return false; } } @@ -12259,7 +12571,7 @@ bool CSSParserImpl::ParseFont() { nsCSSValue family; - if (ParseVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) { + if (ParseSingleTokenVariant(family, VARIANT_HK, nsCSSProps::kFontKTable)) { if (eCSSUnit_Inherit == family.GetUnit() || eCSSUnit_Initial == family.GetUnit() || eCSSUnit_Unset == family.GetUnit()) { @@ -12364,17 +12676,18 @@ CSSParserImpl::ParseFont() // Get mandatory font-size nsCSSValue size; - if (! ParseNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP, - nsCSSProps::kFontSizeKTable)) { + if (!ParseSingleTokenNonNegativeVariant(size, VARIANT_KEYWORD | VARIANT_LP, + nsCSSProps::kFontSizeKTable)) { return false; } // Get optional "/" line-height nsCSSValue lineHeight; if (ExpectSymbol('/', true)) { - if (! ParseNonNegativeVariant(lineHeight, - VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL, - nullptr)) { + if (!ParseSingleTokenNonNegativeVariant(lineHeight, + VARIANT_NUMBER | VARIANT_LP | + VARIANT_NORMAL, + nullptr)) { return false; } } @@ -12423,8 +12736,8 @@ CSSParserImpl::ParseFont() bool CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue) { - if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE, - nsCSSProps::kFontSynthesisKTable)) { + if (!ParseSingleTokenVariant(aValue, VARIANT_HK | VARIANT_NONE, + nsCSSProps::kFontSynthesisKTable)) { return false; } @@ -12532,7 +12845,8 @@ CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature, bool CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { return true; } @@ -12646,7 +12960,8 @@ static const int32_t maskEastAsian[] = { bool CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { return true; } @@ -12661,7 +12976,8 @@ CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue) bool CSSParserImpl::ParseContain(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { return true; } static const int32_t maskContain[] = { MASK_END_VALUE }; @@ -12692,9 +13008,9 @@ static const int32_t maskLigatures[] = { bool CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue) { - if (ParseVariant(aValue, - VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE, - nullptr)) { + if (ParseSingleTokenVariant(aValue, + VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE, + nullptr)) { return true; } @@ -12716,7 +13032,8 @@ static const int32_t maskNumeric[] = { bool CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { return true; } @@ -12735,9 +13052,9 @@ CSSParserImpl::ParseFontVariant() nsCSSValue value; nsCSSValue normal(eCSSUnit_Normal); - if (ParseVariant(value, - VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE, - nullptr)) { + if (ParseSingleTokenVariant(value, + VARIANT_INHERIT | VARIANT_NORMAL | VARIANT_NONE, + nullptr)) { AppendValue(eCSSProperty_font_variant_ligatures, value); if (eCSSUnit_None == value.GetUnit()) { // 'none' applies the value 'normal' to all properties other @@ -12919,8 +13236,8 @@ CSSParserImpl::ParseFontVariant() bool CSSParserImpl::ParseFontWeight(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT, - nsCSSProps::kFontWeightKTable)) { + if (ParseSingleTokenVariant(aValue, VARIANT_HKI | VARIANT_SYSFONT, + nsCSSProps::kFontWeightKTable)) { if (eCSSUnit_Integer == aValue.GetUnit()) { // ensure unit value int32_t intValue = aValue.GetIntValue(); if ((100 <= intValue) && @@ -13300,7 +13617,8 @@ ValidFontFeatureTag(const nsString& aTag) bool CSSParserImpl::ParseFontFeatureSettings(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, + nullptr)) { return true; } @@ -13411,7 +13729,8 @@ CSSParserImpl::ParseListStyle() bool CSSParserImpl::ParseListStyleType(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_STRING, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_STRING, + nullptr)) { return true; } @@ -13438,7 +13757,8 @@ CSSParserImpl::ParseMargin() bool CSSParserImpl::ParseMarks(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kPageMarksKTable)) { + if (ParseSingleTokenVariant(aValue, VARIANT_HK, + nsCSSProps::kPageMarksKTable)) { if (eCSSUnit_Enumerated == aValue.GetUnit()) { if (NS_STYLE_PAGE_MARKS_NONE != aValue.GetIntValue() && false == CheckEndProperty()) { @@ -13463,7 +13783,7 @@ bool CSSParserImpl::ParseObjectPosition() { nsCSSValue value; - if (!ParseVariant(value, VARIANT_INHERIT, nullptr) && + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr) && !ParsePositionValue(value)) { return false; } @@ -13509,7 +13829,8 @@ bool CSSParserImpl::ParseOverflow() { nsCSSValue overflow; - if (!ParseVariant(overflow, VARIANT_HK, nsCSSProps::kOverflowKTable)) { + if (!ParseSingleTokenVariant(overflow, VARIANT_HK, + nsCSSProps::kOverflowKTable)) { return false; } @@ -13548,7 +13869,7 @@ bool CSSParserImpl::ParseQuotes() { nsCSSValue value; - if (!ParseVariant(value, VARIANT_HOS, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_HOS, nullptr)) { return false; } if (value.GetUnit() == eCSSUnit_String) { @@ -13557,11 +13878,11 @@ CSSParserImpl::ParseQuotes() for (;;) { quotes->mXValue = open; // get mandatory close - if (!ParseVariant(quotes->mYValue, VARIANT_STRING, nullptr)) { + if (!ParseSingleTokenVariant(quotes->mYValue, VARIANT_STRING, nullptr)) { return false; } // look for another open - if (!ParseVariant(open, VARIANT_STRING, nullptr)) { + if (!ParseSingleTokenVariant(open, VARIANT_STRING, nullptr)) { break; } quotes->mNext = new nsCSSValuePairList; @@ -13576,11 +13897,12 @@ bool CSSParserImpl::ParseSize() { nsCSSValue width, height; - if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) { + if (!ParseSingleTokenVariant(width, VARIANT_AHKL, + nsCSSProps::kPageSizeKTable)) { return false; } if (width.IsLengthUnit()) { - ParseVariant(height, VARIANT_LENGTH, nullptr); + ParseSingleTokenVariant(height, VARIANT_LENGTH, nullptr); } if (width == height || height.GetUnit() == eCSSUnit_Null) { @@ -13632,13 +13954,13 @@ CSSParserImpl::ParseTextDecoration() bool CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[]) { - if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { // 'inherit', 'initial' and 'unset' must be alone return true; } nsCSSValue left; - if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) { + if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD, aTable)) { return false; } @@ -13648,7 +13970,7 @@ CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[]) } nsCSSValue right; - if (ParseVariant(right, VARIANT_KEYWORD, aTable)) { + if (ParseSingleTokenVariant(right, VARIANT_KEYWORD, aTable)) { // 'true' must be combined with some other value than 'true'. if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE && right.GetIntValue() == NS_STYLE_TEXT_ALIGN_UNSAFE) { @@ -13693,7 +14015,8 @@ CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue) NS_STYLE_TEXT_DECORATION_LINE_BLINK | NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS), "text decoration constants need to be bitmasks"); - if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) { + if (ParseSingleTokenVariant(aValue, VARIANT_HK, + nsCSSProps::kTextDecorationLineKTable)) { if (eCSSUnit_Enumerated == aValue.GetUnit()) { int32_t intValue = aValue.GetIntValue(); if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) { @@ -13726,19 +14049,19 @@ CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue) bool CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { // 'inherit', 'initial' and 'unset' must be alone return true; } nsCSSValue left; - if (!ParseVariant(left, VARIANT_KEYWORD | VARIANT_STRING, - nsCSSProps::kTextOverflowKTable)) + if (!ParseSingleTokenVariant(left, VARIANT_KEYWORD | VARIANT_STRING, + nsCSSProps::kTextOverflowKTable)) return false; nsCSSValue right; - if (ParseVariant(right, VARIANT_KEYWORD | VARIANT_STRING, - nsCSSProps::kTextOverflowKTable)) + if (ParseSingleTokenVariant(right, VARIANT_KEYWORD | VARIANT_STRING, + nsCSSProps::kTextOverflowKTable)) aValue.SetPairValue(left, right); else { aValue = left; @@ -13752,7 +14075,8 @@ CSSParserImpl::ParseTouchAction(nsCSSValue& aValue) // Avaliable values of property touch-action: // auto | none | [pan-x || pan-y] | manipulation - if (!ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTouchActionKTable)) { + if (!ParseSingleTokenVariant(aValue, VARIANT_HK, + nsCSSProps::kTouchActionKTable)) { return false; } @@ -13788,8 +14112,8 @@ CSSParserImpl::ParseTouchAction(nsCSSValue& aValue) bool CSSParserImpl::ParseTextCombineUpright(nsCSSValue& aValue) { - if (!ParseVariant(aValue, VARIANT_HK, - nsCSSProps::kTextCombineUprightKTable)) { + if (!ParseSingleTokenVariant(aValue, VARIANT_HK, + nsCSSProps::kTextCombineUprightKTable)) { return false; } @@ -13843,7 +14167,7 @@ CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], for (uint16_t index = 0; index < aMaxElems; ++index) { nsCSSValue newValue; int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index]; - if (!ParseVariant(newValue, m, nullptr)) { + if (ParseVariant(newValue, m, nullptr) != CSSParseResult::Ok) { break; } @@ -14135,7 +14459,7 @@ bool CSSParserImpl::ParseWillChange() VARIANT_ALL | VARIANT_AUTO; nsCSSValue value; - if (!ParseVariant(value, variantMask, nullptr)) { + if (!ParseSingleTokenVariant(value, variantMask, nullptr)) { return false; } @@ -14206,7 +14530,8 @@ bool CSSParserImpl::ParseTransform(bool aIsPrefixed) { nsCSSValue value; // 'inherit', 'initial', 'unset' and 'none' must be alone - if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { nsCSSValueSharedList* list = new nsCSSValueSharedList; value.SetSharedListValue(list); list->mHead = new nsCSSValueList; @@ -14249,8 +14574,8 @@ CSSParserImpl::ParsePolygonFunction(nsCSSValue& aValue) nsCSSValuePairList* item = coordinates.SetPairListValue(); for (;;) { nsCSSValue xValue, yValue; - if (!ParseVariant(xValue, VARIANT_LPCALC, nullptr) || - !ParseVariant(yValue, VARIANT_LPCALC, nullptr)) { + if (ParseVariant(xValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok || + ParseVariant(yValue, VARIANT_LPCALC, nullptr) != CSSParseResult::Ok) { REPORT_UNEXPECTED_TOKEN(PECoordinatePair); SkipUntil(')'); return false; @@ -14291,9 +14616,14 @@ CSSParserImpl::ParseCircleOrEllipseFunction(nsCSSKeyword aKeyword, int32_t mask = VARIANT_LPCALC | VARIANT_NONNEGATIVE_DIMENSION | VARIANT_KEYWORD; - if (ParseVariant(radiusX, mask, nsCSSProps::kShapeRadiusKTable)) { + CSSParseResult result = + ParseVariant(radiusX, mask, nsCSSProps::kShapeRadiusKTable); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::Ok) { if (aKeyword == eCSSKeyword_ellipse) { - if (!ParseVariant(radiusY, mask, nsCSSProps::kShapeRadiusKTable)) { + if (ParseVariant(radiusY, mask, nsCSSProps::kShapeRadiusKTable) != + CSSParseResult::Ok) { REPORT_UNEXPECTED_TOKEN(PEExpectedRadius); SkipUntil(')'); return false; @@ -14345,12 +14675,20 @@ CSSParserImpl::ParseInsetFunction(nsCSSValue& aValue) RefPtr functionArray = aValue.InitFunction(eCSSKeyword_inset, 5); - if (ParseVariant(functionArray->Item(1), VARIANT_LPCALC, nullptr)) { - // Consume up to 4, but only require one. - ParseVariant(functionArray->Item(2), VARIANT_LPCALC, nullptr) && - ParseVariant(functionArray->Item(3), VARIANT_LPCALC, nullptr) && - ParseVariant(functionArray->Item(4), VARIANT_LPCALC, nullptr); - } else { + int count = 0; + while (count < 4) { + CSSParseResult result = + ParseVariant(functionArray->Item(count + 1), VARIANT_LPCALC, nullptr); + if (result == CSSParseResult::Error) { + count = 0; + break; + } else if (result == CSSParseResult::NotFound) { + break; + } + ++count; + } + + if (count == 0) { REPORT_UNEXPECTED_TOKEN(PEExpectedShapeArg); SkipUntil(')'); return false; @@ -14414,7 +14752,7 @@ CSSParserImpl::ParseBasicShape(nsCSSValue& aValue, bool* aConsumedTokens) bool CSSParserImpl::ParseClipPath() { nsCSSValue value; - if (!ParseVariant(value, VARIANT_HUO, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_HUO, nullptr)) { if (!nsLayoutUtils::CSSClipPathShapesEnabled()) { // With CSS Clip Path Shapes disabled, we should only accept // SVG clipPath reference and none. @@ -14489,7 +14827,11 @@ bool CSSParserImpl::ParseTransformOrigin(bool aPerspective) value.SetPairValue(position.mXValue, position.mYValue); } else { nsCSSValue depth; - if (!ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr)) { + CSSParseResult result = + ParseVariant(depth, VARIANT_LENGTH | VARIANT_CALC, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { depth.SetFloatValue(0.0f, eCSSUnit_Pixel); } value.SetTripletValue(position.mXValue, position.mYValue, depth); @@ -14533,7 +14875,7 @@ CSSParserImpl::ParseDropShadow(nsCSSValue* aValue) bool CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue) { - if (ParseVariant(*aValue, VARIANT_URL, nullptr)) { + if (ParseSingleTokenVariant(*aValue, VARIANT_URL, nullptr)) { return true; } @@ -14655,7 +14997,8 @@ CSSParserImpl::ParseFilter() { nsCSSValue value; // 'inherit', 'initial', 'unset' and 'none' must be alone - if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { nsCSSValueList* cur = value.SetListValue(); while (cur) { if (!ParseSingleFilter(&cur->mValue)) { @@ -14683,14 +15026,17 @@ CSSParserImpl::ParseTransitionProperty() { nsCSSValue value; // 'inherit', 'initial', 'unset' and 'none' must be alone - if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { // Accept a list of arbitrary identifiers. They should be // CSS properties, but we want to accept any so that we // accept properties that we don't know about yet, e.g. // transition-property: invalid-property, left, opacity; nsCSSValueList* cur = value.SetListValue(); for (;;) { - if (!ParseVariant(cur->mValue, VARIANT_IDENTIFIER | VARIANT_ALL, nullptr)) { + if (!ParseSingleTokenVariant(cur->mValue, + VARIANT_IDENTIFIER | VARIANT_ALL, + nullptr)) { return false; } if (cur->mValue.GetUnit() == eCSSUnit_Ident) { @@ -14776,7 +15122,8 @@ CSSParserImpl::ParseTransitionStepTimingFunctionValues(nsCSSValue& aValue) RefPtr val = nsCSSValue::Array::Create(2); - if (!ParseOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, nullptr)) { + if (!ParseSingleTokenOneOrLargerVariant(val->Item(0), VARIANT_INTEGER, + nullptr)) { return false; } @@ -14837,7 +15184,7 @@ CSSParserImpl::ParseAnimationOrTransitionShorthand( // first see if 'inherit', 'initial' or 'unset' is specified. If one is, // it can be the only thing specified, so don't attempt to parse any // additional properties - if (ParseVariant(tempValue, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(tempValue, VARIANT_INHERIT, nullptr)) { for (uint32_t i = 0; i < aNumProperties; ++i) { AppendValue(aProperties[i], tempValue); } @@ -14877,7 +15224,12 @@ CSSParserImpl::ParseAnimationOrTransitionShorthand( for (uint32_t i = 0; !foundProperty && i < aNumProperties; ++i) { if (!parsedProperty[i]) { // if we haven't found this property yet, try to parse it - if (ParseSingleValueProperty(tempValue, aProperties[i])) { + CSSParseResult result = + ParseSingleValueProperty(tempValue, aProperties[i]); + if (result == CSSParseResult::Error) { + return eParseAnimationOrTransitionShorthand_Error; + } + if (result == CSSParseResult::Ok) { parsedProperty[i] = true; cur[i] = AppendValueToList(aValues[i], cur[i], tempValue); foundProperty = true; @@ -15063,14 +15415,14 @@ CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow) if (aIsBoxShadow) { // Optional inset keyword (ignore errors) - ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD, - nsCSSProps::kBoxShadowTypeKTable); + ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD, + nsCSSProps::kBoxShadowTypeKTable); } nsCSSValue xOrColor; bool haveColor = false; - if (!ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC, - nullptr)) { + if (ParseVariant(xOrColor, VARIANT_COLOR | VARIANT_LENGTH | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { return false; } if (xOrColor.IsLengthUnit() || xOrColor.IsCalcUnit()) { @@ -15085,15 +15437,15 @@ CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow) haveColor = true; // X coordinate mandatory after color - if (!ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC, - nullptr)) { + if (ParseVariant(val->Item(IndexX), VARIANT_LENGTH | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { return false; } } // Y coordinate; mandatory - if (!ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC, - nullptr)) { + if (ParseVariant(val->Item(IndexY), VARIANT_LENGTH | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { return false; } @@ -15101,27 +15453,38 @@ CSSParserImpl::ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow) // value which we must reject. If we use ParseNonNegativeVariant // we can't tell the difference between an unspecified radius // and a negative radius. - if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC, - nullptr) && - val->Item(IndexRadius).IsLengthUnit() && - val->Item(IndexRadius).GetFloatValue() < 0) { + CSSParseResult result = + ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH | VARIANT_CALC, + nullptr); + if (result == CSSParseResult::Error) { return false; + } else if (result == CSSParseResult::Ok) { + if (val->Item(IndexRadius).IsLengthUnit() && + val->Item(IndexRadius).GetFloatValue() < 0) { + return false; + } } if (aIsBoxShadow) { // Optional spread - ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, nullptr); + if (ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH | VARIANT_CALC, + nullptr) == CSSParseResult::Error) { + return false; + } } if (!haveColor) { // Optional color - ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr); + if (ParseVariant(val->Item(IndexColor), VARIANT_COLOR, nullptr) == + CSSParseResult::Error) { + return false; + } } if (aIsBoxShadow && val->Item(IndexInset).GetUnit() == eCSSUnit_Null) { // Optional inset keyword - ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD, - nsCSSProps::kBoxShadowTypeKTable); + ParseSingleTokenVariant(val->Item(IndexInset), VARIANT_KEYWORD, + nsCSSProps::kBoxShadowTypeKTable); } aValue.SetArrayValue(val, eCSSUnit_Array); @@ -15136,7 +15499,8 @@ CSSParserImpl::ParseShadowList(nsCSSProperty aProperty) nsCSSValue value; // 'inherit', 'initial', 'unset' and 'none' must be alone - if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { nsCSSValueList* cur = value.SetListValue(); for (;;) { if (!ParseShadowItem(cur->mValue, isBoxShadow)) { @@ -15188,17 +15552,22 @@ CSSParserImpl::ParsePaint(nsCSSProperty aPropID) { nsCSSValue x, y; - if (!ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL | - VARIANT_OPENTYPE_SVG_KEYWORD, - nsCSSProps::kContextPatternKTable)) { + if (ParseVariant(x, VARIANT_HC | VARIANT_NONE | VARIANT_URL | + VARIANT_OPENTYPE_SVG_KEYWORD, + nsCSSProps::kContextPatternKTable) != CSSParseResult::Ok) { return false; } bool canHaveFallback = x.GetUnit() == eCSSUnit_URL || x.GetUnit() == eCSSUnit_Enumerated; if (canHaveFallback) { - if (!ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr)) + CSSParseResult result = + ParseVariant(y, VARIANT_COLOR | VARIANT_NONE, nullptr); + if (result == CSSParseResult::Error) { + return false; + } else if (result == CSSParseResult::NotFound) { y.SetNoneValue(); + } } if (!canHaveFallback) { @@ -15217,12 +15586,13 @@ CSSParserImpl::ParseDasharray() nsCSSValue value; // 'inherit', 'initial', 'unset' and 'none' are only allowed on their own - if (!ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE | - VARIANT_OPENTYPE_SVG_KEYWORD, - nsCSSProps::kStrokeContextValueKTable)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT | VARIANT_NONE | + VARIANT_OPENTYPE_SVG_KEYWORD, + nsCSSProps::kStrokeContextValueKTable)) { nsCSSValueList *cur = value.SetListValue(); for (;;) { - if (!ParseNonNegativeVariant(cur->mValue, VARIANT_LPN, nullptr)) { + if (!ParseSingleTokenNonNegativeVariant(cur->mValue, VARIANT_LPN, + nullptr)) { return false; } if (CheckEndProperty()) { @@ -15243,7 +15613,8 @@ bool CSSParserImpl::ParseMarker() { nsCSSValue marker; - if (ParseSingleValueProperty(marker, eCSSProperty_marker_end)) { + if (ParseSingleValueProperty(marker, eCSSProperty_marker_end) == + CSSParseResult::Ok) { AppendValue(eCSSProperty_marker_end, marker); AppendValue(eCSSProperty_marker_mid, marker); AppendValue(eCSSProperty_marker_start, marker); @@ -15272,7 +15643,7 @@ CSSParserImpl::ParsePaintOrder() "missing paint-order values in kPaintOrderKTable"); nsCSSValue value; - if (!ParseVariant(value, VARIANT_HK, kPaintOrderKTable)) { + if (!ParseSingleTokenVariant(value, VARIANT_HK, kPaintOrderKTable)) { return false; } @@ -15353,7 +15724,7 @@ bool CSSParserImpl::ParseAll() { nsCSSValue value; - if (!ParseVariant(value, VARIANT_INHERIT, nullptr)) { + if (!ParseSingleTokenVariant(value, VARIANT_INHERIT, nullptr)) { return false; } @@ -15430,7 +15801,8 @@ bool CSSParserImpl::ParseScrollSnapType() { nsCSSValue value; - if (!ParseVariant(value, VARIANT_HK, nsCSSProps::kScrollSnapTypeKTable)) { + if (!ParseSingleTokenVariant(value, VARIANT_HK, + nsCSSProps::kScrollSnapTypeKTable)) { return false; } AppendValue(eCSSProperty_scroll_snap_type_x, value); @@ -15441,7 +15813,8 @@ CSSParserImpl::ParseScrollSnapType() bool CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSProperty aPropID) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { return true; } if (!GetToken(true)) { @@ -15450,9 +15823,9 @@ CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSProperty aPropID) if (mToken.mType == eCSSToken_Function && nsCSSKeywords::LookupKeyword(mToken.mIdent) == eCSSKeyword_repeat) { nsCSSValue lengthValue; - if (!ParseNonNegativeVariant(lengthValue, - VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_CALC, - nullptr)) { + if (ParseNonNegativeVariant(lengthValue, + VARIANT_LENGTH | VARIANT_PERCENT | VARIANT_CALC, + nullptr) != CSSParseResult::Ok) { REPORT_UNEXPECTED(PEExpectedNonnegativeNP); SkipUntil(')'); return false; @@ -15475,7 +15848,7 @@ CSSParserImpl::ParseScrollSnapPoints(nsCSSValue& aValue, nsCSSProperty aPropID) bool CSSParserImpl::ParseScrollSnapDestination(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT, nullptr)) { return true; } nsCSSValue itemValue; @@ -15491,7 +15864,8 @@ CSSParserImpl::ParseScrollSnapDestination(nsCSSValue& aValue) bool CSSParserImpl::ParseScrollSnapCoordinate(nsCSSValue& aValue) { - if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + if (ParseSingleTokenVariant(aValue, VARIANT_INHERIT | VARIANT_NONE, + nullptr)) { return true; } nsCSSValue itemValue; @@ -15802,6 +16176,8 @@ nsCSSParser::Startup() { Preferences::AddBoolVarCache(&sOpentypeSVGEnabled, "gfx.font_rendering.opentype_svg.enabled"); + Preferences::AddBoolVarCache(&sWebkitPrefixedAliasesEnabled, + "layout.css.prefixes.webkit"); Preferences::AddBoolVarCache(&sUnprefixingServiceEnabled, "layout.css.unprefixing-service.enabled"); #ifdef NIGHTLY_BUILD @@ -15810,6 +16186,8 @@ nsCSSParser::Startup() #endif Preferences::AddBoolVarCache(&sMozGradientsEnabled, "layout.css.prefixes.gradients"); + Preferences::AddBoolVarCache(&sControlCharVisibility, + "layout.css.control-characters.visible"); } nsCSSParser::nsCSSParser(mozilla::css::Loader* aLoader, @@ -15885,11 +16263,12 @@ nsCSSParser::ParseSheet(const nsAString& aInput, nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules) + bool aAllowUnsafeRules, + LoaderReusableStyleSheets* aReusableSheets) { return static_cast(mImpl)-> ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber, - aAllowUnsafeRules); + aAllowUnsafeRules, aReusableSheets); } nsresult @@ -15945,6 +16324,19 @@ nsCSSParser::ParseProperty(const nsCSSProperty aPropID, aIsImportant, aIsSVGMode); } +void +nsCSSParser::ParseLonghandProperty(const nsCSSProperty aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURI, + nsIURI* aBaseURI, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aResult) +{ + static_cast(mImpl)-> + ParseLonghandProperty(aPropID, aPropValue, aSheetURI, aBaseURI, + aSheetPrincipal, aResult); +} + void nsCSSParser::ParseVariable(const nsAString& aVariableName, const nsAString& aPropValue, @@ -16141,3 +16533,12 @@ nsCSSParser::IsValueValidForProperty(const nsCSSProperty aPropID, return static_cast(mImpl)-> IsValueValidForProperty(aPropID, aPropValue); } + +/* static */ +uint8_t +nsCSSParser::ControlCharVisibilityDefault() +{ + return sControlCharVisibility + ? NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE + : NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN; +} diff --git a/layout/style/nsCSSParser.h b/layout/style/nsCSSParser.h index 8be66ed8f5..34ad931a1e 100644 --- a/layout/style/nsCSSParser.h +++ b/layout/style/nsCSSParser.h @@ -33,6 +33,7 @@ namespace css { class Rule; class Declaration; class Loader; +class LoaderReusableStyleSheets; class StyleRule; } // namespace css } // namespace mozilla @@ -79,13 +80,17 @@ public: * @param aLineNumber the line number of the first line of the sheet. * @param aAllowUnsafeRules see aEnableUnsafeRules in * mozilla::css::Loader::LoadSheetSync + * @param aReusableSheets style sheets that can be reused by an @import. + * This can be nullptr. */ nsresult ParseSheet(const nsAString& aInput, nsIURI* aSheetURL, nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules); + bool aAllowUnsafeRules, + mozilla::css::LoaderReusableStyleSheets* aReusableSheets = + nullptr); // Parse HTML style attribute or its equivalent in other markup // languages. aBaseURL is the base url to use for relative links in @@ -132,6 +137,16 @@ public: bool aIsImportant, bool aIsSVGMode = false); + // Same as ParseProperty but returns an nsCSSValue in aResult + // rather than storing the property in a Declaration. aPropID + // must be a longhand property. + void ParseLonghandProperty(const nsCSSProperty aPropID, + const nsAString& aPropValue, + nsIURI* aSheetURL, + nsIURI* aBaseURL, + nsIPrincipal* aSheetPrincipal, + nsCSSValue& aResult); + // The same as ParseProperty but for a variable. void ParseVariable(const nsAString& aVariableName, const nsAString& aPropValue, @@ -309,6 +324,11 @@ public: bool IsValueValidForProperty(const nsCSSProperty aPropID, const nsAString& aPropValue); + // Return the default value to be used for -moz-control-character-visibility, + // from preferences (cached by our Startup(), so that both nsStyleText and + // nsRuleNode can have fast access to it). + static uint8_t ControlCharVisibilityDefault(); + protected: // This is a CSSParserImpl*, but if we expose that type name in this // header, we can't put the type definition (in nsCSSParser.cpp) in diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index a513d549b4..1a321cbf91 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -682,7 +682,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_AXIS | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, @@ -704,14 +704,14 @@ CSS_PROP_SHORTHAND( border_block_end, BorderBlockEnd, CSS_PROPERTY_PARSE_FUNCTION | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, "layout.css.vertical-text.enabled") CSS_PROP_SHORTHAND( border-block-start, border_block_start, BorderBlockStart, CSS_PROPERTY_PARSE_FUNCTION | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, "layout.css.vertical-text.enabled") CSS_PROP_LOGICAL( border-block-end-color, @@ -719,7 +719,7 @@ CSS_PROP_LOGICAL( BorderBlockEndColor, CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS | CSS_PROPERTY_LOGICAL_END_EDGE, @@ -736,7 +736,7 @@ CSS_PROP_LOGICAL( BorderBlockEndStyle, CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS | CSS_PROPERTY_LOGICAL_END_EDGE, @@ -755,7 +755,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS | CSS_PROPERTY_LOGICAL_END_EDGE, @@ -772,7 +772,7 @@ CSS_PROP_LOGICAL( BorderBlockStartColor, CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, "layout.css.vertical-text.enabled", @@ -788,7 +788,7 @@ CSS_PROP_LOGICAL( BorderBlockStartStyle, CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, "layout.css.vertical-text.enabled", @@ -806,7 +806,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, "layout.css.vertical-text.enabled", @@ -2181,7 +2181,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_AXIS, "layout.css.vertical-text.enabled", @@ -2286,7 +2286,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_APPLIES_TO_PAGE_RULE | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS | CSS_PROPERTY_LOGICAL_END_EDGE, @@ -2306,7 +2306,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_APPLIES_TO_PAGE_RULE | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, "layout.css.vertical-text.enabled", @@ -2436,7 +2436,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_AXIS | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, @@ -2469,7 +2469,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_AXIS, "layout.css.vertical-text.enabled", @@ -2515,7 +2515,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_AXIS | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, @@ -2534,7 +2534,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_AXIS, "layout.css.vertical-text.enabled", @@ -2585,7 +2585,7 @@ CSS_PROP_POSITION( object_fit, ObjectFit, CSS_PROPERTY_PARSE_VALUE | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, "layout.css.object-fit-and-position.enabled", VARIANT_HK, kObjectFitKTable, @@ -2597,7 +2597,7 @@ CSS_PROP_POSITION( ObjectPosition, CSS_PROPERTY_PARSE_FUNCTION | CSS_PROPERTY_STORES_CALC | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, "layout.css.object-fit-and-position.enabled", 0, kBackgroundPositionKTable, @@ -2610,7 +2610,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS | CSS_PROPERTY_LOGICAL_END_EDGE, @@ -2628,7 +2628,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, "layout.css.vertical-text.enabled", @@ -2645,7 +2645,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_END_EDGE, "layout.css.vertical-text.enabled", @@ -2662,7 +2662,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL, "layout.css.vertical-text.enabled", VARIANT_AHLP | VARIANT_CALC, @@ -2761,7 +2761,7 @@ CSS_PROP_DISPLAY( overflow_clip_box, OverflowClipBox, CSS_PROPERTY_PARSE_VALUE | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, "layout.css.overflow-clip-box.enabled", VARIANT_HK, @@ -2810,7 +2810,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS | CSS_PROPERTY_LOGICAL_END_EDGE, @@ -2832,7 +2832,7 @@ CSS_PROP_LOGICAL( CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_LOGICAL | CSS_PROPERTY_LOGICAL_BLOCK_AXIS, "layout.css.vertical-text.enabled", @@ -3276,7 +3276,7 @@ CSS_PROP_VISIBILITY( text_orientation, TextOrientation, CSS_PROPERTY_PARSE_VALUE | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, "layout.css.vertical-text.enabled", VARIANT_HK, kTextOrientationKTable, @@ -3423,7 +3423,21 @@ CSS_PROP_POSITION( nullptr, offsetof(nsStylePosition, mOffset), eStyleAnimType_Sides_Top) - CSS_PROP_DISPLAY( +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_DISPLAY( + -moz-top-layer, + _moz_top_layer, + CSS_PROP_DOMPROP_PREFIXED(TopLayer), + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "", + VARIANT_HK, + kTopLayerKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +#endif // CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_DISPLAY( touch-action, touch_action, TouchAction, @@ -3599,11 +3613,14 @@ CSS_PROP_POSITION( kWidthKTable, offsetof(nsStylePosition, mWidth), eStyleAnimType_Coord) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL CSS_PROP_USERINTERFACE( -moz-window-dragging, _moz_window_dragging, CSS_PROP_DOMPROP_PREFIXED(WindowDragging), - CSS_PROPERTY_PARSE_VALUE, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME, "", VARIANT_HK, kWindowDraggingKTable, @@ -3613,12 +3630,15 @@ CSS_PROP_UIRESET( -moz-window-shadow, _moz_window_shadow, CSS_PROP_DOMPROP_PREFIXED(WindowShadow), - CSS_PROPERTY_PARSE_VALUE, + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME, "", VARIANT_HK, kWindowShadowKTable, CSS_PROP_NO_OFFSET, eStyleAnimType_None) +#endif CSS_PROP_TEXT( word-break, word_break, @@ -3667,7 +3687,7 @@ CSS_PROP_VISIBILITY( writing_mode, WritingMode, CSS_PROPERTY_PARSE_VALUE | - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, "layout.css.vertical-text.enabled", VARIANT_HK, kWritingModeKTable, @@ -3763,10 +3783,11 @@ CSS_PROP_FONT( -moz-script-level, script_level, ScriptLevel, - // REVIEW: no range restriction? - // NOTE: CSSParserImpl::ParseSingleValueProperty only accepts this - // property when mUnsafeRulesEnabled is set. + // We only allow 'script-level' when unsafe rules are enabled, because + // otherwise it could interfere with rulenode optimizations if used in + // a non-MathML-enabled document. CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_PARSE_VALUE, "", // script-level can take Auto, Integer and Number values, but only Auto @@ -3780,7 +3801,6 @@ CSS_PROP_FONT( -moz-script-size-multiplier, script_size_multiplier, ScriptSizeMultiplier, - // REVIEW: no range restriction? CSS_PROPERTY_INTERNAL | CSS_PROPERTY_PARSE_INACCESSIBLE, "", @@ -3792,7 +3812,6 @@ CSS_PROP_FONT( -moz-script-min-size, script_min_size, ScriptMinSize, - // REVIEW: no range restriction? CSS_PROPERTY_INTERNAL | CSS_PROPERTY_PARSE_INACCESSIBLE, "", @@ -3815,9 +3834,8 @@ CSS_PROP_FONT( -moz-math-display, math_display, MathDisplay, - // NOTE: CSSParserImpl::ParseSingleValueProperty only accepts this - // property when mUnsafeRulesEnabled is set. CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_PARSE_VALUE, "", VARIANT_HK, diff --git a/layout/style/nsCSSPropertySet.h b/layout/style/nsCSSPropertySet.h index 6416df61e3..06ce4e2c5c 100644 --- a/layout/style/nsCSSPropertySet.h +++ b/layout/style/nsCSSPropertySet.h @@ -8,6 +8,7 @@ #define nsCSSPropertySet_h__ #include "mozilla/ArrayUtils.h" +#include "mozilla/PodOperations.h" #include "nsCSSProperty.h" #include // for CHAR_BIT @@ -62,6 +63,10 @@ public: } } + bool Equals(const nsCSSPropertySet& aOther) const { + return mozilla::PodEqual(mProperties, aOther.mProperties); + } + private: typedef unsigned long property_set_type; public: diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index ceedcd5b16..7739e70157 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -28,6 +28,35 @@ using namespace mozilla; typedef nsCSSProps::KTableValue KTableValue; +// MSVC before 2015 doesn't consider string literal as a constant +// expression, thus we are not able to do this check here. +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +// By wrapping internal-only properties in this macro, we are not +// exposing them in the CSSOM. Since currently it is not necessary to +// allow accessing them in that way, it is easier and cheaper to just +// do this rather than exposing them conditionally. +#define CSS_PROP(name_, id_, method_, flags_, pref_, ...) \ + static_assert(!((flags_) & CSS_PROPERTY_ENABLED_MASK) || pref_[0], \ + "Internal-only property '" #name_ "' should be wrapped in " \ + "#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL"); +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#define CSS_PROP_LIST_EXCLUDE_INTERNAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_EXCLUDE_INTERNAL +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP +#endif + +#define CSS_PROP(name_, id_, method_, flags_, pref_, ...) \ + static_assert(!((flags_) & CSS_PROPERTY_ENABLED_IN_CHROME) || \ + ((flags_) & CSS_PROPERTY_ENABLED_IN_UA_SHEETS), \ + "Property '" #name_ "' is enabled in chrome, so it should " \ + "also be enabled in UA sheets"); +#define CSS_PROP_LIST_INCLUDE_LOGICAL +#include "nsCSSPropList.h" +#undef CSS_PROP_LIST_INCLUDE_LOGICAL +#undef CSS_PROP + // required to make the symbol external, so that TestCSSPropertyLookup.cpp can link with it extern const char* const kCSSRawProperties[]; @@ -55,6 +84,7 @@ static nsStaticCaseInsensitiveNameTable* gPropertyTable; static nsStaticCaseInsensitiveNameTable* gFontDescTable; static nsStaticCaseInsensitiveNameTable* gCounterDescTable; static nsStaticCaseInsensitiveNameTable* gPredefinedCounterStyleTable; +static nsDataHashtable* gPropertyIDLNameTable; /* static */ nsCSSProperty * nsCSSProps::gShorthandsContainingTable[eCSSProperty_COUNT_no_shorthands]; @@ -139,6 +169,7 @@ nsCSSProps::AddRefTable(void) MOZ_ASSERT(!gFontDescTable, "pre existing array!"); MOZ_ASSERT(!gCounterDescTable, "pre existing array!"); MOZ_ASSERT(!gPredefinedCounterStyleTable, "pre existing array!"); + MOZ_ASSERT(!gPropertyIDLNameTable, "pre existing array!"); gPropertyTable = CreateStaticTable( kCSSRawProperties, eCSSProperty_COUNT_with_aliases); @@ -149,6 +180,15 @@ nsCSSProps::AddRefTable(void) kCSSRawPredefinedCounterStyles, ArrayLength(kCSSRawPredefinedCounterStyles)); + gPropertyIDLNameTable = new nsDataHashtable; + for (nsCSSProperty p = nsCSSProperty(0); + size_t(p) < ArrayLength(kIDLNameTable); + p = nsCSSProperty(p + 1)) { + if (kIDLNameTable[p]) { + gPropertyIDLNameTable->Put(nsDependentCString(kIDLNameTable[p]), p); + } + } + BuildShorthandsContainingTable(); static bool prefObserversInited = false; @@ -184,11 +224,12 @@ nsCSSProps::AddRefTable(void) #ifdef DEBUG { - // Assert that if CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS is used - // on a shorthand property that all of its component longhands - // also has the flag. + // Assert that if CSS_PROPERTY_ENABLED_IN_UA_SHEETS or + // CSS_PROPERTY_ENABLED_IN_CHROME is used on a shorthand property + // that all of its component longhands also have the flag. static uint32_t flagsToCheck[] = { - CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + CSS_PROPERTY_ENABLED_IN_CHROME }; for (nsCSSProperty shorthand = eCSSProperty_COUNT_no_shorthands; shorthand < eCSSProperty_COUNT; @@ -204,7 +245,7 @@ nsCSSProps::AddRefTable(void) ++p) { MOZ_ASSERT(nsCSSProps::PropHasFlags(*p, flag), "all subproperties of a property with a " - "CSS_PROPERTY_ALWAYS_ENABLED_* flag must also have " + "CSS_PROPERTY_ENABLED_* flag must also have " "the flag"); } } @@ -429,6 +470,9 @@ nsCSSProps::ReleaseTable(void) delete gPredefinedCounterStyleTable; gPredefinedCounterStyleTable = nullptr; + delete gPropertyIDLNameTable; + gPropertyIDLNameTable = nullptr; + delete [] gShorthandsContainingPool; gShorthandsContainingPool = nullptr; } @@ -478,7 +522,7 @@ nsCSSProps::LookupProperty(const nsACString& aProperty, } MOZ_ASSERT(eCSSAliasCount != 0, "'res' must be an alias at this point so we better have some!"); - // We intentionally don't support eEnabledInUASheets or eEnabledInChromeOrCertifiedApp + // We intentionally don't support eEnabledInUASheets or eEnabledInChrome // for aliases yet because it's unlikely there will be a need for it. if (IsEnabled(res) || aEnabled == eIgnoreEnabledState) { res = gAliases[res - eCSSProperty_COUNT]; @@ -512,8 +556,8 @@ nsCSSProps::LookupProperty(const nsAString& aProperty, EnabledState aEnabled) } MOZ_ASSERT(eCSSAliasCount != 0, "'res' must be an alias at this point so we better have some!"); - // We intentionally don't support eEnabledInUASheets for aliases yet - // because it's unlikely there will be a need for it. + // We intentionally don't support eEnabledInUASheets or eEnabledInChrome + // for aliases yet because it's unlikely there will be a need for it. if (IsEnabled(res) || aEnabled == eIgnoreEnabledState) { res = gAliases[res - eCSSProperty_COUNT]; MOZ_ASSERT(0 <= res && res < eCSSProperty_COUNT, @@ -525,6 +569,30 @@ nsCSSProps::LookupProperty(const nsAString& aProperty, EnabledState aEnabled) return eCSSProperty_UNKNOWN; } +nsCSSProperty +nsCSSProps::LookupPropertyByIDLName(const nsACString& aPropertyIDLName, + EnabledState aEnabled) +{ + nsCSSProperty res; + if (!gPropertyIDLNameTable->Get(aPropertyIDLName, &res)) { + return eCSSProperty_UNKNOWN; + } + MOZ_ASSERT(res < eCSSProperty_COUNT); + if (!IsEnabled(res, aEnabled)) { + return eCSSProperty_UNKNOWN; + } + return res; +} + +nsCSSProperty +nsCSSProps::LookupPropertyByIDLName(const nsAString& aPropertyIDLName, + EnabledState aEnabled) +{ + MOZ_ASSERT(gPropertyIDLNameTable, "no lookup table, needs addref"); + return LookupPropertyByIDLName(NS_ConvertUTF16toUTF8(aPropertyIDLName), + aEnabled); +} + nsCSSFontDesc nsCSSProps::LookupFontDesc(const nsACString& aFontDesc) { @@ -1855,6 +1923,12 @@ const KTableValue nsCSSProps::kTouchActionKTable[] = { eCSSKeyword_UNKNOWN, -1 }; +const KTableValue nsCSSProps::kTopLayerKTable[] = { + eCSSKeyword_none, NS_STYLE_TOP_LAYER_NONE, + eCSSKeyword_top, NS_STYLE_TOP_LAYER_TOP, + eCSSKeyword_UNKNOWN, -1 +}; + const KTableValue nsCSSProps::kTransformBoxKTable[] = { eCSSKeyword_border_box, NS_STYLE_TRANSFORM_BOX_BORDER_BOX, eCSSKeyword_fill_box, NS_STYLE_TRANSFORM_BOX_FILL_BOX, @@ -3005,16 +3079,24 @@ nsCSSProps::gPropertyIndexInStruct[eCSSProperty_COUNT_no_shorthands] = { /* static */ bool nsCSSProps::gPropertyEnabled[eCSSProperty_COUNT_with_aliases] = { + // If the property has any "ENABLED_IN" flag set, it is disabled by + // default. Note that, if a property has pref, whatever its default + // value is, it will later be changed in nsCSSProps::AddRefTable(). + // If the property has "ENABLED_IN" flags but doesn't have a pref, + // it is an internal property which is disabled elsewhere. + #define IS_ENABLED_BY_DEFAULT(flags_) \ + (!((flags_) & CSS_PROPERTY_ENABLED_MASK)) + #define CSS_PROP(name_, id_, method_, flags_, pref_, parsevariant_, \ kwtable_, stylestruct_, stylestructoffset_, animtype_) \ - true, + IS_ENABLED_BY_DEFAULT(flags_), #define CSS_PROP_LIST_INCLUDE_LOGICAL #include "nsCSSPropList.h" #undef CSS_PROP_LIST_INCLUDE_LOGICAL #undef CSS_PROP #define CSS_PROP_SHORTHAND(name_, id_, method_, flags_, pref_) \ - true, + IS_ENABLED_BY_DEFAULT(flags_), #include "nsCSSPropList.h" #undef CSS_PROP_SHORTHAND @@ -3022,6 +3104,8 @@ nsCSSProps::gPropertyEnabled[eCSSProperty_COUNT_with_aliases] = { true, #include "nsCSSPropAliasList.h" #undef CSS_PROP_ALIAS + + #undef IS_ENABLED_BY_DEFAULT }; #include "../../dom/base/PropertyUseCounterMap.inc" diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index c11cc9c0b8..592d8c3733 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -56,6 +56,17 @@ // Keyword used iff gfx.font_rendering.opentype_svg.enabled is true: #define VARIANT_OPENTYPE_SVG_KEYWORD 0x40000000 +// Variants that can consume more than one token +#define VARIANT_MULTIPLE_TOKENS \ + (VARIANT_COLOR | /* rgb(...), hsl(...), etc. */ \ + VARIANT_COUNTER | /* counter(...), counters(...) */ \ + VARIANT_ATTR | /* attr(...) */ \ + VARIANT_GRADIENT | /* linear-gradient(...), etc. */ \ + VARIANT_TIMING_FUNCTION | /* cubic-bezier(...), steps(...) */ \ + VARIANT_IMAGE_RECT | /* -moz-image-rect(...) */ \ + VARIANT_CALC | /* calc(...) */ \ + VARIANT_ELEMENT) /* -moz-element(...) */ + // Common combinations of variants #define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH) #define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT) @@ -200,14 +211,23 @@ static_assert((CSS_PROPERTY_PARSE_PROPERTY_MASK & // This property requires a stacking context. #define CSS_PROPERTY_CREATES_STACKING_CONTEXT (1<<21) -// This property is always enabled in UA sheets. This is meant to be used -// together with a pref that enables the property for non-UA sheets. -// Note that if such a property has an alias, then any use of that alias -// in an UA sheet will still be ignored unless the pref is enabled. -// In other words, this bit has no effect on the use of aliases. -#define CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS (1<<22) - -// XXX (1<<23) will shortly be reused in bug 1069192. +// The following two flags along with the pref defines where the this +// property can be used: +// * If none of the two flags is presented, the pref completely controls +// the availability of this property. And in that case, if it has no +// pref, this property is usable everywhere. +// * If any of the flags is set, this property is always enabled in the +// specific contexts regardless of the value of the pref. If there is +// no pref for this property at all in this case, it is an internal- +// only property, which cannot be used anywhere else, and should be +// wrapped in "#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL". +// Note that, these flags have no effect on the use of aliases of this +// property. +#define CSS_PROPERTY_ENABLED_MASK (3<<22) +#define CSS_PROPERTY_ENABLED_IN_UA_SHEETS (1<<22) +#define CSS_PROPERTY_ENABLED_IN_CHROME (1<<23) +#define CSS_PROPERTY_ENABLED_IN_UA_SHEETS_AND_CHROME \ + (CSS_PROPERTY_ENABLED_IN_UA_SHEETS | CSS_PROPERTY_ENABLED_IN_CHROME) // This property's unitless values are pixels. #define CSS_PROPERTY_NUMBERS_ARE_PIXELS (1<<24) @@ -298,6 +318,8 @@ public: eEnabledForAllContent = 0, // Enable a property in UA sheets. eEnabledInUASheets = 0x01, + // Enable a property in chrome code. + eEnabledInChrome = 0x02, // Special value to unconditionally enable a property. This implies all the // bits above, but is strictly more than just their OR-ed union. // This just skips any test so a property will be enabled even if it would @@ -312,6 +334,15 @@ public: EnabledState aEnabled); static nsCSSProperty LookupProperty(const nsACString& aProperty, EnabledState aEnabled); + // As above, but looked up using a property's IDL name. + // eCSSPropertyExtra_variable won't be returned from these methods. + static nsCSSProperty LookupPropertyByIDLName( + const nsAString& aPropertyIDLName, + EnabledState aEnabled); + static nsCSSProperty LookupPropertyByIDLName( + const nsACString& aPropertyIDLName, + EnabledState aEnabled); + // Returns whether aProperty is a custom property name, i.e. begins with // "--". This assumes that the CSS Variables pref has been enabled. static bool IsCustomPropertyName(const nsAString& aProperty); @@ -543,15 +574,12 @@ public: return kIDLNameSortPositionTable[aProperty]; } -public: - static bool IsEnabled(nsCSSProperty aProperty) { MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_with_aliases, "out of range"); return gPropertyEnabled[aProperty]; } -private: // A table for the use counter associated with each CSS property. If a // property does not have a use counter defined in UseCounters.conf, then // its associated entry is |eUseCounter_UNKNOWN|. @@ -574,7 +602,12 @@ public: return true; } if ((aEnabled & eEnabledInUASheets) && - PropHasFlags(aProperty, CSS_PROPERTY_ALWAYS_ENABLED_IN_UA_SHEETS)) + PropHasFlags(aProperty, CSS_PROPERTY_ENABLED_IN_UA_SHEETS)) + { + return true; + } + if ((aEnabled & eEnabledInChrome) && + PropHasFlags(aProperty, CSS_PROPERTY_ENABLED_IN_CHROME)) { return true; } @@ -745,6 +778,7 @@ public: static const KTableValue kTextOverflowKTable[]; static const KTableValue kTextTransformKTable[]; static const KTableValue kTouchActionKTable[]; + static const KTableValue kTopLayerKTable[]; static const KTableValue kTransformBoxKTable[]; static const KTableValue kTransitionTimingFunctionKTable[]; static const KTableValue kUnicodeBidiKTable[]; diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index ac658c422a..ed8650b4f1 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -122,6 +122,9 @@ CSS_PSEUDO_CLASS(mozWindowInactive, ":-moz-window-inactive", 0, "") // according to HTML integer attribute parsing rules. CSS_PSEUDO_CLASS(mozTableBorderNonzero, ":-moz-table-border-nonzero", 0, "") +// Matches HTML frame/iframe elements which are mozbrowser. +CSS_PSEUDO_CLASS(mozBrowserFrame, ":-moz-browser-frame", 0, "") + // Matches whatever the contextual reference elements are for the // matching operation. CSS_PSEUDO_CLASS(scope, ":scope", 0, "layout.css.scope-pseudo.enabled") diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 4d2aa1ae85..19c4470453 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -56,6 +56,7 @@ #include "mozilla/TypedEnumBits.h" #include "RuleProcessorCache.h" #include "nsIDOMMutationEvent.h" +#include "nsIMozBrowserFrame.h" using namespace mozilla; using namespace mozilla::dom; @@ -1013,7 +1014,7 @@ RuleCascadeData::AttributeListFor(nsIAtom* aAttribute) // nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets, - uint8_t aSheetType, + SheetType aSheetType, Element* aScopeElement, nsCSSRuleProcessor* aPreviousCSSRuleProcessor, @@ -1034,7 +1035,7 @@ nsCSSRuleProcessor::nsCSSRuleProcessor(const sheet_array_type& aSheets, , mDocumentRulesAndCacheKeyValid(false) #endif { - NS_ASSERTION(!!mScopeElement == (aSheetType == nsStyleSet::eScopedDocSheet), + NS_ASSERTION(!!mScopeElement == (aSheetType == SheetType::ScopedDoc), "aScopeElement must be specified iff aSheetType is " "eScopedDocSheet"); for (sheet_array_type::size_type i = mSheets.Length(); i-- != 0; ) { @@ -2144,6 +2145,17 @@ static bool SelectorMatches(Element* aElement, } break; + case nsCSSPseudoClasses::ePseudoClass_mozBrowserFrame: + { + nsCOMPtr + browserFrame = do_QueryInterface(aElement); + if (!browserFrame || + !browserFrame->GetReallyIsBrowserOrApp()) { + return false; + } + } + break; + case nsCSSPseudoClasses::ePseudoClass_mozDir: case nsCSSPseudoClasses::ePseudoClass_dir: { @@ -3426,7 +3438,7 @@ struct CascadeEnumData { nsTArray& aDocumentRules, nsMediaQueryResultCacheKey& aKey, nsDocumentRuleResultCacheKey& aDocumentKey, - uint8_t aSheetType, + SheetType aSheetType, bool aMustGatherDocumentRules) : mPresContext(aPresContext), mFontFaceRules(aFontFaceRules), @@ -3464,7 +3476,7 @@ struct CascadeEnumData { // Hooray, a manual PLDHashTable since nsClassHashtable doesn't // provide a getter that gives me a *reference* to the value. PLDHashTable mRulesByWeight; // of PerWeightDataListItem linked lists - uint8_t mSheetType; + SheetType mSheetType; bool mMustGatherDocumentRules; }; diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index 18d76f1450..022c57a7eb 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -15,14 +15,15 @@ #include "mozilla/Attributes.h" #include "mozilla/EventStates.h" #include "mozilla/MemoryReporting.h" -#include "nsIStyleRuleProcessor.h" -#include "nsIMediaList.h" -#include "nsTArray.h" +#include "mozilla/RefCountType.h" +#include "mozilla/SheetType.h" +#include "mozilla/UniquePtr.h" #include "nsAutoPtr.h" #include "nsExpirationTracker.h" +#include "nsIMediaList.h" +#include "nsIStyleRuleProcessor.h" #include "nsRuleWalker.h" -#include "mozilla/RefCountType.h" -#include "mozilla/UniquePtr.h" +#include "nsTArray.h" struct CascadeEnumData; struct ElementDependentRuleProcessorData; @@ -59,11 +60,11 @@ public: typedef nsTArray> sheet_array_type; // aScopeElement must be non-null iff aSheetType is - // nsStyleSet::eScopedDocSheet. + // SheetType::ScopedDoc. // aPreviousCSSRuleProcessor is the rule processor (if any) that this // one is replacing. nsCSSRuleProcessor(const sheet_array_type& aSheets, - uint8_t aSheetType, + mozilla::SheetType aSheetType, mozilla::dom::Element* aScopeElement, nsCSSRuleProcessor* aPreviousCSSRuleProcessor, bool aIsShared = false); @@ -262,7 +263,7 @@ private: MozRefCountType mStyleSetRefCnt; // type of stylesheet using this processor - uint8_t mSheetType; // == nsStyleSet::sheetType + mozilla::SheetType mSheetType; const bool mIsShared; diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index f9d8717db8..edc412bde6 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -2235,8 +2235,6 @@ nsCSSKeyframeRule::ChangeDeclaration(css::Declaration* aDeclaration) nsIDocument* doc = GetDocument(); MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); - // Be careful to not assign to an nsAutoPtr if we would be assigning - // the thing it already holds. if (aDeclaration != mDeclaration) { mDeclaration = aDeclaration; } @@ -2739,8 +2737,6 @@ void nsCSSPageRule::ChangeDeclaration(css::Declaration* aDeclaration) { mImportantRule = nullptr; - // Be careful to not assign to an nsAutoPtr if we would be assigning - // the thing it already holds. if (aDeclaration != mDeclaration) { mDeclaration = aDeclaration; } diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index e23dc91e2c..3efc67f922 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -9,12 +9,19 @@ #ifndef nsCSSRules_h_ #define nsCSSRules_h_ +#include "Declaration.h" +#include "StyleRule.h" +#include "gfxFontFeatures.h" #include "mozilla/Attributes.h" -#include "mozilla/Move.h" - #include "mozilla/MemoryReporting.h" +#include "mozilla/Move.h" +#include "mozilla/SheetType.h" #include "mozilla/css/GroupRule.h" #include "mozilla/dom/FontFace.h" +#include "nsAutoPtr.h" +#include "nsCSSProperty.h" +#include "nsCSSValue.h" +#include "nsDOMCSSDeclaration.h" #include "nsIDOMCSSConditionRule.h" #include "nsIDOMCSSCounterStyleRule.h" #include "nsIDOMCSSFontFaceRule.h" @@ -22,18 +29,11 @@ #include "nsIDOMCSSGroupingRule.h" #include "nsIDOMCSSMediaRule.h" #include "nsIDOMCSSMozDocumentRule.h" +#include "nsIDOMCSSPageRule.h" #include "nsIDOMCSSSupportsRule.h" #include "nsIDOMMozCSSKeyframeRule.h" #include "nsIDOMMozCSSKeyframesRule.h" -#include "nsAutoPtr.h" -#include "nsCSSProperty.h" -#include "nsCSSValue.h" #include "nsTArray.h" -#include "nsDOMCSSDeclaration.h" -#include "Declaration.h" -#include "nsIDOMCSSPageRule.h" -#include "StyleRule.h" -#include "gfxFontFeatures.h" class nsMediaList; @@ -287,7 +287,7 @@ protected: // specific @font-face rules struct nsFontFaceRuleContainer { RefPtr mRule; - uint8_t mSheetType; + mozilla::SheetType mSheetType; }; inline nsCSSFontFaceRule* @@ -388,12 +388,12 @@ class nsCSSKeyframeRule final : public mozilla::css::Rule, public nsIDOMMozCSSKeyframeRule { public: - // WARNING: Steals the contents of aKeys *and* aDeclaration + // WARNING: Steals the contents of aKeys nsCSSKeyframeRule(InfallibleTArray& aKeys, - nsAutoPtr&& aDeclaration, + mozilla::css::Declaration* aDeclaration, uint32_t aLineNumber, uint32_t aColumnNumber) : mozilla::css::Rule(aLineNumber, aColumnNumber) - , mDeclaration(mozilla::Move(aDeclaration)) + , mDeclaration(aDeclaration) { mKeys.SwapElements(aKeys); } @@ -431,7 +431,7 @@ public: private: nsTArray mKeys; - nsAutoPtr mDeclaration; + RefPtr mDeclaration; // lazily created when needed: RefPtr mDOMDeclaration; }; @@ -521,11 +521,10 @@ class nsCSSPageRule final : public mozilla::css::Rule, public nsIDOMCSSPageRule { public: - // WARNING: Steals the contents of aDeclaration - nsCSSPageRule(nsAutoPtr&& aDeclaration, + nsCSSPageRule(mozilla::css::Declaration* aDeclaration, uint32_t aLineNumber, uint32_t aColumnNumber) : mozilla::css::Rule(aLineNumber, aColumnNumber) - , mDeclaration(mozilla::Move(aDeclaration)) + , mDeclaration(aDeclaration) , mImportantRule(nullptr) { } @@ -560,7 +559,7 @@ public: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override; private: - nsAutoPtr mDeclaration; + RefPtr mDeclaration; // lazily created when needed: RefPtr mDOMDeclaration; RefPtr mImportantRule; diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index 6364695ed3..a96a154cbc 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -23,6 +23,7 @@ #include "nsPresContext.h" #include "nsStyleUtil.h" #include "nsDeviceContext.h" +#include "nsStyleSet.h" using namespace mozilla; @@ -2369,7 +2370,6 @@ css::URLValue::URLValue(nsIURI* aURI, nsStringBuffer* aString, mURIResolved(true) { MOZ_ASSERT(aOriginPrincipal, "Must have an origin principal"); - mString->AddRef(); } css::URLValue::URLValue(nsStringBuffer* aString, nsIURI* aBaseURI, @@ -2381,12 +2381,6 @@ css::URLValue::URLValue(nsStringBuffer* aString, nsIURI* aBaseURI, mURIResolved(false) { MOZ_ASSERT(aOriginPrincipal, "Must have an origin principal"); - mString->AddRef(); -} - -css::URLValue::~URLValue() -{ - mString->Release(); } bool @@ -2570,6 +2564,7 @@ nsCSSValueGradient::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) con nsCSSValueTokenStream::nsCSSValueTokenStream() : mPropertyID(eCSSProperty_UNKNOWN) , mShorthandPropertyID(eCSSProperty_UNKNOWN) + , mLevel(SheetType::Count) { MOZ_COUNT_CTOR(nsCSSValueTokenStream); } diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index 325a85f3bf..b43bc3b20b 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -10,6 +10,7 @@ #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/SheetType.h" #include "nsIPrincipal.h" #include "nsIURI.h" @@ -87,7 +88,7 @@ struct URLValue { nsIPrincipal* aOriginPrincipal); protected: - ~URLValue(); + ~URLValue() {}; public: bool operator==(const URLValue& aOther) const; @@ -108,8 +109,7 @@ private: // null if the URI is invalid. mutable nsCOMPtr mURI; public: - nsStringBuffer* mString; // Could use nsRefPtr, but it'd add useless - // null-checks; this is never null. + RefPtr mString; nsCOMPtr mReferrer; nsCOMPtr mOriginPrincipal; @@ -1496,6 +1496,7 @@ public: return mPropertyID == aOther.mPropertyID && mShorthandPropertyID == aOther.mShorthandPropertyID && mTokenStream.Equals(aOther.mTokenStream) && + mLevel == aOther.mLevel && (mBaseURI == aOther.mBaseURI || (mBaseURI && aOther.mBaseURI && NS_SUCCEEDED(mBaseURI->Equals(aOther.mBaseURI, &eq)) && @@ -1544,6 +1545,7 @@ public: // mozilla::CSSStyleSheet* mSheet; uint32_t mLineNumber; uint32_t mLineOffset; + mozilla::SheetType mLevel; // This table is used to hold a reference on to any ImageValue that results // from re-parsing this token stream at computed value time. When properties diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index a03ab434d3..8ada59c0a2 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -96,7 +96,8 @@ struct nsComputedStyleMap bool IsEnabled() const { - return nsCSSProps::IsEnabled(mProperty); + return nsCSSProps::IsEnabled(mProperty, + nsCSSProps::eEnabledForAllContent); } }; @@ -505,8 +506,8 @@ nsComputedDOMStyle::GetStyleContextForElementNoFlush(Element* aElement, for (nsRuleNode* ruleNode = sc->RuleNode(); !ruleNode->IsRoot(); ruleNode = ruleNode->GetParent()) { - if (ruleNode->GetLevel() == nsStyleSet::eAgentSheet || - ruleNode->GetLevel() == nsStyleSet::eUserSheet) { + if (ruleNode->GetLevel() == SheetType::Agent || + ruleNode->GetLevel() == SheetType::User) { rules.AppendElement(ruleNode->GetRule()); } } diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index 26882c3b66..cfbbddb6c5 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -138,7 +138,7 @@ nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) } // cannot fail - css::Declaration *decl = new css::Declaration(); + RefPtr decl = new css::Declaration(); decl->InitializeEmpty(); RefPtr newRule = new css::StyleRule(nullptr, decl, 0, 0); diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index b02b98ff3e..4aa08914ae 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -123,7 +123,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - nsAutoPtr decl(new css::Declaration()); + RefPtr decl(new css::Declaration()); decl->InitializeEmpty(); nsCSSParser cssParser(env.mCSSLoader); bool changed; @@ -134,7 +134,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) return result; } - return SetCSSDeclaration(decl.forget()); + return SetCSSDeclaration(decl); } NS_IMETHODIMP @@ -328,16 +328,13 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSProperty aPropID, // between when we mutate the declaration and when we set the new // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - css::Declaration* decl = olddecl->EnsureMutable(); + RefPtr decl = olddecl->EnsureMutable(); nsCSSParser cssParser(env.mCSSLoader); bool changed; cssParser.ParseProperty(aPropID, aPropValue, env.mSheetURI, env.mBaseURI, env.mPrincipal, decl, &changed, aIsImportant); if (!changed) { - if (decl != olddecl) { - delete decl; - } // Parsing failed -- but we don't throw an exception for that. return NS_OK; } @@ -369,7 +366,7 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, // between when we mutate the declaration and when we set the new // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - css::Declaration* decl = olddecl->EnsureMutable(); + RefPtr decl = olddecl->EnsureMutable(); nsCSSParser cssParser(env.mCSSLoader); bool changed; @@ -379,9 +376,6 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, env.mBaseURI, env.mPrincipal, decl, &changed, aIsImportant); if (!changed) { - if (decl != olddecl) { - delete decl; - } // Parsing failed -- but we don't throw an exception for that. return NS_OK; } @@ -392,8 +386,8 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, nsresult nsDOMCSSDeclaration::RemoveProperty(const nsCSSProperty aPropID) { - css::Declaration* decl = GetCSSDeclaration(eOperation_RemoveProperty); - if (!decl) { + css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty); + if (!olddecl) { return NS_OK; // no decl, so nothing to remove } @@ -404,7 +398,7 @@ nsDOMCSSDeclaration::RemoveProperty(const nsCSSProperty aPropID) // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - decl = decl->EnsureMutable(); + RefPtr decl = olddecl->EnsureMutable(); decl->RemoveProperty(aPropID); return SetCSSDeclaration(decl); } @@ -415,8 +409,8 @@ nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName) MOZ_ASSERT(Substring(aPropertyName, 0, CSS_CUSTOM_NAME_PREFIX_LENGTH).EqualsLiteral("--")); - css::Declaration* decl = GetCSSDeclaration(eOperation_RemoveProperty); - if (!decl) { + css::Declaration* olddecl = GetCSSDeclaration(eOperation_RemoveProperty); + if (!olddecl) { return NS_OK; // no decl, so nothing to remove } @@ -427,16 +421,8 @@ nsDOMCSSDeclaration::RemoveCustomProperty(const nsAString& aPropertyName) // rule (see stack in bug 209575). mozAutoDocConditionalContentUpdateBatch autoUpdate(DocToUpdate(), true); - decl = decl->EnsureMutable(); + RefPtr decl = olddecl->EnsureMutable(); decl->RemoveVariableDeclaration(Substring(aPropertyName, CSS_CUSTOM_NAME_PREFIX_LENGTH)); return SetCSSDeclaration(decl); } - -bool IsCSSPropertyExposedToJS(nsCSSProperty aProperty, JSContext* cx, JSObject* obj) -{ - MOZ_ASSERT_UNREACHABLE("This is currently not used anywhere, " - "but should be reused soon in bug 1069192"); - nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent; - return nsCSSProps::IsEnabled(aProperty, enabledState); -} diff --git a/layout/style/nsDOMCSSDeclaration.h b/layout/style/nsDOMCSSDeclaration.h index 4f1bc88937..ced905e4b1 100644 --- a/layout/style/nsDOMCSSDeclaration.h +++ b/layout/style/nsDOMCSSDeclaration.h @@ -168,12 +168,4 @@ protected: virtual ~nsDOMCSSDeclaration(); }; -bool IsCSSPropertyExposedToJS(nsCSSProperty aProperty, JSContext* cx, JSObject* obj); - -template -MOZ_ALWAYS_INLINE bool IsCSSPropertyExposedToJS(JSContext* cx, JSObject* obj) -{ - return IsCSSPropertyExposedToJS(Property, cx, obj); -} - #endif // nsDOMCSSDeclaration_h___ diff --git a/layout/style/nsLayoutStylesheetCache.cpp b/layout/style/nsLayoutStylesheetCache.cpp index 536baa6428..82254fe6c7 100644 --- a/layout/style/nsLayoutStylesheetCache.cpp +++ b/layout/style/nsLayoutStylesheetCache.cpp @@ -157,13 +157,6 @@ nsLayoutStylesheetCache::QuirkSheet() return gStyleCache->mQuirkSheet; } -CSSStyleSheet* -nsLayoutStylesheetCache::FullScreenOverrideSheet() -{ - EnsureGlobal(); - return gStyleCache->mFullScreenOverrideSheet; -} - CSSStyleSheet* nsLayoutStylesheetCache::SVGSheet() { @@ -244,6 +237,32 @@ nsLayoutStylesheetCache::ContentPreferenceSheet(nsPresContext* aPresContext) return gStyleCache->mContentPreferenceSheet; } +/* static */ CSSStyleSheet* +nsLayoutStylesheetCache::ContentEditableSheet() +{ + EnsureGlobal(); + + if (!gStyleCache->mContentEditableSheet) { + LoadSheetURL("resource://gre/res/contenteditable.css", + gStyleCache->mContentEditableSheet, true); + } + + return gStyleCache->mContentEditableSheet; +} + +/* static */ CSSStyleSheet* +nsLayoutStylesheetCache::DesignModeSheet() +{ + EnsureGlobal(); + + if (!gStyleCache->mDesignModeSheet) { + LoadSheetURL("resource://gre/res/designmode.css", + gStyleCache->mDesignModeSheet, true); + } + + return gStyleCache->mDesignModeSheet; +} + void nsLayoutStylesheetCache::Shutdown() { @@ -272,10 +291,11 @@ nsLayoutStylesheetCache::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf #define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0; MEASURE(mChromePreferenceSheet); + MEASURE(mContentEditableSheet); MEASURE(mContentPreferenceSheet); MEASURE(mCounterStylesSheet); + MEASURE(mDesignModeSheet); MEASURE(mFormsSheet); - MEASURE(mFullScreenOverrideSheet); MEASURE(mHTMLSheet); MEASURE(mMathMLSheet); MEASURE(mMinimalXULSheet); @@ -316,8 +336,6 @@ nsLayoutStylesheetCache::nsLayoutStylesheetCache() // per-profile, since they're profile-invariant. LoadSheetURL("resource://gre-resources/counterstyles.css", mCounterStylesSheet, true); - LoadSheetURL("resource://gre-resources/full-screen-override.css", - mFullScreenOverrideSheet, true); LoadSheetURL("chrome://global/content/minimal-xul.css", mMinimalXULSheet, true); LoadSheetURL("resource://gre-resources/quirk.css", diff --git a/layout/style/nsLayoutStylesheetCache.h b/layout/style/nsLayoutStylesheetCache.h index c8960818ed..9aebc35468 100644 --- a/layout/style/nsLayoutStylesheetCache.h +++ b/layout/style/nsLayoutStylesheetCache.h @@ -44,7 +44,6 @@ class nsLayoutStylesheetCache final static mozilla::CSSStyleSheet* MinimalXULSheet(); static mozilla::CSSStyleSheet* XULSheet(); static mozilla::CSSStyleSheet* QuirkSheet(); - static mozilla::CSSStyleSheet* FullScreenOverrideSheet(); static mozilla::CSSStyleSheet* SVGSheet(); static mozilla::CSSStyleSheet* MathMLSheet(); static mozilla::CSSStyleSheet* CounterStylesSheet(); @@ -52,6 +51,8 @@ class nsLayoutStylesheetCache final static mozilla::CSSStyleSheet* NoFramesSheet(); static mozilla::CSSStyleSheet* ChromePreferenceSheet(nsPresContext* aPresContext); static mozilla::CSSStyleSheet* ContentPreferenceSheet(nsPresContext* aPresContext); + static mozilla::CSSStyleSheet* ContentEditableSheet(); + static mozilla::CSSStyleSheet* DesignModeSheet(); static void InvalidatePreferenceSheets(); @@ -85,10 +86,11 @@ private: static mozilla::StaticRefPtr gStyleCache; static mozilla::css::Loader* gCSSLoader; RefPtr mChromePreferenceSheet; + RefPtr mContentEditableSheet; RefPtr mContentPreferenceSheet; RefPtr mCounterStylesSheet; + RefPtr mDesignModeSheet; RefPtr mFormsSheet; - RefPtr mFullScreenOverrideSheet; RefPtr mHTMLSheet; RefPtr mMathMLSheet; RefPtr mMinimalXULSheet; diff --git a/layout/style/nsRuleData.h b/layout/style/nsRuleData.h index 3fed3a556c..f4d098c8ec 100644 --- a/layout/style/nsRuleData.h +++ b/layout/style/nsRuleData.h @@ -13,6 +13,7 @@ #include "mozilla/CSSVariableDeclarations.h" #include "mozilla/RuleNodeCacheConditions.h" +#include "mozilla/SheetType.h" #include "nsCSSProps.h" #include "nsCSSValue.h" #include "nsStyleStructFwd.h" @@ -28,7 +29,7 @@ struct nsRuleData const uint32_t mSIDs; mozilla::RuleNodeCacheConditions mConditions; bool mIsImportantRule; - uint16_t mLevel; // an nsStyleSet::sheetType + mozilla::SheetType mLevel; nsPresContext* const mPresContext; nsStyleContext* const mStyleContext; diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 4428d539fd..6130c8b2ff 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -66,12 +66,36 @@ using std::min; using namespace mozilla; using namespace mozilla::dom; -#define NS_SET_IMAGE_REQUEST(method_, context_, request_) \ +void* +nsConditionalResetStyleData::GetConditionalStyleData(nsStyleStructID aSID, + nsStyleContext* aStyleContext) const +{ + Entry* e = static_cast(mEntries[aSID]); + MOZ_ASSERT(e, "if mConditionalBits bit is set, we must have at least one " + "conditional style struct"); + do { + if (e->mConditions.Matches(aStyleContext)) { + void* data = e->mStyleStruct; + + // For reset structs with conditions, we cache the data on the + // style context. + // Tell the style context that it doesn't own the data + aStyleContext->AddStyleBit(GetBitForSID(aSID)); + aStyleContext->SetStyle(aSID, data); + + return data; + } + e = e->mNext; + } while (e); + return nullptr; +} + +#define NS_SET_IMAGE_REQUEST(method_, context_, request_) \ if ((context_)->PresContext()->IsDynamic()) { \ - method_(request_); \ - } else { \ + method_(request_); \ + } else { \ RefPtr req = nsContentUtils::GetStaticRequest(request_); \ - method_(req); \ + method_(req); \ } #define NS_SET_IMAGE_REQUEST_WITH_DOC(method_, context_, requestgetter_) \ @@ -505,6 +529,10 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue, } switch (aValue.GetUnit()) { case eCSSUnit_EM: { + if (aValue.GetFloatValue() == 0.0f) { + // Don't call SetFontSizeDependency for '0em'. + return 0; + } // CSS2.1 specifies that this unit scales to the computed font // size, not the em-width in the font metrics, despite the name. aConditions.SetFontSizeDependency(aFontSize); @@ -1466,11 +1494,11 @@ nsRuleNode::DestroyInternal(nsRuleNode ***aDestroyQueueTail) nsRuleNode* nsRuleNode::CreateRootNode(nsPresContext* aPresContext) { return new (aPresContext) - nsRuleNode(aPresContext, nullptr, nullptr, 0xff, false); + nsRuleNode(aPresContext, nullptr, nullptr, SheetType::Unknown, false); } nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, - nsIStyleRule* aRule, uint8_t aLevel, + nsIStyleRule* aRule, SheetType aLevel, bool aIsImportant) : mPresContext(aContext), mParent(aParent), @@ -1505,9 +1533,9 @@ nsRuleNode::nsRuleNode(nsPresContext* aContext, nsRuleNode* aParent, // nsStyleSet::GetContext depends on there being only one animation // rule. - MOZ_ASSERT(IsRoot() || GetLevel() != nsStyleSet::eAnimationSheet || + MOZ_ASSERT(IsRoot() || GetLevel() != SheetType::Animation || mParent->IsRoot() || - mParent->GetLevel() != nsStyleSet::eAnimationSheet, + mParent->GetLevel() != SheetType::Animation, "must be only one rule at animation level"); } @@ -1522,7 +1550,7 @@ nsRuleNode::~nsRuleNode() } nsRuleNode* -nsRuleNode::Transition(nsIStyleRule* aRule, uint8_t aLevel, +nsRuleNode::Transition(nsIStyleRule* aRule, SheetType aLevel, bool aIsImportantRule) { nsRuleNode* next = nullptr; @@ -2181,6 +2209,12 @@ nsRuleNode::ResolveVariableReferences(const nsStyleStructID aSID, &aContext->StyleVariables()->mVariables; nsCSSValueTokenStream* tokenStream = value->GetTokenStreamValue(); + MOZ_ASSERT(tokenStream->mLevel != SheetType::Count, + "Token stream should have a defined level"); + + AutoRestore saveLevel(aRuleData->mLevel); + aRuleData->mLevel = tokenStream->mLevel; + // Note that ParsePropertyWithVariableReferences relies on the fact // that the nsCSSValue in aRuleData for the property we are re-parsing // is still the token stream value. When @@ -2339,6 +2373,11 @@ nsRuleNode::WalkRuleTree(const nsStyleStructID aSID, // branch that they never need to examine their rules for this particular struct type // ever again. PropagateDependentBit(aSID, ruleNode, startStruct); + // For inherited structs, mark the struct (which will be set on + // the context by our caller) as not being owned by the context. + if (!isReset) { + aContext->AddStyleBit(nsCachedStyleData::GetBitForSID(aSID)); + } return startStruct; } if ((!startStruct && !isReset && @@ -2708,11 +2747,10 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex /* Propagate the bit down. */ \ PropagateDependentBit(eStyleStruct_##type_, aHighestNode, data_); \ /* Tell the style context that it doesn't own the data */ \ - aContext-> \ - AddStyleBit(nsCachedStyleData::GetBitForSID(eStyleStruct_##type_)); \ + aContext->AddStyleBit(NS_STYLE_INHERIT_BIT(type_)); \ } \ - /* Always cache inherited data on the style context */ \ - aContext->SetStyle##type_(data_); \ + /* For inherited structs, our caller will cache the data on the */ \ + /* style context */ \ \ return data_; @@ -2751,8 +2789,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex mStyleData.mResetData-> \ SetStyleData(eStyleStruct_##type_, mPresContext, data_, conditions); \ /* Tell the style context that it doesn't own the data */ \ - aContext-> \ - AddStyleBit(nsCachedStyleData::GetBitForSID(eStyleStruct_##type_)); \ + aContext->AddStyleBit(NS_STYLE_INHERIT_BIT(type_)); \ aContext->SetStyle(eStyleStruct_##type_, data_); \ } else { \ /* We can't be cached in the rule node. We have to be put right */ \ @@ -4375,7 +4412,7 @@ nsRuleNode::ComputeTextData(void* aStartStruct, conditions, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mControlCharacterVisibility, - NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE, 0, 0, 0, 0); + nsCSSParser::ControlCharVisibilityDefault(), 0, 0, 0, 0); COMPUTE_END_INHERITED(Text, text) } @@ -4815,8 +4852,9 @@ CountTransitionProps(const TransitionPropInfo* aInfo, return numTransitions; } -static void -ComputeTimingFunction(const nsCSSValue& aValue, nsTimingFunction& aResult) +/* static */ void +nsRuleNode::ComputeTimingFunction(const nsCSSValue& aValue, + nsTimingFunction& aResult) { switch (aValue.GetUnit()) { case eCSSUnit_Enumerated: @@ -5442,6 +5480,13 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, parentDisplay->mIsolation, NS_STYLE_ISOLATION_AUTO, 0, 0, 0, 0); + // -moz-top-layer: enum, inherit, initial + SetDiscrete(*aRuleData->ValueForTopLayer(), display->mTopLayer, + conditions, + SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, + parentDisplay->mTopLayer, NS_STYLE_TOP_LAYER_NONE, + 0, 0, 0, 0); + // Backup original display value for calculation of a hypothetical // box (CSS2 10.6.4/10.6.5), in addition to getting our style data right later. // See nsHTMLReflowState::CalculateHypotheticalBox @@ -5481,6 +5526,16 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, parentDisplay->mPosition, NS_STYLE_POSITION_STATIC, 0, 0, 0, 0); + // If an element is put in the top layer, while it is not absolutely + // positioned, the position value should be computed to 'absolute' per + // the Fullscreen API spec. + if (display->mTopLayer != NS_STYLE_TOP_LAYER_NONE && + !display->IsAbsolutelyPositionedStyle()) { + display->mPosition = NS_STYLE_POSITION_ABSOLUTE; + // We cannot cache this struct because otherwise it may be used as + // an aStartStruct for some other elements. + conditions.SetUncacheable(); + } // clear: enum, inherit, initial SetDiscrete(*aRuleData->ValueForClear(), display->mBreakType, conditions, @@ -9364,15 +9419,23 @@ nsRuleNode::GetStyleData(nsStyleStructID aSID, "if we ever call this on rule nodes that aren't used " "directly, we should adjust handling of mDependentBits " "in some way."); + MOZ_ASSERT(!aContext->GetCachedStyleData(aSID), + "style context should not have cached data for struct"); const void *data; // Never use cached data for animated style inside a pseudo-element; // see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto. if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { - data = mStyleData.GetStyleData(aSID, aContext); - if (MOZ_LIKELY(data != nullptr)) + data = mStyleData.GetStyleData(aSID, aContext, aComputeData); + if (MOZ_LIKELY(data != nullptr)) { + // For inherited structs, mark the struct (which will be set on + // the context by our caller) as not being owned by the context. + if (!nsCachedStyleData::IsReset(aSID)) { + aContext->AddStyleBit(nsCachedStyleData::GetBitForSID(aSID)); + } return data; // We have a fully specified struct. Just return it. + } } if (MOZ_UNLIKELY(!aComputeData)) @@ -9663,8 +9726,8 @@ nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, rule->MapRuleInfoInto(&ruleData); - if (ruleData.mLevel == nsStyleSet::eAgentSheet || - ruleData.mLevel == nsStyleSet::eUserSheet) { + if (ruleData.mLevel == SheetType::Agent || + ruleData.mLevel == SheetType::User) { // This is a rule whose effect we want to ignore, so if any of // the properties we care about were set, set them to the dummy // value that they'll never otherwise get. @@ -9786,7 +9849,7 @@ nsRuleNode::ComputePropertiesOverridingAnimation( // property) for transitions yet (which will make their // MapRuleInfoInto skip the properties that are currently // animating), we should skip them explicitly. - if (ruleData.mLevel == nsStyleSet::eTransitionSheet) { + if (ruleData.mLevel == SheetType::Transition) { continue; } @@ -9836,3 +9899,12 @@ nsRuleNode::ParentHasPseudoElementData(nsStyleContext* aContext) nsStyleContext* parent = aContext->GetParent(); return parent && parent->HasPseudoElementData(); } + +#ifdef DEBUG +bool +nsRuleNode::ContextHasCachedData(nsStyleContext* aContext, + nsStyleStructID aSID) +{ + return !!aContext->GetCachedStyleData(aSID); +} +#endif diff --git a/layout/style/nsRuleNode.h b/layout/style/nsRuleNode.h index 3e8014e5bf..26dbe0673f 100644 --- a/layout/style/nsRuleNode.h +++ b/layout/style/nsRuleNode.h @@ -15,6 +15,7 @@ #include "mozilla/PodOperations.h" #include "mozilla/RangedArray.h" #include "mozilla/RuleNodeCacheConditions.h" +#include "mozilla/SheetType.h" #include "nsPresContext.h" #include "nsStyleStruct.h" @@ -159,22 +160,29 @@ struct nsConditionalResetStyleData } void* GetStyleData(nsStyleStructID aSID, - nsStyleContext* aStyleContext) const { + nsStyleContext* aStyleContext, + bool aCanComputeData) const { if (!(mConditionalBits & GetBitForSID(aSID))) { return mEntries[aSID]; } - Entry* e = static_cast(mEntries[aSID]); - MOZ_ASSERT(e, "if mConditionalBits bit is set, we must have at least one " - "conditional style struct"); - do { - if (e->mConditions.Matches(aStyleContext)) { - return e->mStyleStruct; - } - e = e->mNext; - } while (e); - return nullptr; + if (!aCanComputeData) { + // If aCanComputeData is false, then any previously-computed data + // would have been cached on the style context. Therefore it's + // unnecessary to check the conditional data. It's also + // incorrect, because calling e->mConditions.Matches() below could + // cause additional structs to be computed, which is incorrect + // during CalcStyleDifference. + return nullptr; + } + return GetConditionalStyleData(aSID, aStyleContext); } +private: + // non-inline helper for GetStyleData + void* GetConditionalStyleData(nsStyleStructID aSID, + nsStyleContext* aStyleContext) const; + +public: void SetStyleData(nsStyleStructID aSID, void* aStyleStruct) { MOZ_ASSERT(!(mConditionalBits & GetBitForSID(aSID)), "rule node should not have unconditional and conditional style " @@ -273,10 +281,11 @@ struct nsCachedStyleData } void* NS_FASTCALL GetStyleData(const nsStyleStructID aSID, - nsStyleContext* aStyleContext) { + nsStyleContext* aStyleContext, + bool aCanComputeData) { if (IsReset(aSID)) { if (mResetData) { - return mResetData->GetStyleData(aSID, aStyleContext); + return mResetData->GetStyleData(aSID, aStyleContext, aCanComputeData); } } else { if (mInheritedData) { @@ -308,9 +317,12 @@ struct nsCachedStyleData mInheritedData->mStyleStructs[eStyleStruct_##name_]) : nullptr; \ } #define STYLE_STRUCT_RESET(name_, checkdata_cb_) \ - nsStyle##name_ * NS_FASTCALL GetStyle##name_ (nsStyleContext* aContext) { \ + nsStyle##name_ * NS_FASTCALL GetStyle##name_ (nsStyleContext* aContext, \ + bool aCanComputeData) { \ return mResetData ? static_cast( \ - mResetData->GetStyleData(eStyleStruct_##name_, aContext)) : nullptr; \ + mResetData->GetStyleData(eStyleStruct_##name_, aContext, \ + aCanComputeData)) \ + : nullptr; \ } #include "nsStyleStructList.h" #undef STYLE_STRUCT_RESET @@ -418,10 +430,10 @@ private: struct Key { nsIStyleRule* mRule; - uint8_t mLevel; + mozilla::SheetType mLevel; bool mIsImportantRule; - Key(nsIStyleRule* aRule, uint8_t aLevel, bool aIsImportantRule) + Key(nsIStyleRule* aRule, mozilla::SheetType aLevel, bool aIsImportantRule) : mRule(aRule), mLevel(aLevel), mIsImportantRule(aIsImportantRule) {} @@ -800,7 +812,7 @@ protected: private: nsRuleNode(nsPresContext* aPresContext, nsRuleNode* aParent, - nsIStyleRule* aRule, uint8_t aLevel, bool aIsImportant); + nsIStyleRule* aRule, mozilla::SheetType aLevel, bool aIsImportant); ~nsRuleNode(); public: @@ -812,7 +824,7 @@ public: static void EnsureInlineDisplay(uint8_t& display); // Transition never returns null; on out of memory it'll just return |this|. - nsRuleNode* Transition(nsIStyleRule* aRule, uint8_t aLevel, + nsRuleNode* Transition(nsIStyleRule* aRule, mozilla::SheetType aLevel, bool aIsImportantRule); nsRuleNode* GetParent() const { return mParent; } bool IsRoot() const { return mParent == nullptr; } @@ -823,11 +835,11 @@ public: return const_cast(this)->RuleTree(); } - // These uint8_ts are really nsStyleSet::sheetType values. - uint8_t GetLevel() const { + mozilla::SheetType GetLevel() const { NS_ASSERTION(!IsRoot(), "can't call on root"); - return (mDependentBits & NS_RULE_NODE_LEVEL_MASK) >> - NS_RULE_NODE_LEVEL_SHIFT; + return mozilla::SheetType( + (mDependentBits & NS_RULE_NODE_LEVEL_MASK) >> + NS_RULE_NODE_LEVEL_SHIFT); } bool IsImportantRule() const { NS_ASSERTION(!IsRoot(), "can't call on root"); @@ -882,12 +894,14 @@ public: #define STYLE_STRUCT_INHERITED(name_, checkdata_cb_) \ template \ const nsStyle##name_* \ - GetStyle##name_(nsStyleContext* aContext) \ + GetStyle##name_(nsStyleContext* aContext, uint64_t& aContextStyleBits) \ { \ NS_ASSERTION(IsUsedDirectly(), \ "if we ever call this on rule nodes that aren't used " \ "directly, we should adjust handling of mDependentBits " \ "in some way."); \ + MOZ_ASSERT(!ContextHasCachedData(aContext, eStyleStruct_##name_), \ + "style context should not have cached data for struct"); \ \ const nsStyle##name_ *data; \ \ @@ -895,8 +909,15 @@ public: /* see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto */ \ if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { \ data = mStyleData.GetStyle##name_(); \ - if (MOZ_LIKELY(data != nullptr)) \ + if (data != nullptr) { \ + /* For inherited structs, mark the struct (which will be set on */ \ + /* the context by our caller) as not being owned by the context. */ \ + /* Normally this would be aContext->AddStyleBit(), but aContext is */ \ + /* an incomplete type here, so we work around that with a param. */ \ + aContextStyleBits |= NS_STYLE_INHERIT_BIT(name_); \ + /* Our caller will cache the data on the style context. */ \ return data; \ + } \ } \ \ if (!aComputeData) \ @@ -918,15 +939,18 @@ public: "if we ever call this on rule nodes that aren't used " \ "directly, we should adjust handling of mDependentBits " \ "in some way."); \ + MOZ_ASSERT(!ContextHasCachedData(aContext, eStyleStruct_##name_), \ + "style context should not have cached data for struct"); \ \ const nsStyle##name_ *data; \ \ /* Never use cached data for animated style inside a pseudo-element; */ \ /* see comment on cacheability in AnimValuesStyleRule::MapRuleInfoInto */ \ if (!(HasAnimationData() && ParentHasPseudoElementData(aContext))) { \ - data = mStyleData.GetStyle##name_(aContext); \ - if (MOZ_LIKELY(data != nullptr)) \ + data = mStyleData.GetStyle##name_(aContext, aComputeData); \ + if (MOZ_LIKELY(data != nullptr)) { \ return data; \ + } \ } \ \ if (!aComputeData) \ @@ -1053,6 +1077,17 @@ public: nscolor& aResult); static bool ParentHasPseudoElementData(nsStyleContext* aContext); + + static void ComputeTimingFunction(const nsCSSValue& aValue, + nsTimingFunction& aResult); + +private: +#ifdef DEBUG + // non-inline helper function to allow assertions without incomplete + // type errors + bool ContextHasCachedData(nsStyleContext* aContext, nsStyleStructID aSID); +#endif + }; #endif diff --git a/layout/style/nsRuleWalker.h b/layout/style/nsRuleWalker.h index f2c4a7de7e..51e2228a7e 100644 --- a/layout/style/nsRuleWalker.h +++ b/layout/style/nsRuleWalker.h @@ -54,7 +54,7 @@ public: bool AtRoot() { return mCurrent == mRoot; } - void SetLevel(uint8_t aLevel, bool aImportance, + void SetLevel(mozilla::SheetType aLevel, bool aImportance, bool aCheckForImportantRules) { NS_ASSERTION(!aCheckForImportantRules || !aImportance, "Shouldn't be checking for important rules while walking " @@ -63,7 +63,7 @@ public: mImportance = aImportance; mCheckForImportantRules = aCheckForImportantRules; } - uint8_t GetLevel() const { return mLevel; } + mozilla::SheetType GetLevel() const { return mLevel; } bool GetImportance() const { return mImportance; } bool GetCheckForImportantRules() const { return mCheckForImportantRules; } @@ -86,7 +86,7 @@ public: private: nsRuleNode* mCurrent; // Our current position. Never null. nsRuleNode* mRoot; // The root of the tree we're walking. - uint8_t mLevel; // an nsStyleSet::sheetType + mozilla::SheetType mLevel; bool mImportance; bool mCheckForImportantRules; // If true, check for important rules as // we walk and set to false if we find diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index bb1ab6a7e5..c586b22e62 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -866,6 +866,10 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_TOUCH_ACTION_PAN_Y (1 << 3) #define NS_STYLE_TOUCH_ACTION_MANIPULATION (1 << 4) +// See nsStyleDisplay +#define NS_STYLE_TOP_LAYER_NONE 0 // not in the top layer +#define NS_STYLE_TOP_LAYER_TOP 1 // in the top layer + // See nsStyleDisplay #define NS_STYLE_TRANSFORM_BOX_BORDER_BOX 0 #define NS_STYLE_TRANSFORM_BOX_FILL_BOX 1 diff --git a/layout/style/nsStyleContext.cpp b/layout/style/nsStyleContext.cpp index 448f61617b..771a08b7d6 100644 --- a/layout/style/nsStyleContext.cpp +++ b/layout/style/nsStyleContext.cpp @@ -408,27 +408,19 @@ nsStyleContext::HasChildThatUsesGrandancestorStyle() const ListContainsStyleContextThatUsesGrandancestorStyle(mChild); } -const void* nsStyleContext::GetCachedStyleData(nsStyleStructID aSID) -{ - const void* cachedData; - if (nsCachedStyleData::IsReset(aSID)) { - if (mCachedResetData) { - cachedData = mCachedResetData->mStyleStructs[aSID]; - } else { - cachedData = nullptr; - } - } else { - cachedData = mCachedInheritedData.mStyleStructs[aSID]; - } - return cachedData; -} - const void* nsStyleContext::StyleData(nsStyleStructID aSID) { const void* cachedData = GetCachedStyleData(aSID); if (cachedData) return cachedData; // We have computed data stored on this node in the context tree. - return mRuleNode->GetStyleData(aSID, this, true); // Our rule node will take care of it for us. + // Our rule node will take care of it for us. + const void* newData = mRuleNode->GetStyleData(aSID, this, true); + if (!nsCachedStyleData::IsReset(aSID)) { + // always cache inherited data on the style context; the rule + // node set the bit in mBits for us if needed. + mCachedInheritedData.mStyleStructs[aSID] = const_cast(newData); + } + return newData; } // This is an evil evil function, since it forces you to alloc your own separate copy of @@ -840,19 +832,22 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, // we worry about 'inherit' values.) bool compare = mRuleNode != aOther->mRuleNode; + DebugOnly structsFound = 0; + // If we had any change in variable values, then we'll need to examine // all of the other style structs too, even if the new style context has // the same rule node as the old one. const nsStyleVariables* thisVariables = PeekStyleVariables(); if (thisVariables) { + structsFound |= NS_STYLE_INHERIT_BIT(Variables); const nsStyleVariables* otherVariables = aOther->StyleVariables(); if (thisVariables->mVariables == otherVariables->mVariables) { - *aEqualStructs |= nsCachedStyleData::GetBitForSID(eStyleStruct_Variables); + *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables); } else { compare = true; } } else { - *aEqualStructs |= nsCachedStyleData::GetBitForSID(eStyleStruct_Variables); + *aEqualStructs |= NS_STYLE_INHERIT_BIT(Variables); } DebugOnly styleStructCount = 1; // count Variables already @@ -861,6 +856,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, PR_BEGIN_MACRO \ const nsStyle##struct_* this##struct_ = PeekStyle##struct_(); \ if (this##struct_) { \ + structsFound |= NS_STYLE_INHERIT_BIT(struct_); \ const nsStyle##struct_* other##struct_ = aOther->Style##struct_(); \ nsChangeHint maxDifference = nsStyle##struct_::MaxDifference(); \ nsChangeHint differenceAlwaysHandledForDescendants = \ @@ -922,7 +918,7 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, DO_STRUCT_DIFFERENCE(SVGReset); DO_STRUCT_DIFFERENCE(SVG); #undef EXTRA_DIFF_ARGS -#define EXTRA_DIFF_ARGS , this +#define EXTRA_DIFF_ARGS , PeekStyleVisibility() DO_STRUCT_DIFFERENCE(Position); #undef EXTRA_DIFF_ARGS #define EXTRA_DIFF_ARGS /* nothing */ @@ -940,6 +936,16 @@ nsStyleContext::CalcStyleDifference(nsStyleContext* aOther, MOZ_ASSERT(styleStructCount == nsStyleStructID_Length, "missing a call to DO_STRUCT_DIFFERENCE"); +#ifdef DEBUG + #define STYLE_STRUCT(name_, callback_) \ + MOZ_ASSERT(!!(structsFound & NS_STYLE_INHERIT_BIT(name_)) == \ + !!PeekStyle##name_(), \ + "PeekStyleData results must not change in the middle of " \ + "difference calculation."); + #include "nsStyleStructList.h" + #undef STYLE_STRUCT +#endif + // We check for struct pointer equality here rather than as part of the // DO_STRUCT_DIFFERENCE calls, since those calls can result in structs // we previously examined and found to be null on this style context @@ -1497,7 +1503,7 @@ nsStyleContext::GetCachedStyleDataAsString(uint32_t aStructs) structs.Append(' '); } structs.AppendPrintf("%s=%p", StructName(i), data); - if (HasCachedInheritedStyleData(i)) { + if (HasCachedDependentStyleData(i)) { structs.AppendLiteral("(dependent)"); } else { structs.AppendLiteral("(owned)"); diff --git a/layout/style/nsStyleContext.h b/layout/style/nsStyleContext.h index 2c73dfa54b..c1998a972c 100644 --- a/layout/style/nsStyleContext.h +++ b/layout/style/nsStyleContext.h @@ -282,11 +282,14 @@ public: #undef STYLE_STRUCT_INHERITED /** - * Returns whether this style context has cached, inherited style data for a - * given style struct. + * Returns whether this style context has cached style data for a + * given style struct and it does NOT own that struct. This can + * happen because it was inherited from the parent style context, or + * because it was stored conditionally on the rule node. */ - bool HasCachedInheritedStyleData(nsStyleStructID aSID) - { return mBits & nsCachedStyleData::GetBitForSID(aSID); } + bool HasCachedDependentStyleData(nsStyleStructID aSID) { + return mBits & nsCachedStyleData::GetBitForSID(aSID); + } nsRuleNode* RuleNode() { return mRuleNode; } void AddStyleBit(const uint64_t& aBit) { mBits |= aBit; } @@ -453,6 +456,28 @@ public: int32_t& LoggingDepth(); #endif + /** + * Return style data that is currently cached on the style context. + * Only returns the structs we cache ourselves; never consults the + * rule tree. + * + * For "internal" use only in nsStyleContext and nsRuleNode. + */ + const void* GetCachedStyleData(nsStyleStructID aSID) + { + const void* cachedData; + if (nsCachedStyleData::IsReset(aSID)) { + if (mCachedResetData) { + cachedData = mCachedResetData->mStyleStructs[aSID]; + } else { + cachedData = nullptr; + } + } else { + cachedData = mCachedInheritedData.mStyleStructs[aSID]; + } + return cachedData; + } + private: // Private destructor, to discourage deletion outside of Release(): ~nsStyleContext(); @@ -469,10 +494,6 @@ private: static bool ListContainsStyleContextThatUsesGrandancestorStyle( const nsStyleContext* aHead); - // Helper function that GetStyleData and GetUniqueStyleData use. Only - // returns the structs we cache ourselves; never consults the ruletree. - inline const void* GetCachedStyleData(nsStyleStructID aSID); - #ifdef DEBUG struct AutoCheckDependency { @@ -511,9 +532,20 @@ private: mCachedInheritedData.mStyleStructs[eStyleStruct_##name_]); \ if (cachedData) /* Have it cached already, yay */ \ return cachedData; \ + if (!aComputeData) { \ + /* We always cache inherited structs on the context when we */ \ + /* compute them. */ \ + return nullptr; \ + } \ /* Have the rulenode deal */ \ AUTO_CHECK_DEPENDENCY(eStyleStruct_##name_); \ - return mRuleNode->GetStyle##name_(this); \ + const nsStyle##name_ * newData = \ + mRuleNode->GetStyle##name_(this, mBits); \ + /* always cache inherited data on the style context; the rule */ \ + /* node set the bit in mBits for us if needed. */ \ + mCachedInheritedData.mStyleStructs[eStyleStruct_##name_] = \ + const_cast(newData); \ + return newData; \ } #define STYLE_STRUCT_RESET(name_, checkdata_cb_) \ template \ @@ -591,8 +623,15 @@ private: // sometimes allocate the mCachedResetData. nsResetStyleData* mCachedResetData; // Cached reset style data. nsInheritedStyleData mCachedInheritedData; // Cached inherited style data - uint64_t mBits; // Which structs are inherited from the - // parent context or owned by mRuleNode. + + // mBits stores a number of things: + // - It records (using the style struct bits) which structs are + // inherited from the parent context or owned by mRuleNode (i.e., + // not owned by the style context). + // - It also stores the additional bits listed at the top of + // nsStyleStruct.h. + uint64_t mBits; + uint32_t mRefCnt; #ifdef DEBUG diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 594bf9a04e..62b3909009 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -13,6 +13,7 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/CSSStyleSheet.h" +#include "mozilla/EnumeratedRange.h" #include "mozilla/EventStates.h" #include "mozilla/MemoryReporting.h" #include "mozilla/RuleProcessorCache.h" @@ -143,18 +144,18 @@ nsDisableTextZoomStyleRule::List(FILE* out, int32_t aIndent) const } #endif -static const nsStyleSet::sheetType gCSSSheetTypes[] = { +static const SheetType gCSSSheetTypes[] = { // From lowest to highest in cascading order. - nsStyleSet::eAgentSheet, - nsStyleSet::eUserSheet, - nsStyleSet::eDocSheet, - nsStyleSet::eScopedDocSheet, - nsStyleSet::eOverrideSheet + SheetType::Agent, + SheetType::User, + SheetType::Doc, + SheetType::ScopedDoc, + SheetType::Override }; -static bool IsCSSSheetType(nsStyleSet::sheetType aSheetType) +static bool IsCSSSheetType(SheetType aSheetType) { - for (nsStyleSet::sheetType type : gCSSSheetTypes) { + for (SheetType type : gCSSSheetTypes) { if (type == aSheetType) { return true; } @@ -177,7 +178,7 @@ nsStyleSet::nsStyleSet() nsStyleSet::~nsStyleSet() { - for (sheetType type : gCSSSheetTypes) { + for (SheetType type : gCSSSheetTypes) { for (uint32_t i = 0, n = mSheets[type].Length(); i < n; i++) { static_cast(mSheets[type][i])->DropStyleSet(this); } @@ -185,12 +186,12 @@ nsStyleSet::~nsStyleSet() // drop reference to cached rule processors nsCSSRuleProcessor* rp; - rp = static_cast(mRuleProcessors[eAgentSheet].get()); + rp = static_cast(mRuleProcessors[SheetType::Agent].get()); if (rp) { MOZ_ASSERT(rp->IsShared()); rp->ReleaseStyleSetRef(); } - rp = static_cast(mRuleProcessors[eUserSheet].get()); + rp = static_cast(mRuleProcessors[SheetType::User].get()); if (rp) { MOZ_ASSERT(rp->IsShared()); rp->ReleaseStyleSetRef(); @@ -202,17 +203,17 @@ nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { size_t n = aMallocSizeOf(this); - for (int i = 0; i < eSheetTypeCount; i++) { - if (mRuleProcessors[i]) { + for (SheetType type : MakeEnumeratedRange(SheetType::Count)) { + if (mRuleProcessors[type]) { bool shared = false; - if (i == eAgentSheet || i == eUserSheet) { + if (type == SheetType::Agent || type == SheetType::User) { // The only two origins we consider caching rule processors for. nsCSSRuleProcessor* rp = - static_cast(mRuleProcessors[i].get()); + static_cast(mRuleProcessors[type].get()); shared = rp->IsShared(); } if (!shared) { - n += mRuleProcessors[i]->SizeOfIncludingThis(aMallocSizeOf); + n += mRuleProcessors[type]->SizeOfIncludingThis(aMallocSizeOf); } } // mSheets is a C-style array of nsCOMArrays. We do not own the sheets in @@ -220,7 +221,7 @@ nsStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const // document owns them) so we do not count the sheets here (we pass nullptr // as the aSizeOfElementIncludingThis argument). All we're doing here is // counting the size of the nsCOMArrays' buffers. - n += mSheets[i].SizeOfExcludingThis(nullptr, aMallocSizeOf); + n += mSheets[type].SizeOfExcludingThis(nullptr, aMallocSizeOf); } for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { @@ -246,12 +247,12 @@ nsStyleSet::Init(nsPresContext *aPresContext) // Make an explicit GatherRuleProcessors call for the levels that // don't have style sheets. The other levels will have their calls - // triggered by DirtyRuleProcessors. (We should probably convert the - // ePresHintSheet and eStyleAttrSheet levels to work like this as - // well, and not implement nsIStyleSheet.) - GatherRuleProcessors(eAnimationSheet); - GatherRuleProcessors(eTransitionSheet); - GatherRuleProcessors(eSVGAttrAnimationSheet); + // triggered by DirtyRuleProcessors. + GatherRuleProcessors(SheetType::PresHint); + GatherRuleProcessors(SheetType::SVGAttrAnimation); + GatherRuleProcessors(SheetType::StyleAttr); + GatherRuleProcessors(SheetType::Animation); + GatherRuleProcessors(SheetType::Transition); } nsresult @@ -387,8 +388,10 @@ SortStyleSheetsByScope(nsTArray& aSheets) } nsresult -nsStyleSet::GatherRuleProcessors(sheetType aType) +nsStyleSet::GatherRuleProcessors(SheetType aType) { + NS_ENSURE_FALSE(mInShutdown, NS_ERROR_FAILURE); + // We might be in GatherRuleProcessors because we are dropping a sheet, // resulting in an nsCSSSelector being destroyed. Tell the // RestyleManager for each document we're used in so that they can @@ -399,7 +402,7 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) } nsCOMPtr oldRuleProcessor(mRuleProcessors[aType]); nsTArray> oldScopedDocRuleProcessors; - if (aType == eAgentSheet || aType == eUserSheet) { + if (aType == SheetType::Agent || aType == SheetType::User) { // drop reference to cached rule processor nsCSSRuleProcessor* rp = static_cast(mRuleProcessors[aType].get()); @@ -409,7 +412,7 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) } } mRuleProcessors[aType] = nullptr; - if (aType == eScopedDocSheet) { + if (aType == SheetType::ScopedDoc) { for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { nsIStyleRuleProcessor* processor = mScopedDocSheetRuleProcessors[i].get(); Element* scope = @@ -420,9 +423,9 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) // Clear mScopedDocSheetRuleProcessors, but save it. oldScopedDocRuleProcessors.SwapElements(mScopedDocSheetRuleProcessors); } - if (mAuthorStyleDisabled && (aType == eDocSheet || - aType == eScopedDocSheet || - aType == eStyleAttrSheet)) { + if (mAuthorStyleDisabled && (aType == SheetType::Doc || + aType == SheetType::ScopedDoc || + aType == SheetType::StyleAttr)) { // Don't regather if this level is disabled. Note that we gather // preshint sheets no matter what, but then skip them for some // elements later if mAuthorStyleDisabled. @@ -431,24 +434,24 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) switch (aType) { // handle the types for which have a rule processor that does not // implement the style sheet interface. - case eAnimationSheet: + case SheetType::Animation: MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->AnimationManager(); return NS_OK; - case eTransitionSheet: + case SheetType::Transition: MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->TransitionManager(); return NS_OK; - case eStyleAttrSheet: + case SheetType::StyleAttr: MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->Document()->GetInlineStyleSheet(); return NS_OK; - case ePresHintSheet: + case SheetType::PresHint: MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->Document()->GetAttributeStyleSheet(); return NS_OK; - case eSVGAttrAnimationSheet: + case SheetType::SVGAttrAnimation: MOZ_ASSERT(mSheets[aType].IsEmpty()); mRuleProcessors[aType] = PresContext()->Document()->GetSVGAttrAnimationRuleProcessor(); @@ -457,9 +460,9 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) // keep going break; } - if (aType == eScopedDocSheet) { + if (aType == SheetType::ScopedDoc) { // Create a rule processor for each scope. - uint32_t count = mSheets[eScopedDocSheet].Count(); + uint32_t count = mSheets[SheetType::ScopedDoc].Count(); if (count) { // Gather the scoped style sheets into an array as // CSSStyleSheets, and mark all of their scope elements @@ -467,7 +470,7 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) nsTArray sheets(count); for (uint32_t i = 0; i < count; i++) { RefPtr sheet = - do_QueryObject(mSheets[eScopedDocSheet].ObjectAt(i)); + do_QueryObject(mSheets[SheetType::ScopedDoc].ObjectAt(i)); sheets.AppendElement(sheet); Element* scope = sheet->GetScopeElement(); @@ -509,8 +512,7 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) sheetsForScope.AppendElements(sheets.Elements() + start, end - start); nsCSSRuleProcessor* oldRP = oldScopedRuleProcessorHash.Get(scope); mScopedDocSheetRuleProcessors.AppendElement - (new nsCSSRuleProcessor(sheetsForScope, uint8_t(aType), scope, - oldRP)); + (new nsCSSRuleProcessor(sheetsForScope, aType, scope, oldRP)); start = end; } while (start < count); @@ -519,8 +521,8 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) } if (mSheets[aType].Count()) { switch (aType) { - case eAgentSheet: - case eUserSheet: { + case SheetType::Agent: + case SheetType::User: { // levels containing non-scoped CSS style sheets whose rule processors // we want to re-use nsCOMArray& sheets = mSheets[aType]; @@ -537,7 +539,7 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) nsCSSRuleProcessor* rp = RuleProcessorCache::GetRuleProcessor(cssSheetsRaw, PresContext()); if (!rp) { - rp = new nsCSSRuleProcessor(cssSheets, uint8_t(aType), nullptr, + rp = new nsCSSRuleProcessor(cssSheets, aType, nullptr, static_cast( oldRuleProcessor.get()), true /* aIsShared */); @@ -553,8 +555,8 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) rp->AddStyleSetRef(); break; } - case eDocSheet: - case eOverrideSheet: { + case SheetType::Doc: + case SheetType::Override: { // levels containing non-scoped CSS stylesheets whose rule processors // we don't want to re-use nsCOMArray& sheets = mSheets[aType]; @@ -565,7 +567,7 @@ nsStyleSet::GatherRuleProcessors(sheetType aType) cssSheets.AppendElement(cssSheet); } mRuleProcessors[aType] = - new nsCSSRuleProcessor(cssSheets, uint8_t(aType), nullptr, + new nsCSSRuleProcessor(cssSheets, aType, nullptr, static_cast( oldRuleProcessor.get())); } break; @@ -591,7 +593,7 @@ IsScopedStyleSheet(nsIStyleSheet* aSheet) } nsresult -nsStyleSet::AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet) +nsStyleSet::AppendStyleSheet(SheetType aType, nsIStyleSheet *aSheet) { NS_PRECONDITION(aSheet, "null arg"); NS_ASSERTION(aSheet->IsApplicable(), @@ -608,7 +610,7 @@ nsStyleSet::AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet) } nsresult -nsStyleSet::PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet) +nsStyleSet::PrependStyleSheet(SheetType aType, nsIStyleSheet *aSheet) { NS_PRECONDITION(aSheet, "null arg"); NS_ASSERTION(aSheet->IsApplicable(), @@ -625,7 +627,7 @@ nsStyleSet::PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet) } nsresult -nsStyleSet::RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet) +nsStyleSet::RemoveStyleSheet(SheetType aType, nsIStyleSheet *aSheet) { NS_PRECONDITION(aSheet, "null arg"); NS_ASSERTION(aSheet->IsComplete(), @@ -640,7 +642,7 @@ nsStyleSet::RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet) } nsresult -nsStyleSet::ReplaceSheets(sheetType aType, +nsStyleSet::ReplaceSheets(SheetType aType, const nsCOMArray &aNewSheets) { bool cssSheetType = IsCSSSheetType(aType); @@ -664,7 +666,7 @@ nsStyleSet::ReplaceSheets(sheetType aType, } nsresult -nsStyleSet::InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet, +nsStyleSet::InsertStyleSheetBefore(SheetType aType, nsIStyleSheet *aNewSheet, nsIStyleSheet *aReferenceSheet) { NS_PRECONDITION(aNewSheet && aReferenceSheet, "null arg"); @@ -686,13 +688,19 @@ nsStyleSet::InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet, return DirtyRuleProcessors(aType); } +static inline uint32_t +DirtyBit(SheetType aType) +{ + return 1 << uint32_t(aType); +} + nsresult -nsStyleSet::DirtyRuleProcessors(sheetType aType) +nsStyleSet::DirtyRuleProcessors(SheetType aType) { if (!mBatching) return GatherRuleProcessors(aType); - mDirty |= 1 << aType; + mDirty |= DirtyBit(aType); return NS_OK; } @@ -708,9 +716,9 @@ nsStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled) if (aStyleDisabled == !mAuthorStyleDisabled) { mAuthorStyleDisabled = aStyleDisabled; BeginUpdate(); - mDirty |= 1 << eDocSheet | - 1 << eScopedDocSheet | - 1 << eStyleAttrSheet; + mDirty |= DirtyBit(SheetType::Doc) | + DirtyBit(SheetType::ScopedDoc) | + DirtyBit(SheetType::StyleAttr); return EndUpdate(); } return NS_OK; @@ -725,9 +733,9 @@ nsStyleSet::AddDocStyleSheet(nsIStyleSheet* aSheet, nsIDocument* aDocument) NS_ASSERTION(aSheet->IsApplicable(), "Inapplicable sheet being placed in style set"); - sheetType type = IsScopedStyleSheet(aSheet) ? - eScopedDocSheet : - eDocSheet; + SheetType type = IsScopedStyleSheet(aSheet) ? + SheetType::ScopedDoc : + SheetType::Doc; nsCOMArray& sheets = mSheets[type]; bool present = sheets.RemoveObject(aSheet); @@ -769,7 +777,8 @@ nsStyleSet::RemoveDocStyleSheet(nsIStyleSheet *aSheet) { RefPtr cssSheet = do_QueryObject(aSheet); bool isScoped = cssSheet && cssSheet->GetScopeElement(); - return RemoveStyleSheet(isScoped ? eScopedDocSheet : eDocSheet, aSheet); + return RemoveStyleSheet(isScoped ? SheetType::ScopedDoc : SheetType::Doc, + aSheet); } // Batching @@ -788,9 +797,9 @@ nsStyleSet::EndUpdate() return NS_OK; } - for (int i = 0; i < eSheetTypeCount; ++i) { - if (mDirty & (1 << i)) { - nsresult rv = GatherRuleProcessors(sheetType(i)); + for (SheetType type : MakeEnumeratedRange(SheetType::Count)) { + if (mDirty & DirtyBit(type)) { + nsresult rv = GatherRuleProcessors(type); NS_ENSURE_SUCCESS(rv, rv); } } @@ -812,7 +821,7 @@ static inline bool IsMoreSpecificThanAnimation(nsRuleNode *aRuleNode) { return !aRuleNode->IsRoot() && - (aRuleNode->GetLevel() == nsStyleSet::eTransitionSheet || + (aRuleNode->GetLevel() == SheetType::Transition || aRuleNode->IsImportantRule()); } @@ -824,7 +833,7 @@ GetAnimationRule(nsRuleNode *aRuleNode) n = n->GetParent(); } - if (n->IsRoot() || n->GetLevel() != nsStyleSet::eAnimationSheet) { + if (n->IsRoot() || n->GetLevel() != SheetType::Animation) { return nullptr; } @@ -846,17 +855,17 @@ ReplaceAnimationRule(nsRuleNode *aOldRuleNode, if (aOldAnimRule) { MOZ_ASSERT(n->GetRule() == aOldAnimRule, "wrong rule"); - MOZ_ASSERT(n->GetLevel() == nsStyleSet::eAnimationSheet, + MOZ_ASSERT(n->GetLevel() == SheetType::Animation, "wrong level"); n = n->GetParent(); } MOZ_ASSERT(!IsMoreSpecificThanAnimation(n) && - (n->IsRoot() || n->GetLevel() != nsStyleSet::eAnimationSheet), + (n->IsRoot() || n->GetLevel() != SheetType::Animation), "wrong level"); if (aNewAnimRule) { - n = n->Transition(aNewAnimRule, nsStyleSet::eAnimationSheet, false); + n = n->Transition(aNewAnimRule, SheetType::Animation, false); n->SetIsAnimationRule(); } @@ -1111,30 +1120,30 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, // this will be either the root or one of the restriction rules. nsRuleNode* lastRestrictionRN = aRuleWalker->CurrentNode(); - aRuleWalker->SetLevel(eAgentSheet, false, true); - if (mRuleProcessors[eAgentSheet]) - (*aCollectorFunc)(mRuleProcessors[eAgentSheet], aData); + aRuleWalker->SetLevel(SheetType::Agent, false, true); + if (mRuleProcessors[SheetType::Agent]) + (*aCollectorFunc)(mRuleProcessors[SheetType::Agent], aData); nsRuleNode* lastAgentRN = aRuleWalker->CurrentNode(); bool haveImportantUARules = !aRuleWalker->GetCheckForImportantRules(); - aRuleWalker->SetLevel(eUserSheet, false, true); + aRuleWalker->SetLevel(SheetType::User, false, true); bool skipUserStyles = aElement && aElement->IsInNativeAnonymousSubtree(); - if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different - (*aCollectorFunc)(mRuleProcessors[eUserSheet], aData); + if (!skipUserStyles && mRuleProcessors[SheetType::User]) // NOTE: different + (*aCollectorFunc)(mRuleProcessors[SheetType::User], aData); nsRuleNode* lastUserRN = aRuleWalker->CurrentNode(); bool haveImportantUserRules = !aRuleWalker->GetCheckForImportantRules(); - aRuleWalker->SetLevel(ePresHintSheet, false, false); - if (mRuleProcessors[ePresHintSheet]) - (*aCollectorFunc)(mRuleProcessors[ePresHintSheet], aData); + aRuleWalker->SetLevel(SheetType::PresHint, false, false); + if (mRuleProcessors[SheetType::PresHint]) + (*aCollectorFunc)(mRuleProcessors[SheetType::PresHint], aData); - aRuleWalker->SetLevel(eSVGAttrAnimationSheet, false, false); - if (mRuleProcessors[eSVGAttrAnimationSheet]) - (*aCollectorFunc)(mRuleProcessors[eSVGAttrAnimationSheet], aData); + aRuleWalker->SetLevel(SheetType::SVGAttrAnimation, false, false); + if (mRuleProcessors[SheetType::SVGAttrAnimation]) + (*aCollectorFunc)(mRuleProcessors[SheetType::SVGAttrAnimation], aData); nsRuleNode* lastSVGAttrAnimationRN = aRuleWalker->CurrentNode(); - aRuleWalker->SetLevel(eDocSheet, false, true); + aRuleWalker->SetLevel(SheetType::Doc, false, true); bool cutOffInheritance = false; if (mBindingManager && aElement) { // We can supply additional document-level sheets that should be walked. @@ -1143,8 +1152,8 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, &cutOffInheritance); } if (!skipUserStyles && !cutOffInheritance && // NOTE: different - mRuleProcessors[eDocSheet]) - (*aCollectorFunc)(mRuleProcessors[eDocSheet], aData); + mRuleProcessors[SheetType::Doc]) + (*aCollectorFunc)(mRuleProcessors[SheetType::Doc], aData); nsRuleNode* lastDocRN = aRuleWalker->CurrentNode(); bool haveImportantDocRules = !aRuleWalker->GetCheckForImportantRules(); nsTArray lastScopedRNs; @@ -1155,7 +1164,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, lastScopedRNs.SetLength(mScopedDocSheetRuleProcessors.Length()); haveImportantScopedRules.SetLength(mScopedDocSheetRuleProcessors.Length()); for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) { - aRuleWalker->SetLevel(eScopedDocSheet, false, true); + aRuleWalker->SetLevel(SheetType::ScopedDoc, false, true); nsCSSRuleProcessor* processor = static_cast(mScopedDocSheetRuleProcessors[i].get()); aData->mScope = processor->GetScopeElement(); @@ -1167,25 +1176,25 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, aData->mScope = nullptr; } nsRuleNode* lastScopedRN = aRuleWalker->CurrentNode(); - aRuleWalker->SetLevel(eStyleAttrSheet, false, true); - if (mRuleProcessors[eStyleAttrSheet]) - (*aCollectorFunc)(mRuleProcessors[eStyleAttrSheet], aData); + aRuleWalker->SetLevel(SheetType::StyleAttr, false, true); + if (mRuleProcessors[SheetType::StyleAttr]) + (*aCollectorFunc)(mRuleProcessors[SheetType::StyleAttr], aData); nsRuleNode* lastStyleAttrRN = aRuleWalker->CurrentNode(); bool haveImportantStyleAttrRules = !aRuleWalker->GetCheckForImportantRules(); - aRuleWalker->SetLevel(eOverrideSheet, false, true); - if (mRuleProcessors[eOverrideSheet]) - (*aCollectorFunc)(mRuleProcessors[eOverrideSheet], aData); + aRuleWalker->SetLevel(SheetType::Override, false, true); + if (mRuleProcessors[SheetType::Override]) + (*aCollectorFunc)(mRuleProcessors[SheetType::Override], aData); nsRuleNode* lastOvrRN = aRuleWalker->CurrentNode(); bool haveImportantOverrideRules = !aRuleWalker->GetCheckForImportantRules(); // This needs to match IsMoreSpecificThanAnimation() above. - aRuleWalker->SetLevel(eAnimationSheet, false, false); - (*aCollectorFunc)(mRuleProcessors[eAnimationSheet], aData); + aRuleWalker->SetLevel(SheetType::Animation, false, false); + (*aCollectorFunc)(mRuleProcessors[SheetType::Animation], aData); if (haveAnyImportantScopedRules) { for (uint32_t i = lastScopedRNs.Length(); i-- != 0; ) { - aRuleWalker->SetLevel(eScopedDocSheet, true, false); + aRuleWalker->SetLevel(SheetType::ScopedDoc, true, false); nsRuleNode* startRN = lastScopedRNs[i]; nsRuleNode* endRN = i == 0 ? lastDocRN : lastScopedRNs[i - 1]; if (haveImportantScopedRules[i]) { @@ -1205,7 +1214,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, #endif if (haveImportantDocRules) { - aRuleWalker->SetLevel(eDocSheet, true, false); + aRuleWalker->SetLevel(SheetType::Doc, true, false); AddImportantRules(lastDocRN, lastSVGAttrAnimationRN, aRuleWalker); // doc } #ifdef DEBUG @@ -1215,7 +1224,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, #endif if (haveImportantStyleAttrRules) { - aRuleWalker->SetLevel(eStyleAttrSheet, true, false); + aRuleWalker->SetLevel(SheetType::StyleAttr, true, false); AddImportantRules(lastStyleAttrRN, lastScopedRN, aRuleWalker); // style attr } #ifdef DEBUG @@ -1225,7 +1234,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, #endif if (haveImportantOverrideRules) { - aRuleWalker->SetLevel(eOverrideSheet, true, false); + aRuleWalker->SetLevel(SheetType::Override, true, false); AddImportantRules(lastOvrRN, lastStyleAttrRN, aRuleWalker); // override } #ifdef DEBUG @@ -1239,7 +1248,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, #endif if (haveImportantUserRules) { - aRuleWalker->SetLevel(eUserSheet, true, false); + aRuleWalker->SetLevel(SheetType::User, true, false); AddImportantRules(lastUserRN, lastAgentRN, aRuleWalker); //user } #ifdef DEBUG @@ -1249,7 +1258,7 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, #endif if (haveImportantUARules) { - aRuleWalker->SetLevel(eAgentSheet, true, false); + aRuleWalker->SetLevel(SheetType::Agent, true, false); AddImportantRules(lastAgentRN, lastRestrictionRN, aRuleWalker); //agent } #ifdef DEBUG @@ -1265,8 +1274,8 @@ nsStyleSet::FileRules(nsIStyleRuleProcessor::EnumFunc aCollectorFunc, #ifdef DEBUG nsRuleNode *lastImportantRN = aRuleWalker->CurrentNode(); #endif - aRuleWalker->SetLevel(eTransitionSheet, false, false); - (*aCollectorFunc)(mRuleProcessors[eTransitionSheet], aData); + aRuleWalker->SetLevel(SheetType::Transition, false, false); + (*aCollectorFunc)(mRuleProcessors[SheetType::Transition], aData); #ifdef DEBUG AssertNoCSSRules(aRuleWalker->CurrentNode(), lastImportantRN); #endif @@ -1282,18 +1291,18 @@ nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc, { NS_ASSERTION(mBatching == 0, "rule processors out of date"); - if (mRuleProcessors[eAgentSheet]) - (*aFunc)(mRuleProcessors[eAgentSheet], aData); + if (mRuleProcessors[SheetType::Agent]) + (*aFunc)(mRuleProcessors[SheetType::Agent], aData); bool skipUserStyles = aData->mElement->IsInNativeAnonymousSubtree(); - if (!skipUserStyles && mRuleProcessors[eUserSheet]) // NOTE: different - (*aFunc)(mRuleProcessors[eUserSheet], aData); + if (!skipUserStyles && mRuleProcessors[SheetType::User]) // NOTE: different + (*aFunc)(mRuleProcessors[SheetType::User], aData); - if (mRuleProcessors[ePresHintSheet]) - (*aFunc)(mRuleProcessors[ePresHintSheet], aData); + if (mRuleProcessors[SheetType::PresHint]) + (*aFunc)(mRuleProcessors[SheetType::PresHint], aData); - if (mRuleProcessors[eSVGAttrAnimationSheet]) - (*aFunc)(mRuleProcessors[eSVGAttrAnimationSheet], aData); + if (mRuleProcessors[SheetType::SVGAttrAnimation]) + (*aFunc)(mRuleProcessors[SheetType::SVGAttrAnimation], aData); bool cutOffInheritance = false; if (mBindingManager) { @@ -1305,19 +1314,19 @@ nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc, } } if (!skipUserStyles && !cutOffInheritance) { - if (mRuleProcessors[eDocSheet]) // NOTE: different - (*aFunc)(mRuleProcessors[eDocSheet], aData); + if (mRuleProcessors[SheetType::Doc]) // NOTE: different + (*aFunc)(mRuleProcessors[SheetType::Doc], aData); if (aData->mElement->IsElementInStyleScope()) { for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); i++) (*aFunc)(mScopedDocSheetRuleProcessors[i], aData); } } - if (mRuleProcessors[eStyleAttrSheet]) - (*aFunc)(mRuleProcessors[eStyleAttrSheet], aData); - if (mRuleProcessors[eOverrideSheet]) - (*aFunc)(mRuleProcessors[eOverrideSheet], aData); - (*aFunc)(mRuleProcessors[eAnimationSheet], aData); - (*aFunc)(mRuleProcessors[eTransitionSheet], aData); + if (mRuleProcessors[SheetType::StyleAttr]) + (*aFunc)(mRuleProcessors[SheetType::StyleAttr], aData); + if (mRuleProcessors[SheetType::Override]) + (*aFunc)(mRuleProcessors[SheetType::Override], aData); + (*aFunc)(mRuleProcessors[SheetType::Animation], aData); + (*aFunc)(mRuleProcessors[SheetType::Transition], aData); } static void @@ -1391,7 +1400,7 @@ nsStyleSet::ResolveStyleForRules(nsStyleContext* aParentContext, nsRuleWalker ruleWalker(mRuleTree, mAuthorStyleDisabled); // FIXME: Perhaps this should be passed in, but it probably doesn't // matter. - ruleWalker.SetLevel(eDocSheet, false, false); + ruleWalker.SetLevel(SheetType::Doc, false, false); for (uint32_t i = 0; i < aRules.Length(); i++) { ruleWalker.ForwardOnPossiblyCSSRule(aRules.ElementAt(i)); } @@ -1415,7 +1424,7 @@ nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext, // resulting context. It's also the right thing for the one case (the // transition manager's cover rule) where we put the result of this // function in the style context tree. - ruleWalker.SetLevel(eTransitionSheet, false, false); + ruleWalker.SetLevel(SheetType::Transition, false, false); for (int32_t i = 0; i < aRules.Count(); i++) { ruleWalker.ForwardOnPossiblyCSSRule(aRules.ObjectAt(i)); } @@ -1450,37 +1459,37 @@ nsStyleSet::ResolveStyleByAddingRules(nsStyleContext* aBaseContext, struct RuleNodeInfo { nsIStyleRule* mRule; - uint8_t mLevel; + SheetType mLevel; bool mIsImportant; bool mIsAnimationRule; }; struct CascadeLevel { - uint8_t mLevel; + SheetType mLevel; bool mIsImportant; bool mCheckForImportantRules; nsRestyleHint mLevelReplacementHint; }; static const CascadeLevel gCascadeLevels[] = { - { nsStyleSet::eAgentSheet, false, false, nsRestyleHint(0) }, - { nsStyleSet::eUserSheet, false, false, nsRestyleHint(0) }, - { nsStyleSet::ePresHintSheet, false, false, nsRestyleHint(0) }, - { nsStyleSet::eSVGAttrAnimationSheet, false, false, eRestyle_SVGAttrAnimations }, - { nsStyleSet::eDocSheet, false, false, nsRestyleHint(0) }, - { nsStyleSet::eScopedDocSheet, false, false, nsRestyleHint(0) }, - { nsStyleSet::eStyleAttrSheet, false, true, eRestyle_StyleAttribute | - eRestyle_StyleAttribute_Animations }, - { nsStyleSet::eOverrideSheet, false, false, nsRestyleHint(0) }, - { nsStyleSet::eAnimationSheet, false, false, eRestyle_CSSAnimations }, - { nsStyleSet::eScopedDocSheet, true, false, nsRestyleHint(0) }, - { nsStyleSet::eDocSheet, true, false, nsRestyleHint(0) }, - { nsStyleSet::eStyleAttrSheet, true, false, eRestyle_StyleAttribute | - eRestyle_StyleAttribute_Animations }, - { nsStyleSet::eOverrideSheet, true, false, nsRestyleHint(0) }, - { nsStyleSet::eUserSheet, true, false, nsRestyleHint(0) }, - { nsStyleSet::eAgentSheet, true, false, nsRestyleHint(0) }, - { nsStyleSet::eTransitionSheet, false, false, eRestyle_CSSTransitions }, + { SheetType::Agent, false, false, nsRestyleHint(0) }, + { SheetType::User, false, false, nsRestyleHint(0) }, + { SheetType::PresHint, false, false, nsRestyleHint(0) }, + { SheetType::SVGAttrAnimation, false, false, eRestyle_SVGAttrAnimations }, + { SheetType::Doc, false, false, nsRestyleHint(0) }, + { SheetType::ScopedDoc, false, false, nsRestyleHint(0) }, + { SheetType::StyleAttr, false, true, eRestyle_StyleAttribute | + eRestyle_StyleAttribute_Animations }, + { SheetType::Override, false, false, nsRestyleHint(0) }, + { SheetType::Animation, false, false, eRestyle_CSSAnimations }, + { SheetType::ScopedDoc, true, false, nsRestyleHint(0) }, + { SheetType::Doc, true, false, nsRestyleHint(0) }, + { SheetType::StyleAttr, true, false, eRestyle_StyleAttribute | + eRestyle_StyleAttribute_Animations }, + { SheetType::Override, true, false, nsRestyleHint(0) }, + { SheetType::User, true, false, nsRestyleHint(0) }, + { SheetType::Agent, true, false, nsRestyleHint(0) }, + { SheetType::Transition, false, false, eRestyle_CSSTransitions }, }; nsRuleNode* @@ -1545,7 +1554,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, if (doReplace) { switch (level->mLevel) { - case nsStyleSet::eAnimationSheet: { + case SheetType::Animation: { if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement || aPseudoType == nsCSSPseudoElements::ePseudo_before || aPseudoType == nsCSSPseudoElements::ePseudo_after) { @@ -1558,7 +1567,7 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, } break; } - case nsStyleSet::eTransitionSheet: { + case SheetType::Transition: { if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement || aPseudoType == nsCSSPseudoElements::ePseudo_before || aPseudoType == nsCSSPseudoElements::ePseudo_after) { @@ -1571,22 +1580,22 @@ nsStyleSet::RuleNodeWithReplacement(Element* aElement, } break; } - case nsStyleSet::eSVGAttrAnimationSheet: { + case SheetType::SVGAttrAnimation: { SVGAttrAnimationRuleProcessor* ruleProcessor = static_cast( - mRuleProcessors[eSVGAttrAnimationSheet].get()); + mRuleProcessors[SheetType::SVGAttrAnimation].get()); if (ruleProcessor && aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) { ruleProcessor->ElementRulesMatching(aElement, &ruleWalker); } break; } - case nsStyleSet::eStyleAttrSheet: { + case SheetType::StyleAttr: { if (!level->mIsImportant) { // First time through, we handle the non-!important rule. nsHTMLCSSStyleSheet* ruleProcessor = static_cast( - mRuleProcessors[eStyleAttrSheet].get()); + mRuleProcessors[SheetType::StyleAttr].get()); if (ruleProcessor) { lastScopedRN = ruleWalker.CurrentNode(); if (aPseudoType == @@ -1766,7 +1775,7 @@ nsStyleSet::WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType, nsRuleWalker* aRuleWalker) { // This needs to match GetPseudoRestriction in nsRuleNode.cpp. - aRuleWalker->SetLevel(eAgentSheet, false, false); + aRuleWalker->SetLevel(SheetType::Agent, false, false); if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLetter) aRuleWalker->Forward(mFirstLetterRule); else if (aPseudoType == nsCSSPseudoElements::ePseudo_firstLine) @@ -1778,7 +1787,7 @@ nsStyleSet::WalkRestrictionRule(nsCSSPseudoElements::Type aPseudoType, void nsStyleSet::WalkDisableTextZoomRule(Element* aElement, nsRuleWalker* aRuleWalker) { - aRuleWalker->SetLevel(eAgentSheet, false, false); + aRuleWalker->SetLevel(SheetType::Agent, false, false); if (aElement->IsSVGElement(nsGkAtoms::text)) aRuleWalker->Forward(mDisableTextZoomStyleRule); } @@ -2019,7 +2028,7 @@ nsStyleSet::AppendFontFaceRules(nsTArray& aArray) nsPresContext* presContext = PresContext(); for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { - if (gCSSSheetTypes[i] == eScopedDocSheet) + if (gCSSSheetTypes[i] == SheetType::ScopedDoc) continue; nsCSSRuleProcessor *ruleProc = static_cast (mRuleProcessors[gCSSSheetTypes[i]].get()); @@ -2037,7 +2046,7 @@ nsStyleSet::KeyframesRuleForName(const nsString& aName) nsPresContext* presContext = PresContext(); for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) { - if (gCSSSheetTypes[i] == eScopedDocSheet) + if (gCSSSheetTypes[i] == SheetType::ScopedDoc) continue; nsCSSRuleProcessor *ruleProc = static_cast (mRuleProcessors[gCSSSheetTypes[i]].get()); @@ -2059,7 +2068,7 @@ nsStyleSet::CounterStyleRuleForName(const nsAString& aName) nsPresContext* presContext = PresContext(); for (uint32_t i = ArrayLength(gCSSSheetTypes); i-- != 0; ) { - if (gCSSSheetTypes[i] == eScopedDocSheet) + if (gCSSSheetTypes[i] == SheetType::ScopedDoc) continue; nsCSSRuleProcessor *ruleProc = static_cast (mRuleProcessors[gCSSSheetTypes[i]].get()); @@ -2135,7 +2144,7 @@ nsStyleSet::AppendPageRules(nsTArray& aArray) nsPresContext* presContext = PresContext(); for (uint32_t i = 0; i < ArrayLength(gCSSSheetTypes); ++i) { - if (gCSSSheetTypes[i] == eScopedDocSheet) + if (gCSSSheetTypes[i] == SheetType::ScopedDoc) continue; nsCSSRuleProcessor* ruleProc = static_cast (mRuleProcessors[gCSSSheetTypes[i]].get()); @@ -2435,16 +2444,14 @@ nsStyleSet::MediumFeaturesChanged() // We can't use WalkRuleProcessors without a content node. nsPresContext* presContext = PresContext(); bool stylesChanged = false; - for (uint32_t i = 0; i < ArrayLength(mRuleProcessors); ++i) { - nsIStyleRuleProcessor *processor = mRuleProcessors[i]; + for (nsIStyleRuleProcessor* processor : mRuleProcessors) { if (!processor) { continue; } bool thisChanged = processor->MediumFeaturesChanged(presContext); stylesChanged = stylesChanged || thisChanged; } - for (uint32_t i = 0; i < mScopedDocSheetRuleProcessors.Length(); ++i) { - nsIStyleRuleProcessor *processor = mScopedDocSheetRuleProcessors[i]; + for (nsIStyleRuleProcessor* processor : mScopedDocSheetRuleProcessors) { bool thisChanged = processor->MediumFeaturesChanged(presContext); stylesChanged = stylesChanged || thisChanged; } @@ -2500,7 +2507,7 @@ nsStyleSet::InitialStyleRule() } bool -nsStyleSet::HasRuleProcessorUsedByMultipleStyleSets(sheetType aSheetType) +nsStyleSet::HasRuleProcessorUsedByMultipleStyleSets(SheetType aSheetType) { MOZ_ASSERT(size_t(aSheetType) < ArrayLength(mRuleProcessors)); if (!IsCSSSheetType(aSheetType) || !mRuleProcessors[aSheetType]) { diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index eb463d4c12..1978c6bcbb 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -14,7 +14,9 @@ #include "mozilla/Attributes.h" #include "mozilla/CSSStyleSheet.h" +#include "mozilla/EnumeratedArray.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/SheetType.h" #include "nsIStyleRuleProcessor.h" #include "nsBindingManager.h" @@ -304,46 +306,26 @@ class nsStyleSet final mBindingManager = aBindingManager; } - // The "origins" of the CSS cascade, from lowest precedence to - // highest (for non-!important rules). - enum sheetType { - eAgentSheet, // CSS - eUserSheet, // CSS - ePresHintSheet, - eSVGAttrAnimationSheet, - eDocSheet, // CSS - eScopedDocSheet, - eStyleAttrSheet, - eOverrideSheet, // CSS - eAnimationSheet, - eTransitionSheet, - eSheetTypeCount - // be sure to keep the number of bits in |mDirty| below and in - // NS_RULE_NODE_LEVEL_MASK updated when changing the number of sheet - // types - }; - // APIs to manipulate the style sheet lists. The sheets in each // list are stored with the most significant sheet last. - nsresult AppendStyleSheet(sheetType aType, nsIStyleSheet *aSheet); - nsresult PrependStyleSheet(sheetType aType, nsIStyleSheet *aSheet); - nsresult RemoveStyleSheet(sheetType aType, nsIStyleSheet *aSheet); - nsresult ReplaceSheets(sheetType aType, + nsresult AppendStyleSheet(mozilla::SheetType aType, nsIStyleSheet *aSheet); + nsresult PrependStyleSheet(mozilla::SheetType aType, nsIStyleSheet *aSheet); + nsresult RemoveStyleSheet(mozilla::SheetType aType, nsIStyleSheet *aSheet); + nsresult ReplaceSheets(mozilla::SheetType aType, const nsCOMArray &aNewSheets); - nsresult InsertStyleSheetBefore(sheetType aType, nsIStyleSheet *aNewSheet, + nsresult InsertStyleSheetBefore(mozilla::SheetType aType, + nsIStyleSheet *aNewSheet, nsIStyleSheet *aReferenceSheet); - nsresult DirtyRuleProcessors(sheetType aType); - // Enable/Disable entire author style level (Doc, ScopedDoc & PresHint levels) bool GetAuthorStyleDisabled(); nsresult SetAuthorStyleDisabled(bool aStyleDisabled); - int32_t SheetCount(sheetType aType) const { + int32_t SheetCount(mozilla::SheetType aType) const { return mSheets[aType].Count(); } - nsIStyleSheet* StyleSheetAt(sheetType aType, int32_t aIndex) const { + nsIStyleSheet* StyleSheetAt(mozilla::SheetType aType, int32_t aIndex) const { return mSheets[aType].ObjectAt(aIndex); } @@ -403,21 +385,23 @@ class nsStyleSet final nsIStyleRule* InitialStyleRule(); - bool HasRuleProcessorUsedByMultipleStyleSets(sheetType aSheetType); + bool HasRuleProcessorUsedByMultipleStyleSets(mozilla::SheetType aSheetType); // Tells the RestyleManager for the document using this style set // to drop any nsCSSSelector pointers it has. void ClearSelectors(); - private: +private: nsStyleSet(const nsStyleSet& aCopy) = delete; nsStyleSet& operator=(const nsStyleSet& aCopy) = delete; // Run mark-and-sweep GC on mRuleTree and mOldRuleTrees, based on mRoots. void GCRuleTrees(); + nsresult DirtyRuleProcessors(mozilla::SheetType aType); + // Update the rule processor list after a change to the style sheet list. - nsresult GatherRuleProcessors(sheetType aType); + nsresult GatherRuleProcessors(mozilla::SheetType aType); void AddImportantRules(nsRuleNode* aCurrLevelNode, nsRuleNode* aLastPrevLevelNode, @@ -485,11 +469,13 @@ class nsStyleSet final // The arrays for ePresHintSheet, eStyleAttrSheet, eTransitionSheet, // eAnimationSheet and eSVGAttrAnimationSheet are always empty. // (FIXME: We should reduce the storage needed for them.) - nsCOMArray mSheets[eSheetTypeCount]; + mozilla::EnumeratedArray> mSheets; // mRuleProcessors[eScopedDocSheet] is always null; rule processors // for scoped style sheets are stored in mScopedDocSheetRuleProcessors. - nsCOMPtr mRuleProcessors[eSheetTypeCount]; + mozilla::EnumeratedArray> mRuleProcessors; // Rule processors for HTML5 scoped style sheets, one per scope. nsTArray > mScopedDocSheetRuleProcessors; @@ -507,7 +493,7 @@ class nsStyleSet final unsigned mInReconstruct : 1; unsigned mInitFontFeatureValuesLookup : 1; unsigned mNeedsRestyleAfterEnsureUniqueInner : 1; - unsigned mDirty : 10; // one dirty bit is used per sheet type + unsigned mDirty : int(mozilla::SheetType::Count); // one bit per sheet type uint32_t mUnusedRuleNodeCount; // used to batch rule node GC nsTArray mRoots; // style contexts with no parent diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 9c12a6f3ce..bcb7772804 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -16,6 +16,7 @@ #include "nsPresContext.h" #include "nsIWidget.h" #include "nsCRTGlue.h" +#include "nsCSSParser.h" #include "nsCSSProps.h" #include "nsCOMPtr.h" @@ -1501,7 +1502,7 @@ IsAutonessEqual(const nsStyleSides& aSides1, const nsStyleSides& aSides2) nsChangeHint nsStylePosition::CalcDifference(const nsStylePosition& aOther, - nsStyleContext* aContext) const + const nsStyleVisibility* aOldStyleVisibility) const { nsChangeHint hint = nsChangeHint(0); @@ -1599,23 +1600,43 @@ nsStylePosition::CalcDifference(const nsStylePosition& aOther, bool heightChanged = mHeight != aOther.mHeight || mMinHeight != aOther.mMinHeight || mMaxHeight != aOther.mMaxHeight; - bool isVertical = WritingMode(aContext).IsVertical(); - if (isVertical ? widthChanged : heightChanged) { - // Block-size changes can affect descendant intrinsic sizes due to replaced - // elements with percentage bsizes in descendants which also have - // percentage bsizes. This is handled via nsChangeHint_UpdateComputedBSize - // which clears intrinsic sizes for frames that have such replaced elements. - NS_UpdateHint(hint, nsChangeHint_NeedReflow | - nsChangeHint_UpdateComputedBSize | - nsChangeHint_ReflowChangesSizeOrPosition); - } - if (isVertical ? heightChanged : widthChanged) { - // None of our inline-size differences can affect descendant intrinsic - // sizes and none of them need to force children to reflow. - NS_UpdateHint(hint, NS_SubtractHint(nsChangeHint_AllReflowHints, - NS_CombineHint(nsChangeHint_ClearDescendantIntrinsics, - nsChangeHint_NeedDirtyReflow))); + // If aOldStyleVisibility is null, we don't need to bother with any of + // these tests, since we know that the element never had its + // nsStyleVisibility accessed, which means it couldn't have done + // layout. + // Note that we pass an nsStyleVisibility here because we don't want + // to cause a new struct to be computed during + // nsStyleContext::CalcStyleDifference, which can lead to incorrect + // style data. + // It doesn't matter whether we're looking at the old or new + // visibility struct, since a change between vertical and horizontal + // writing-mode will cause a reframe, and it's easier to pass the old. + if (aOldStyleVisibility) { + bool isVertical = WritingMode(aOldStyleVisibility).IsVertical(); + if (isVertical ? widthChanged : heightChanged) { + // Block-size changes can affect descendant intrinsic sizes due to + // replaced elements with percentage bsizes in descendants which + // also have percentage bsizes. This is handled via + // nsChangeHint_UpdateComputedBSize which clears intrinsic sizes + // for frames that have such replaced elements. + NS_UpdateHint(hint, nsChangeHint_NeedReflow | + nsChangeHint_UpdateComputedBSize | + nsChangeHint_ReflowChangesSizeOrPosition); + } + + if (isVertical ? heightChanged : widthChanged) { + // None of our inline-size differences can affect descendant + // intrinsic sizes and none of them need to force children to + // reflow. + NS_UpdateHint(hint, NS_SubtractHint(nsChangeHint_AllReflowHints, + NS_CombineHint(nsChangeHint_ClearDescendantIntrinsics, + nsChangeHint_NeedDirtyReflow))); + } + } else { + if (widthChanged || heightChanged) { + NS_UpdateHint(hint, nsChangeHint_NeutralChange); + } } // If any of the offsets have changed, then return the respective hints @@ -2641,6 +2662,7 @@ nsStyleDisplay::nsStyleDisplay() mMixBlendMode = NS_STYLE_BLEND_NORMAL; mIsolation = NS_STYLE_ISOLATION_AUTO; mTouchAction = NS_STYLE_TOUCH_ACTION_AUTO; + mTopLayer = NS_STYLE_TOP_LAYER_NONE; mScrollBehavior = NS_STYLE_SCROLL_BEHAVIOR_AUTO; mScrollSnapTypeX = NS_STYLE_SCROLL_SNAP_TYPE_NONE; mScrollSnapTypeY = NS_STYLE_SCROLL_SNAP_TYPE_NONE; @@ -2695,6 +2717,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) , mOrient(aSource.mOrient) , mMixBlendMode(aSource.mMixBlendMode) , mIsolation(aSource.mIsolation) + , mTopLayer(aSource.mTopLayer) , mWillChangeBitField(aSource.mWillChangeBitField) , mWillChange(aSource.mWillChange) , mTouchAction(aSource.mTouchAction) @@ -2752,6 +2775,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const || mScrollSnapPointsX != aOther.mScrollSnapPointsX || mScrollSnapPointsY != aOther.mScrollSnapPointsY || mScrollSnapDestination != aOther.mScrollSnapDestination + || mTopLayer != aOther.mTopLayer || mResize != aOther.mResize) NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); @@ -3442,7 +3466,7 @@ nsStyleText::nsStyleText(void) mRubyPosition = NS_STYLE_RUBY_POSITION_OVER; mTextSizeAdjust = NS_STYLE_TEXT_SIZE_ADJUST_AUTO; mTextCombineUpright = NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE; - mControlCharacterVisibility = NS_STYLE_CONTROL_CHARACTER_VISIBILITY_VISIBLE; + mControlCharacterVisibility = nsCSSParser::ControlCharVisibilityDefault(); mLetterSpacing.SetNormalValue(); mLineHeight.SetNormalValue(); diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 7183ba6bc9..20be200e84 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -14,6 +14,7 @@ #include "mozilla/ArenaObjectID.h" #include "mozilla/Attributes.h" #include "mozilla/CSSVariableValues.h" +#include "mozilla/SheetType.h" #include "nsColor.h" #include "nsCoord.h" #include "nsMargin.h" @@ -35,6 +36,7 @@ class nsIURI; class nsStyleContext; class nsTextFrame; class imgIContainer; +struct nsStyleVisibility; // Includes nsStyleStructID. #include "nsStyleStructFwd.h" @@ -89,6 +91,10 @@ class imgIContainer; // Additional bits for nsRuleNode's mNoneBits: #define NS_RULE_NODE_HAS_ANIMATION_DATA 0x80000000 +static_assert(int(mozilla::SheetType::Count) - 1 <= + (NS_RULE_NODE_LEVEL_MASK >> NS_RULE_NODE_LEVEL_SHIFT), + "NS_RULE_NODE_LEVEL_MASK cannot fit SheetType"); + // The lifetime of these objects is managed by the presshell's arena. struct nsStyleFont { @@ -2015,6 +2021,7 @@ struct nsStyleDisplay { uint8_t mOrient; // [reset] see nsStyleConsts.h uint8_t mMixBlendMode; // [reset] see nsStyleConsts.h uint8_t mIsolation; // [reset] see nsStyleConsts.h + uint8_t mTopLayer; // [reset] see nsStyleConsts.h uint8_t mWillChangeBitField; // [reset] see nsStyleConsts.h. Stores a // bitfield representation of the properties // that are frequently queried. This should @@ -2262,7 +2269,7 @@ struct nsStylePosition { } nsChangeHint CalcDifference(const nsStylePosition& aOther, - nsStyleContext* aContext) const; + const nsStyleVisibility* aOldStyleVisibility) const; static nsChangeHint MaxDifference() { return NS_CombineHint(NS_STYLE_HINT_REFLOW, nsChangeHint(nsChangeHint_RecomputePosition | diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index d28fd772ab..9a50e2b408 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -145,12 +145,14 @@ CSSTransition::QueueEvents() if (!presContext) { return; } - nsTransitionManager* manager = presContext->TransitionManager(); - manager->QueueEvent( - TransitionEventInfo(owningElement, TransitionProperty(), - mEffect->Timing().mIterationDuration, - owningPseudoType)); + nsTransitionManager* manager = presContext->TransitionManager(); + manager->QueueEvent(TransitionEventInfo(owningElement, owningPseudoType, + TransitionProperty(), + mEffect->Timing() + .mIterationDuration, + AnimationTimeToTimeStamp(EffectEnd()), + this)); } bool diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h index aa8e806bb5..592369e7dc 100644 --- a/layout/style/nsTransitionManager.h +++ b/layout/style/nsTransitionManager.h @@ -59,7 +59,7 @@ struct ElementPropertyTransition : public dom::KeyframeEffectReadOnly // mProperties[0].mSegments[0].mFromValue, except when this transition // started as the reversal of another in-progress transition. // Needed so we can handle two reverses in a row. - mozilla::StyleAnimationValue mStartForReversingTest; + StyleAnimationValue mStartForReversingTest; // Likewise, the portion (in value space) of the "full" reversed // transition that we're actually covering. For example, if a :hover // effect has a transition that moves the element 10px to the right @@ -195,14 +195,21 @@ protected: } // namespace dom struct TransitionEventInfo { - nsCOMPtr mElement; + RefPtr mElement; + RefPtr mAnimation; InternalTransitionEvent mEvent; + TimeStamp mTimeStamp; - TransitionEventInfo(nsIContent *aElement, nsCSSProperty aProperty, + TransitionEventInfo(dom::Element* aElement, + nsCSSPseudoElements::Type aPseudoType, + nsCSSProperty aProperty, TimeDuration aDuration, - nsCSSPseudoElements::Type aPseudoType) + const TimeStamp& aTimeStamp, + dom::Animation* aAnimation) : mElement(aElement) + , mAnimation(aAnimation) , mEvent(true, eTransitionEnd) + , mTimeStamp(aTimeStamp) { // XXX Looks like nobody initialize WidgetEvent::time mEvent.propertyName = @@ -213,9 +220,11 @@ struct TransitionEventInfo { // InternalTransitionEvent doesn't support copy-construction, so we need // to ourselves in order to work with nsTArray - TransitionEventInfo(const TransitionEventInfo &aOther) + TransitionEventInfo(const TransitionEventInfo& aOther) : mElement(aOther.mElement) + , mAnimation(aOther.mAnimation) , mEvent(true, eTransitionEnd) + , mTimeStamp(aOther.mTimeStamp) { mEvent.AssignTransitionEventData(aOther.mEvent, false); } @@ -295,6 +304,7 @@ public: } void DispatchEvents() { mEventDispatcher.DispatchEvents(mPresContext); } + void SortEvents() { mEventDispatcher.SortEvents(); } void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } protected: diff --git a/layout/style/test/ListCSSProperties.cpp b/layout/style/test/ListCSSProperties.cpp index 12e6e0c8c1..64d587c182 100644 --- a/layout/style/test/ListCSSProperties.cpp +++ b/layout/style/test/ListCSSProperties.cpp @@ -113,7 +113,10 @@ const char *gInaccessibleProperties[] = { "-moz-script-size-multiplier", "-moz-script-min-size", "-moz-math-variant", - "-moz-math-display" // parsed by UA sheets only + "-moz-math-display", // parsed by UA sheets only + "-moz-top-layer", // parsed by UA sheets only + "-moz-window-dragging", // chrome-only internal properties + "-moz-window-shadow" // chrome-only internal properties }; inline int diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index 6aed36a953..8ea3e306cd 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -37,6 +37,10 @@ generated-files = css_properties.js [test_all_shorthand.html] [test_animations.html] skip-if = toolkit == 'android' +[test_animations_async_tests.html] +support-files = ../../reftests/fonts/Ahem.ttf file_animations_async_tests.html +[test_animations_dynamic_changes.html] +[test_animations_event_order.html] [test_animations_omta.html] [test_animations_omta_start.html] skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1041017 @@ -46,6 +50,9 @@ support-files = file_animations_pausing.html support-files = file_animations_playbackrate.html [test_any_dynamic.html] [test_at_rule_parse_serialize.html] +[test_attribute_selector_eof_behavior.html] +[test_background_blend_mode.html] +[test_box_size_keywords.html] [test_bug73586.html] [test_bug74880.html] [test_bug98997.html] @@ -90,6 +97,7 @@ support-files = bug453896_iframe.html [test_bug517224.html] support-files = bug517224.sjs [test_bug524175.html] +[test_bug525952.html] [test_bug534804.html] [test_bug573255.html] [test_bug580685.html] @@ -102,8 +110,12 @@ support-files = bug517224.sjs [test_bug645998.html] support-files = file_bug645998-1.css file_bug645998-2.css [test_bug716226.html] +[test_bug732153.html] +[test_bug732209.html] +support-files = bug732209-css.sjs [test_bug765590.html] [test_bug771043.html] +[test_bug795520.html] [test_bug798567.html] [test_bug798843_pref.html] [test_bug829816.html] @@ -126,15 +138,18 @@ skip-if = toolkit == 'android' [test_computed_style_prefs.html] [test_condition_text.html] [test_condition_text_assignment.html] -[test_default_computed_style.html] +[test_counter_descriptor_storage.html] +[test_counter_style.html] [test_css_cross_domain.html] skip-if = toolkit == 'android' #bug 536603 [test_css_eof_handling.html] [test_css_escape_api.html] [test_css_function_mismatched_parenthesis.html] +[test_css_loader_crossorigin_data_url.html] [test_css_supports.html] [test_css_supports_variables.html] [test_default_bidi_css.html] +[test_default_computed_style.html] [test_descriptor_storage.html] [test_descriptor_syntax_errors.html] [test_dont_use_document_colors.html] @@ -157,8 +172,8 @@ support-files = flexbox_layout_testcases.js [test_font_loading_api.html] support-files = BitPattern.woff [test_garbage_at_end_of_declarations.html] -[test_grid_item_shorthands.html] [test_grid_container_shorthands.html] +[test_grid_item_shorthands.html] [test_grid_shorthand_serialization.html] [test_group_insertRule.html] [test_hover_quirk.html] @@ -171,6 +186,9 @@ skip-if = toolkit == 'android' skip-if = toolkit == 'android' [test_initial_storage.html] [test_keyframes_rules.html] +[test_load_events_on_stylesheets.html] +[test_logical_properties.html] +skip-if = (toolkit == 'gonk' && debug) # Bug 1186224 [test_media_queries.html] skip-if = (toolkit == 'gonk' && debug) || android_version == '10' || android_version == '18' #debug-only failure; timed out #Android 2.3 and 4.3 aws only; bug 1030419 [test_media_queries_dynamic.html] @@ -179,6 +197,7 @@ skip-if = (toolkit == 'gonk' && debug) || android_version == '10' || android_ver [test_moz_device_pixel_ratio.html] [test_namespace_rule.html] [test_of_type_selectors.xhtml] +[test_page_parser.html] [test_parse_eof.html] [test_parse_ident.html] [test_parse_rule.html] @@ -186,9 +205,11 @@ skip-if = (toolkit == 'gonk' && debug) || android_version == '10' || android_ver [test_parser_diagnostics_unprintables.html] [test_pixel_lengths.html] [test_pointer-events.html] +[test_position_float_display.html] [test_position_sticky.html] support-files = file_position_sticky.html [test_priority_preservation.html] +[test_property_database.html] [test_property_syntax_errors.html] [test_pseudoelement_state.html] skip-if = (toolkit == 'gonk' && debug) || e10s #debug-only failure @@ -204,6 +225,7 @@ skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(monosp [test_selectors.html] skip-if = (toolkit == 'gonk' && debug) || toolkit == 'android' #bug 775227 #debug-only failure; timed out [test_selectors_on_anonymous_content.html] +[test_setPropertyWithNull.html] [test_shorthand_property_getters.html] [test_specified_value_serialization.html] [test_style_attribute_quirks.html] @@ -221,11 +243,12 @@ skip-if = android_version == '10' #Android 2.3 aws only; bug 1030432 [test_transitions_computed_value_combinations.html] [test_transitions_events.html] [test_transitions.html] +skip-if = (android_version == '18' && debug) # bug 1159532 +[test_transitions_bug537151.html] +[test_transitions_dynamic_changes.html] [test_transitions_per_property.html] skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 775227 # b2g(times out, needs more time + various failures) b2g-debug(times out, needs more time + various failures) b2g-desktop(times out, needs more time + various failures) [test_transitions_step_functions.html] -[test_transitions_dynamic_changes.html] -[test_transitions_bug537151.html] [test_unclosed_parentheses.html] [test_units_angle.html] [test_units_frequency.html] @@ -257,22 +280,3 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support) [test_visited_reftests.html] skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(bug 870262, :visited support) b2g-debug(bug 870262, :visited support) b2g-desktop(bug 870262, :visited support) -[test_bug525952.html] -[test_load_events_on_stylesheets.html] -[test_logical_properties.html] -[test_page_parser.html] -[test_bug732153.html] -[test_bug732209.html] -support-files = bug732209-css.sjs -[test_bug795520.html] -[test_background_blend_mode.html] -[test_property_database.html] -[test_counter_style.html] -[test_counter_descriptor_storage.html] -[test_position_float_display.html] -[test_animations_async_tests.html] -support-files = ../../reftests/fonts/Ahem.ttf file_animations_async_tests.html -[test_setPropertyWithNull.html] -[test_attribute_selector_eof_behavior.html] -[test_css_loader_crossorigin_data_url.html] -[test_attribute_selector_eof_behavior.html] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 38b1023de7..290588704a 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -71,6 +71,7 @@ var validGradientAndElementValues = [ "linear-gradient(red -99px, yellow, green, blue 120%)", "linear-gradient(#ffff00, #ef3, rgba(10, 20, 30, 0.4))", "linear-gradient(rgba(10, 20, 30, 0.4), #ffff00, #ef3)", + "linear-gradient(red, green calc(50% + 20px), blue)", "linear-gradient(to top, red, blue)", "linear-gradient(to bottom, red, blue)", @@ -231,6 +232,9 @@ var validGradientAndElementValues = [ "-moz-radial-gradient(.414rad bottom, red, blue)", "-moz-radial-gradient(cover, red, blue)", + "-moz-radial-gradient(cover circle, red, blue)", + "-moz-radial-gradient(contain, red, blue)", + "-moz-radial-gradient(contain ellipse, red, blue)", "-moz-radial-gradient(circle, red, blue)", "-moz-radial-gradient(ellipse closest-corner, red, blue)", "-moz-radial-gradient(farthest-side circle, red, blue)", @@ -354,6 +358,10 @@ var invalidGradientAndElementValues = [ "-moz-linear-gradient(10 10px -45deg, red, blue) repeat", "-moz-linear-gradient(10px 10 -45deg, red, blue) repeat", "linear-gradient(red -99, yellow, green, blue 120%)", + /* Invalid color, calc() or -moz-image-rect() function */ + "linear-gradient(red, rgb(0, rubbish, 0) 50%, red)", + "linear-gradient(red, red calc(50% + rubbish), red)", + "linear-gradient(to top calc(50% + rubbish), red, blue)", /* Old syntax */ "-moz-linear-gradient(10px 10px, 20px, 30px 30px, 40px, from(blue), to(red))", "-moz-radial-gradient(20px 20px, 10px 10px, from(green), to(#ff00ff))", @@ -588,6 +596,108 @@ var unbalancedGradientAndElementValues = [ "-moz-element(#a()", ]; +if (IsCSSPropertyPrefEnabled("layout.css.prefixes.webkit")) { + // Extend gradient lists with valid/invalid webkit-prefixed expressions: + validGradientAndElementValues.push( + // Basic linear-gradient syntax (valid when prefixed or unprefixed): + "-webkit-linear-gradient(red, green, blue)", + + // Angled linear-gradients (valid when prefixed or unprefixed): + "-webkit-linear-gradient(135deg, red, blue)", + "-webkit-linear-gradient(280deg, red 60%, blue)", + + // Basic radial-gradient syntax (valid when prefixed or unprefixed): + "-webkit-radial-gradient(circle, white, black)", + "-webkit-radial-gradient(circle, white, black)", + "-webkit-radial-gradient(ellipse closest-side, white, black)", + "-webkit-radial-gradient(circle farthest-corner, white, black)", + + // Contain/cover keywords (valid only for -moz/-webkit prefixed): + "-webkit-radial-gradient(cover, red, blue)", + "-webkit-radial-gradient(cover circle, red, blue)", + "-webkit-radial-gradient(contain, red, blue)", + "-webkit-radial-gradient(contain ellipse, red, blue)", + + // Initial side/corner/point (valid only for -moz/-webkit prefixed): + "-webkit-linear-gradient(left, red, blue)", + "-webkit-linear-gradient(right top, red, blue)", + "-webkit-linear-gradient(top right, red, blue)", + "-webkit-radial-gradient(right, red, blue)", + "-webkit-radial-gradient(left bottom, red, blue)", + "-webkit-radial-gradient(bottom left, red, blue)", + "-webkit-radial-gradient(center, red, blue)", + "-webkit-radial-gradient(center right, red, blue)", + "-webkit-radial-gradient(center center, red, blue)", + "-webkit-radial-gradient(center top, red, blue)", + "-webkit-radial-gradient(left 50%, red, blue)", + "-webkit-radial-gradient(20px top, red, blue)", + "-webkit-radial-gradient(20em 30%, red, blue)", + + // Point + keyword-sized shape (valid only for -moz/-webkit prefixed): + "-webkit-radial-gradient(center, circle closest-corner, red, blue)", + "-webkit-radial-gradient(10px 20px, cover circle, red, blue)", + "-webkit-radial-gradient(5em 50%, ellipse contain, red, blue)", + + // Repeating examples: + "-webkit-repeating-linear-gradient(red 10%, blue 30%)", + "-webkit-repeating-linear-gradient(30deg, pink 20px, orange 70px)", + "-webkit-repeating-linear-gradient(left, red, blue)", + "-webkit-repeating-linear-gradient(left, red 10%, blue 30%)", + "-webkit-repeating-radial-gradient(circle, red, blue 10%, red 20%)", + "-webkit-repeating-radial-gradient(circle farthest-corner, gray 10px, yellow 20px)", + "-webkit-repeating-radial-gradient(top left, circle, red, blue 4%, red 8%)" + ); + + invalidGradientAndElementValues.push( + // Syntax that's invalid for all types of gradients: + // * empty gradient expressions: + "-webkit-linear-gradient()", + "-webkit-radial-gradient()", + "-webkit-repeating-linear-gradient()", + "-webkit-repeating-radial-gradient()", + + // Linear syntax that's invalid for both -webkit & unprefixed, but valid + // for -moz: + // * initial which includes a length: + "-webkit-linear-gradient(10px, red, blue)", + "-webkit-linear-gradient(10px top, red, blue)", + // * initial which includes a side *and* an angle: + "-webkit-linear-gradient(bottom 30deg, red, blue)", + "-webkit-linear-gradient(30deg bottom, red, blue)", + "-webkit-linear-gradient(10px top 50deg, red, blue)", + "-webkit-linear-gradient(50deg 10px top, red, blue)", + // * initial which includes explicit "center": + "-webkit-linear-gradient(center, red, blue)", + "-webkit-linear-gradient(left center, red, blue)", + "-webkit-linear-gradient(top center, red, blue)", + "-webkit-linear-gradient(center top, red, blue)", + + // Linear syntax that's invalid for -webkit, but valid for -moz & unprefixed: + // * "to" syntax: + "-webkit-linear-gradient(to top, red, blue)", + + // * followed by angle: + "-webkit-radial-gradient(circle 10deg, red, blue)", + + // Radial syntax that's invalid for both -webkit & -moz, but valid for + // unprefixed: + // * " at " syntax: + "-webkit-radial-gradient(circle at left bottom, red, blue)", + // * explicitly-sized shape: + "-webkit-radial-gradient(circle 10px, red, blue)", + "-webkit-radial-gradient(ellipse 40px 20px, red, blue)", + + // Radial syntax that's invalid for both -webkit & unprefixed, but valid + // for -moz: + // * initial angle + "-webkit-radial-gradient(30deg, red, blue)", + // * initial angle/position combo + "-webkit-radial-gradient(top 30deg, red, blue)", + "-webkit-radial-gradient(left top 30deg, red, blue)", + "-webkit-radial-gradient(10px 20px 30deg, red, blue)" + ); +} + var gCSSProperties = { "animation": { domProp: "animation", @@ -596,7 +706,7 @@ var gCSSProperties = { subproperties: [ "animation-name", "animation-duration", "animation-timing-function", "animation-delay", "animation-direction", "animation-fill-mode", "animation-iteration-count", "animation-play-state" ], initial_values: [ "none none 0s 0s ease normal running 1.0", "none", "0s", "ease", "normal", "running", "1.0" ], other_values: [ "none none 0s 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) normal running 1.0", "bounce 1s linear 2s", "bounce 1s 2s linear", "bounce linear 1s 2s", "linear bounce 1s 2s", "linear 1s bounce 2s", "linear 1s 2s bounce", "1s bounce linear 2s", "1s bounce 2s linear", "1s 2s bounce linear", "1s linear bounce 2s", "1s linear 2s bounce", "1s 2s linear bounce", "bounce linear 1s", "bounce 1s linear", "linear bounce 1s", "linear 1s bounce", "1s bounce linear", "1s linear bounce", "1s 2s bounce", "1s bounce 2s", "bounce 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "bounce 1s", "1s bounce", "linear 1s", "1s linear", "1s 2s", "2s 1s", "bounce", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s bounce, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32bounce linear 2s", "1s -bounce linear 2s", "1s -\\32bounce linear 2s", "1s \\32 0bounce linear 2s", "1s -\\32 0bounce linear 2s", "1s \\2bounce linear 2s", "1s -\\2bounce linear 2s", "2s, 1s bounce", "1s bounce, 2s", "2s all, 1s bounce", "1s bounce, 2s all", "1s bounce, 2s none", "2s none, 1s bounce", "2s bounce, 1s all", "2s all, 1s bounce" ], - invalid_values: [ "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial", "2s all,, 1s bounce", "2s all, , 1s bounce" ] + invalid_values: [ "2s inherit", "inherit 2s", "2s bounce, 1s inherit", "2s inherit, 1s bounce", "2s initial", "2s all,, 1s bounce", "2s all, , 1s bounce", "bounce 1s cubic-bezier(0, rubbish) 2s", "bounce 1s steps(rubbish) 2s" ] }, "animation-delay": { domProp: "animationDelay", @@ -850,7 +960,7 @@ var gCSSProperties = { "2px 2px calc(2px + 1%) 2px", "1px 2px 2px 2px / 2px 2px calc(2px + 1%) 2px", ], - invalid_values: [ "2px -2px", "inherit 2px", "inherit / 2px", "2px inherit", "2px / inherit", "2px 2px 2px 2px 2px", "1px / 2px 2px 2px 2px 2px", "2", "2 2", "2px 2px 2px 2px / 2px 2px 2 2px" ] + invalid_values: [ "2px -2px", "inherit 2px", "inherit / 2px", "2px inherit", "2px / inherit", "2px 2px 2px 2px 2px", "1px / 2px 2px 2px 2px 2px", "2", "2 2", "2px 2px 2px 2px / 2px 2px 2 2px", "2px calc(0px + rubbish)" ] }, "border-bottom-left-radius": { domProp: "borderBottomLeftRadius", @@ -869,7 +979,7 @@ var gCSSProperties = { "calc(25px*3)", "calc(3*25px + 50%)", ], - invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] }, "border-bottom-right-radius": { domProp: "borderBottomRightRadius", @@ -888,7 +998,7 @@ var gCSSProperties = { "calc(25px*3)", "calc(3*25px + 50%)", ], - invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] }, "border-top-left-radius": { domProp: "borderTopLeftRadius", @@ -907,7 +1017,7 @@ var gCSSProperties = { "calc(25px*3)", "calc(3*25px + 50%)", ], - invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] }, "border-top-right-radius": { domProp: "borderTopRightRadius", @@ -926,7 +1036,7 @@ var gCSSProperties = { "calc(25px*3)", "calc(3*25px + 50%)", ], - invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px" ] + invalid_values: [ "-1px", "4px -2px", "inherit 2px", "2px inherit", "2", "2px 2", "2 2px", "2px calc(0px + rubbish)" ] }, "-moz-border-right-colors": { domProp: "MozBorderRightColors", @@ -1068,7 +1178,7 @@ var gCSSProperties = { initial_values: [ "auto", "auto auto" ], other_values: [ "3", "20px", "2 10px", "10px 2", "2 auto", "auto 2", "auto 50px", "50px auto" ], invalid_values: [ "5%", "-1px", "-1", "3 5", "10px 4px", "10 2px 5in", "30px -1", - "auto 3 5px", "5 auto 20px", "auto auto auto" ] + "auto 3 5px", "5 auto 20px", "auto auto auto", "calc(50px + rubbish) 2" ] }, "-moz-column-count": { domProp: "MozColumnCount", @@ -1734,7 +1844,8 @@ var gCSSProperties = { ], invalid_values: ["red", "auto", "none", "0.5 0.5", "40px #0000ff", "border", "center red", "right diagonal", - "#00ffff bottom"] + "#00ffff bottom", "0px calc(0px + rubbish)", + "0px 0px calc(0px + rubbish)"] }, "perspective-origin": { domProp: "perspectiveOrigin", @@ -1819,22 +1930,6 @@ var gCSSProperties = { other_values: [ "none", "text", "element", "elements", "all", "toggle", "tri-state", "-moz-all", "-moz-none" ], invalid_values: [] }, - "-moz-window-dragging": { - domProp: "MozWindowDragging", - inherited: true, - type: CSS_TYPE_LONGHAND, - initial_values: [ "no-drag" ], - other_values: [ "drag" ], - invalid_values: [ "none" ] - }, - "-moz-window-shadow": { - domProp: "MozWindowShadow", - inherited: false, - type: CSS_TYPE_LONGHAND, - initial_values: [ "default" ], - other_values: [ "none", "menu", "tooltip", "sheet" ], - invalid_values: [] - }, "background": { domProp: "background", inherited: false, @@ -1925,6 +2020,9 @@ var gCSSProperties = { "url(404.png) padding-box green padding-box", "transparent padding-box url(404.png) border-box", "transparent padding-box url(404.png) padding-box", + /* error inside functions */ + "-moz-image-rect(url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==), rubbish, 50%, 30%, 0) transparent", + "-moz-element(#a rubbish) black", ] }, "background-attachment": { @@ -2021,7 +2119,8 @@ var gCSSProperties = { "top 20%", "bottom 20%", "50% left", "top 50%", "50% bottom 10%", "right 10% 50%", "left right", "top bottom", "left 10% right", - "top 20px bottom 20px", "left left" ], + "top 20px bottom 20px", "left left", + "0px calc(0px + rubbish)"], quirks_values: { "20 20": "20px 20px", "10 5px": "10px 5px", @@ -2067,7 +2166,7 @@ var gCSSProperties = { "calc(-20px) calc(-50%)", "calc(-20%) calc(-50%)" ], - invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto", "5 3" ] + invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto", "5 3", "10px calc(10px + rubbish)" ] }, "border": { domProp: "border", @@ -2151,7 +2250,7 @@ var gCSSProperties = { subproperties: [ "border-left-color", "border-left-style", "border-left-width" ], initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ], other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ], - invalid_values: [ "5%", "5", "5 solid green" ] + invalid_values: [ "5%", "5", "5 solid green", "calc(5px + rubbish) green solid", "5px rgb(0, rubbish, 0) solid" ] }, "border-left-color": { domProp: "borderLeftColor", @@ -2244,7 +2343,7 @@ var gCSSProperties = { type: CSS_TYPE_LONGHAND, initial_values: [ "0", "0 0", "0px", "0 0px", "calc(0px)", "calc(0px) calc(0em)", "calc(2em - 2em) calc(3px + 7px - 10px)", "calc(-5px)", "calc(-5px) calc(-5px)" ], other_values: [ "3px", "4em 2px", "4em 0", "0px 2px", "calc(7px)", "0 calc(7px)", "calc(7px) 0", "calc(0px) calc(7px)", "calc(7px) calc(0px)", "7px calc(0px)", "calc(0px) 7px", "7px calc(0px)", "3px calc(2em)" ], - invalid_values: [ "0%", "0 0%", "-5px", "-5px -5px", "0 -5px", "-5px 0" ], + invalid_values: [ "0%", "0 0%", "-5px", "-5px -5px", "0 -5px", "-5px 0", "0 calc(0px + rubbish)" ], quirks_values: { "2px 5": "2px 5px", "7": "7px", @@ -2359,7 +2458,7 @@ var gCSSProperties = { "calc(2px) calc(2px) calc(2px)", "calc(2px) calc(2px) calc(2px) calc(2px)" ], - invalid_values: [ "3% 3%", "1px 1px 1px 1px 1px", "2px 2px, none", "red 2px 2px blue", "inherit, 2px 2px", "2px 2px, inherit", "2px 2px -5px", "inset 4px 4px black inset", "inset inherit", "inset none", "3 3", "3px 3", "3 3px", "3px 3px 3", "3px 3px 3px 3" ] + invalid_values: [ "3% 3%", "1px 1px 1px 1px 1px", "2px 2px, none", "red 2px 2px blue", "inherit, 2px 2px", "2px 2px, inherit", "2px 2px -5px", "inset 4px 4px black inset", "inset inherit", "inset none", "3 3", "3px 3", "3 3px", "3px 3px 3", "3px 3px 3px 3", "3px calc(3px + rubbish)", "3px 3px calc(3px + rubbish)", "3px 3px 3px calc(3px + rubbish)", "3px 3px 3px 3px rgb(0, rubbish, 0)" ] }, "caption-side": { domProp: "captionSide", @@ -2403,7 +2502,7 @@ var gCSSProperties = { /* XXX needs to be on pseudo-elements */ initial_values: [ "normal", "none" ], other_values: [ '""', "''", '"hello"', "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==')", 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==")', 'counter(foo)', 'counter(bar, upper-roman)', 'counters(foo, ".")', "counters(bar, '-', lower-greek)", "'-' counter(foo) '.'", "attr(title)", "open-quote", "close-quote", "no-open-quote", "no-close-quote", "close-quote attr(title) counters(foo, '.', upper-alpha)", "counter(foo, none)", "counters(bar, '.', none)", "attr(\\32)", "attr(\\2)", "attr(-\\2)", "attr(-\\32)", "counter(\\2)", "counters(\\32, '.')", "counter(-\\32, upper-roman)", "counters(-\\2, '-', lower-greek)", "counter(\\()", "counters(a\\+b, '.')", "counter(\\}, upper-alpha)", "-moz-alt-content", "counter(foo, symbols('*'))", "counter(foo, symbols(numeric '0' '1'))", "counters(foo, '.', symbols('*'))", "counters(foo, '.', symbols(numeric '0' '1'))" ], - invalid_values: [ 'counters(foo)', 'counter(foo, ".")', 'attr("title")', "attr('title')", "attr(2)", "attr(-2)", "counter(2)", "counters(-2, '.')", "-moz-alt-content 'foo'", "'foo' -moz-alt-content" ] + invalid_values: [ 'counters(foo)', 'counter(foo, ".")', 'attr("title")', "attr('title')", "attr(2)", "attr(-2)", "counter(2)", "counters(-2, '.')", "-moz-alt-content 'foo'", "'foo' -moz-alt-content", "counter(one, two, three) 'foo'" ] }, "counter-increment": { domProp: "counterIncrement", @@ -3341,7 +3440,7 @@ var gCSSProperties = { initial_values: [ "none" ], other_values: [ "underline", "overline", "line-through", "blink", "blink line-through underline", "underline overline line-through blink", "-moz-anchor-decoration", "blink -moz-anchor-decoration", "underline red solid", "underline #ff0000", "solid underline", "red underline", "#ff0000 underline", "dotted underline" ], - invalid_values: [ "none none", "underline none", "none underline", "blink none", "none blink", "line-through blink line-through", "underline overline line-through blink none", "underline overline line-throuh blink blink" ] + invalid_values: [ "none none", "underline none", "none underline", "blink none", "none blink", "line-through blink line-through", "underline overline line-through blink none", "underline overline line-throuh blink blink", "rgb(0, rubbish, 0) underline" ] }, "text-decoration-color": { domProp: "textDecorationColor", @@ -3412,7 +3511,7 @@ var gCSSProperties = { "calc(2px) calc(2px) calc(2px)", ], invalid_values: [ "3% 3%", "2px 2px -5px", "2px 2px 2px 2px", "2px 2px, none", "none, 2px 2px", "inherit, 2px 2px", "2px 2px, inherit", "2 2px", "2px 2", "2px 2px 2", "2px 2px 2px 2", - "calc(2px) calc(2px) calc(2px) calc(2px)" + "calc(2px) calc(2px) calc(2px) calc(2px)", "3px 3px calc(3px + rubbish)" ] }, "text-transform": { @@ -3449,7 +3548,7 @@ var gCSSProperties = { subproperties: [ "transition-property", "transition-duration", "transition-timing-function", "transition-delay" ], initial_values: [ "all 0s ease 0s", "all", "0s", "0s 0s", "ease" ], other_values: [ "all 0s cubic-bezier(0.25, 0.1, 0.25, 1.0) 0s", "width 1s linear 2s", "width 1s 2s linear", "width linear 1s 2s", "linear width 1s 2s", "linear 1s width 2s", "linear 1s 2s width", "1s width linear 2s", "1s width 2s linear", "1s 2s width linear", "1s linear width 2s", "1s linear 2s width", "1s 2s linear width", "width linear 1s", "width 1s linear", "linear width 1s", "linear 1s width", "1s width linear", "1s linear width", "1s 2s width", "1s width 2s", "width 1s 2s", "1s 2s linear", "1s linear 2s", "linear 1s 2s", "width 1s", "1s width", "linear 1s", "1s linear", "1s 2s", "2s 1s", "width", "linear", "1s", "height", "2s", "ease-in-out", "2s ease-in", "opacity linear", "ease-out 2s", "2s color, 1s width, 500ms height linear, 1s opacity 4s cubic-bezier(0.0, 0.1, 1.0, 1.0)", "1s \\32width linear 2s", "1s -width linear 2s", "1s -\\32width linear 2s", "1s \\32 0width linear 2s", "1s -\\32 0width linear 2s", "1s \\2width linear 2s", "1s -\\2width linear 2s", "2s, 1s width", "1s width, 2s", "2s all, 1s width", "1s width, 2s all", "2s all, 1s width", "2s width, 1s all" ], - invalid_values: [ "1s width, 2s none", "2s none, 1s width", "2s inherit", "inherit 2s", "2s width, 1s inherit", "2s inherit, 1s width", "2s initial", "1s width,,2s color", "1s width, ,2s color" ] + invalid_values: [ "1s width, 2s none", "2s none, 1s width", "2s inherit", "inherit 2s", "2s width, 1s inherit", "2s inherit, 1s width", "2s initial", "1s width,,2s color", "1s width, ,2s color", "bounce 1s cubic-bezier(0, rubbish) 2s", "bounce 1s steps(rubbish) 2s" ] }, "transition-delay": { domProp: "transitionDelay", @@ -3746,7 +3845,7 @@ var gCSSProperties = { prerequisites: { "color": "blue" }, initial_values: [ "black", "#000", "#000000", "rgb(0,0,0)", "rgba(0,0,0,1)" ], other_values: [ "green", "#fc3", "url('#myserver')", "url(foo.svg#myserver)", 'url("#myserver") green', "none", "currentColor", "context-fill", "context-stroke" ], - invalid_values: [ "000000", "ff00ff" ] + invalid_values: [ "000000", "ff00ff", "url('#myserver') rgb(0, rubbish, 0)" ] }, "fill-opacity": { domProp: "fillOpacity", @@ -4028,7 +4127,8 @@ var gCSSProperties = { "1px 2 3px", "1px 2 3 4px", "-1", - "1 -1" + "1 -1", + "0 1 calc(0px + rubbish)", ] }, "flex-basis": { @@ -5476,6 +5576,7 @@ if (SpecialPowers.getBoolPref("layout.css.clip-path-shapes.enabled")) { "circle(farthest-side closest-side)", "circle(20% 20%)", "circle(at farthest-side)", + "circle(calc(20px + rubbish))", "ellipse(at)", "ellipse(at 20% 20% 30%)", @@ -5487,6 +5588,7 @@ if (SpecialPowers.getBoolPref("layout.css.clip-path-shapes.enabled")) { "ellipse(farthest-side)", "ellipse(20%)", "ellipse(at farthest-side farthest-side)", + "ellipse(at top left calc(20px + rubbish))", "polygon(at)", "polygon(at 20% 20% 30%)", @@ -5508,6 +5610,8 @@ if (SpecialPowers.getBoolPref("layout.css.clip-path-shapes.enabled")) { "inset(1px at 3px)", "inset(1px round 1px // 2px)", "inset(1px round)", + "inset(1px calc(2px + rubbish))", + "inset(1px round 2px calc(3px + rubbish))", ], unbalanced_values: [ "polygon(30% 30%", @@ -5950,6 +6054,7 @@ if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) { "subgrid repeat(1, )", "subgrid repeat(2, (40px))", "subgrid repeat(2, foo)", + "40px calc(0px + rubbish)", ], unbalanced_values: [ "(foo] 40px", @@ -6610,3 +6715,24 @@ if (SpecialPowers.getBoolPref("layout.css.unset-value.enabled")) { gCSSProperties["text-align"].invalid_values.push("true left"); } } + +if (false) { + // TODO These properties are chrome-only, and are not exposed via CSSOM. + // We may still want to find a way to test them. See bug 1206999. + gCSSProperties["-moz-window-dragging"] = { + //domProp: "MozWindowDragging", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "no-drag" ], + other_values: [ "drag" ], + invalid_values: [ "none" ] + }; + gCSSProperties["-moz-window-shadow"] = { + //domProp: "MozWindowShadow", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "default" ], + other_values: [ "none", "menu", "tooltip", "sheet" ], + invalid_values: [] + }; +} diff --git a/layout/style/test/test_animations_dynamic_changes.html b/layout/style/test/test_animations_dynamic_changes.html new file mode 100644 index 0000000000..9ef72ac362 --- /dev/null +++ b/layout/style/test/test_animations_dynamic_changes.html @@ -0,0 +1,65 @@ + + + + + Test for Bug 978833 + + + + + +Mozilla Bug 978833 +

+
+
+
+ + diff --git a/layout/style/test/test_animations_event_order.html b/layout/style/test/test_animations_event_order.html new file mode 100644 index 0000000000..9a59c62833 --- /dev/null +++ b/layout/style/test/test_animations_event_order.html @@ -0,0 +1,552 @@ + + + + + + + Test for CSS Animation and Transition event ordering + (Bug 1183461) + + + + + + + +Mozilla Bug + 1183461 +
+
+
+
+
diff --git a/layout/style/ua.css b/layout/style/ua.css
index b396759269..717fc996f7 100644
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -267,7 +267,7 @@
 
 }
 
-*|*:not(:root):-moz-full-screen {
+*|*:-moz-full-screen:not(:root) {
   position: fixed !important;
   top: 0 !important;
   left: 0 !important;
@@ -285,6 +285,12 @@
   box-sizing: border-box !important;
 }
 
+/* Selectors here should match the check in
+ * nsViewportFrame.cpp:ShouldInTopLayerForFullscreen() */
+*|*:-moz-full-screen:not(:root):not(:-moz-browser-frame) {
+  -moz-top-layer: top !important;
+}
+
 /* XML parse error reporting */
 
 parsererror|parsererror {
diff --git a/mfbt/Array.h b/mfbt/Array.h
index b2ab578d4b..30e183bb24 100644
--- a/mfbt/Array.h
+++ b/mfbt/Array.h
@@ -11,6 +11,7 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/ReverseIterator.h"
 
 #include 
 
@@ -33,6 +34,27 @@ public:
     MOZ_ASSERT(aIndex < Length);
     return mArr[aIndex];
   }
+
+  typedef T*                        iterator;
+  typedef const T*                  const_iterator;
+  typedef ReverseIterator       reverse_iterator;
+  typedef ReverseIterator const_reverse_iterator;
+
+  // Methods for range-based for loops.
+  iterator begin() { return mArr; }
+  const_iterator begin() const { return mArr; }
+  const_iterator cbegin() const { return begin(); }
+  iterator end() { return mArr + Length; }
+  const_iterator end() const { return mArr + Length; }
+  const_iterator cend() const { return end(); }
+
+  // Methods for reverse iterating.
+  reverse_iterator rbegin() { return reverse_iterator(end()); }
+  const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
+  const_reverse_iterator crbegin() const { return rbegin(); }
+  reverse_iterator rend() { return reverse_iterator(begin()); }
+  const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
+  const_reverse_iterator crend() const { return rend(); }
 };
 
 template
diff --git a/mfbt/ArrayUtils.h b/mfbt/ArrayUtils.h
index 9566a3f40a..b572272dd1 100644
--- a/mfbt/ArrayUtils.h
+++ b/mfbt/ArrayUtils.h
@@ -20,6 +20,7 @@
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
+#include "mozilla/EnumeratedArray.h"
 #include "mozilla/TypeTraits.h"
 
 namespace mozilla {
@@ -64,6 +65,13 @@ ArrayLength(const Array& aArr)
   return N;
 }
 
+template
+MOZ_CONSTEXPR size_t
+ArrayLength(const EnumeratedArray& aArr)
+{
+  return size_t(N);
+}
+
 /*
  * Compute the address one past the last element of a constant-length array.
  *
diff --git a/mfbt/DebugOnly.h b/mfbt/DebugOnly.h
index 67d963cf1d..451d9bc373 100644
--- a/mfbt/DebugOnly.h
+++ b/mfbt/DebugOnly.h
@@ -51,8 +51,8 @@ public:
   void operator++(int) { value++; }
   void operator--(int) { value--; }
 
-  // Do not define operator+=() or operator-=() here.  These will coerce via
-  // the implicit cast and built-in operators.  Defining explicit methods here
+  // Do not define operator+=(), etc. here.  These will coerce via the
+  // implicit cast and built-in operators.  Defining explicit methods here
   // will create ambiguity the compiler can't deal with.
 
   T* operator&() { return &value; }
@@ -72,6 +72,9 @@ public:
   void operator--(int) { }
   DebugOnly& operator+=(const T&) { return *this; }
   DebugOnly& operator-=(const T&) { return *this; }
+  DebugOnly& operator&=(const T&) { return *this; }
+  DebugOnly& operator|=(const T&) { return *this; }
+  DebugOnly& operator^=(const T&) { return *this; }
 #endif
 
   /*
diff --git a/mfbt/EnumeratedArray.h b/mfbt/EnumeratedArray.h
index d6d7bad613..afd06ea805 100644
--- a/mfbt/EnumeratedArray.h
+++ b/mfbt/EnumeratedArray.h
@@ -46,7 +46,9 @@ public:
   static const size_t kSize = size_t(SizeAsEnumValue);
 
 private:
-  Array mArray;
+  typedef Array ArrayType;
+
+  ArrayType mArray;
 
 public:
   EnumeratedArray() {}
@@ -67,6 +69,27 @@ public:
   {
     return mArray[size_t(aIndex)];
   }
+
+  typedef typename ArrayType::iterator               iterator;
+  typedef typename ArrayType::const_iterator         const_iterator;
+  typedef typename ArrayType::reverse_iterator       reverse_iterator;
+  typedef typename ArrayType::const_reverse_iterator const_reverse_iterator;
+
+  // Methods for range-based for loops.
+  iterator begin() { return mArray.begin(); }
+  const_iterator begin() const { return mArray.begin(); }
+  const_iterator cbegin() const { return mArray.cbegin(); }
+  iterator end() { return mArray.end(); }
+  const_iterator end() const { return mArray.end(); }
+  const_iterator cend() const { return mArray.cend(); }
+
+  // Methods for reverse iterating.
+  reverse_iterator rbegin() { return mArray.rbegin(); }
+  const_reverse_iterator rbegin() const { return mArray.rbegin(); }
+  const_reverse_iterator crbegin() const { return mArray.crbegin(); }
+  reverse_iterator rend() { return mArray.rend(); }
+  const_reverse_iterator rend() const { return mArray.rend(); }
+  const_reverse_iterator crend() const { return mArray.crend(); }
 };
 
 } // namespace mozilla
diff --git a/mfbt/EnumeratedRange.h b/mfbt/EnumeratedRange.h
index 2fd0f881f1..fdca956321 100644
--- a/mfbt/EnumeratedRange.h
+++ b/mfbt/EnumeratedRange.h
@@ -171,6 +171,9 @@ private:
 #endif
 
 // Create a range to iterate from aBegin to aEnd, exclusive.
+//
+// (Once we can rely on std::underlying_type, we can remove the IntType
+// template parameter.)
 template
 inline detail::EnumeratedRange
 MakeEnumeratedRange(EnumType aBegin, EnumType aEnd)
@@ -189,11 +192,17 @@ MakeEnumeratedRange(EnumType aBegin, EnumType aEnd)
 
 // Create a range to iterate from EnumType(0) to aEnd, exclusive. EnumType(0)
 // should exist, but note that there is no way for us to ensure that it does!
-template
-inline detail::EnumeratedRange
+// Since the enumeration starts at EnumType(0), we know for sure that the values
+// will be in range of our deduced IntType.
+template
+inline detail::EnumeratedRange<
+  typename UnsignedStdintTypeForSize::Type,
+  EnumType>
 MakeEnumeratedRange(EnumType aEnd)
 {
-  return MakeEnumeratedRange(EnumType(0), aEnd);
+  return MakeEnumeratedRange<
+    typename UnsignedStdintTypeForSize::Type>(EnumType(0),
+                                                                aEnd);
 }
 
 #ifdef __GNUC__
diff --git a/mfbt/RangedArray.h b/mfbt/RangedArray.h
index 8ef2dd02a5..afe6267ff3 100644
--- a/mfbt/RangedArray.h
+++ b/mfbt/RangedArray.h
@@ -22,6 +22,10 @@ namespace mozilla {
 template
 class RangedArray
 {
+private:
+  typedef Array ArrayType;
+  ArrayType mArr;
+
 public:
   T& operator[](size_t aIndex)
   {
@@ -35,8 +39,26 @@ public:
     return mArr[aIndex - MinIndex];
   }
 
-private:
-  Array mArr;
+  typedef typename ArrayType::iterator               iterator;
+  typedef typename ArrayType::const_iterator         const_iterator;
+  typedef typename ArrayType::reverse_iterator       reverse_iterator;
+  typedef typename ArrayType::const_reverse_iterator const_reverse_iterator;
+
+  // Methods for range-based for loops.
+  iterator begin() { return mArr.begin(); }
+  const_iterator begin() const { return mArr.begin(); }
+  const_iterator cbegin() const { return mArr.cbegin(); }
+  iterator end() { return mArr.end(); }
+  const_iterator end() const { return mArr.end(); }
+  const_iterator cend() const { return mArr.cend(); }
+
+  // Methods for reverse iterating.
+  reverse_iterator rbegin() { return mArr.rbegin(); }
+  const_reverse_iterator rbegin() const { return mArr.rbegin(); }
+  const_reverse_iterator crbegin() const { return mArr.crbegin(); }
+  reverse_iterator rend() { return mArr.rend(); }
+  const_reverse_iterator rend() const { return mArr.rend(); }
+  const_reverse_iterator crend() const { return mArr.crend(); }
 };
 
 } // namespace mozilla
diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
index 967d2ef249..e9219eecac 100644
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -2698,6 +2698,13 @@ pref("layout.css.scroll-behavior.damping-ratio", "1.0");
 // fixed, as we don't want to expose more indexed properties on the Web.
 pref("layout.css.font-loading-api.enabled", false);
 
+// Should stray control characters be rendered visibly?
+#ifdef RELEASE_BUILD
+pref("layout.css.control-characters.visible", false);
+#else
+pref("layout.css.control-characters.visible", true);
+#endif
+
 // pref for which side vertical scrollbars should be on
 // 0 = end-side in UI direction
 // 1 = end-side in document/content direction
@@ -4398,9 +4405,6 @@ pref("canvas.image.cache.limit", 0);
 // toDataURL() and getImageData()
 pref("canvas.poisondata", false);
 
-// How many images to eagerly decode on a given page. 0 means "no limit".
-pref("image.onload.decode.limit", 0);
-
 // WebGL prefs
 #ifdef ANDROID
 // Disable MSAA on mobile.
diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json
index 586aa5f7c6..024a7ad40c 100644
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -18169,6 +18169,10 @@
       {
         "path": "webdriver/timeouts/page_load_timeouts_tests.py"
       },
+      {
+        "path": "web-animations/keyframe-effect/constructor.html",
+        "url": "/web-animations/keyframe-effect/constructor.html"
+      },
       {
         "path": "webdriver/user_input/clear_test.py"
       }
diff --git a/testing/web-platform/meta/quirks-mode/unitless-length.html.ini b/testing/web-platform/meta/quirks-mode/unitless-length.html.ini
index 2143d3db91..cc60a05755 100644
--- a/testing/web-platform/meta/quirks-mode/unitless-length.html.ini
+++ b/testing/web-platform/meta/quirks-mode/unitless-length.html.ini
@@ -9,12 +9,3 @@
   [calc(2 * 2px) (standards)]
     expected: FAIL
 
-  [1px calc(2) (quirks)]
-    expected: FAIL
-
-  [1px calc(2) (almost standards)]
-    expected: FAIL
-
-  [1px calc(2) (standards)]
-    expected: FAIL
-
diff --git a/testing/web-platform/meta/web-animations/keyframe-effect/constructor.html.ini b/testing/web-platform/meta/web-animations/keyframe-effect/constructor.html.ini
new file mode 100644
index 0000000000..128447204b
--- /dev/null
+++ b/testing/web-platform/meta/web-animations/keyframe-effect/constructor.html.ini
@@ -0,0 +1,53 @@
+[constructor.html]
+  type: testharness
+  [easing values are parsed correctly when passed to the KeyframeEffectReadOnly constructor in KeyframeTimingOptions]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216842
+  [composite values are parsed correctly when passed to the KeyframeEffectReadOnly constructor in PropertyIndexedKeyframes]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+  [composite values are parsed correctly when passed to the KeyframeEffectReadOnly constructor in Keyframe]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+  [composite values are parsed correctly when passed to the KeyframeEffectReadOnly constructor in KeyframeTimingOptions]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+  [a KeyframeEffectReadOnly can be constructed with a Keyframe sequence with different composite values, but the same composite value for a given offset]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216843
+  [a KeyframeEffectReadOnly can be constructed with a one property one value PropertyIndexedKeyframes specification]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly constructed with a one property one value PropertyIndexedKeyframes specification roundtrips]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly can be constructed with a one property one non-array value PropertyIndexedKeyframes specification]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly constructed with a one property one non-array value PropertyIndexedKeyframes specification roundtrips]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly can be constructed with a one property two value PropertyIndexedKeyframes specification where the first value is invalid]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly constructed with a one property two value PropertyIndexedKeyframes specification where the first value is invalid roundtrips]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly can be constructed with a one property two value PropertyIndexedKeyframes specification where the second value is invalid]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly constructed with a one property two value PropertyIndexedKeyframes specification where the second value is invalid roundtrips]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly can be constructed with a two property PropertyIndexedKeyframes specification where one property is missing from the first Keyframe]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly constructed with a two property PropertyIndexedKeyframes specification where one property is missing from the first Keyframe roundtrips]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly can be constructed with a two property PropertyIndexedKeyframes specification where one property is missing from the last Keyframe]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
+  [a KeyframeEffectReadOnly constructed with a two property PropertyIndexedKeyframes specification where one property is missing from the last Keyframe roundtrips]
+    expected: FAIL
+    bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1216844
diff --git a/testing/web-platform/tests/web-animations/keyframe-effect/constructor.html b/testing/web-platform/tests/web-animations/keyframe-effect/constructor.html
new file mode 100644
index 0000000000..45e544da3e
--- /dev/null
+++ b/testing/web-platform/tests/web-animations/keyframe-effect/constructor.html
@@ -0,0 +1,461 @@
+
+
+KeyframeEffectReadOnly constructor tests
+
+
+
+
+
+
+
+
+
+ + + diff --git a/toolkit/content/contentAreaUtils.js b/toolkit/content/contentAreaUtils.js index ab905be161..22bffb8d0f 100644 --- a/toolkit/content/contentAreaUtils.js +++ b/toolkit/content/contentAreaUtils.js @@ -113,7 +113,7 @@ function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache, .getService(Components.interfaces.imgITools) .getImgCacheForDocument(aDoc); var props = - imageCache.findEntryProperties(makeURI(aURL, getCharsetforSave(null))); + imageCache.findEntryProperties(makeURI(aURL, getCharsetforSave(null)), aDoc); if (props) { contentType = props.get("type", nsISupportsCString); contentDisposition = props.get("content-disposition", diff --git a/xpcom/base/ErrorList.h b/xpcom/base/ErrorList.h index 092ffc5547..6e993f28e9 100644 --- a/xpcom/base/ErrorList.h +++ b/xpcom/base/ErrorList.h @@ -934,6 +934,14 @@ ERROR(NS_ERROR_SIGNED_APP_MANIFEST_INVALID, FAILURE(1)), #undef MODULE + /* ======================================================================= */ + /* 39: NS_ERROR_MODULE_DOM_ANIM */ + /* ======================================================================= */ +#define MODULE NS_ERROR_MODULE_DOM_ANIM + ERROR(NS_ERROR_DOM_ANIM_MISSING_PROPS_ERR, FAILURE(1)), + ERROR(NS_ERROR_DOM_ANIM_NO_TARGET_ERR, FAILURE(2)), +#undef MODULE + /* ======================================================================= */ /* 51: NS_ERROR_MODULE_GENERAL */ /* ======================================================================= */ diff --git a/xpcom/base/nsError.h b/xpcom/base/nsError.h index 28e994c9e6..7fccef1f8d 100644 --- a/xpcom/base/nsError.h +++ b/xpcom/base/nsError.h @@ -76,6 +76,7 @@ #define NS_ERROR_MODULE_DOM_FILESYSTEM 36 #define NS_ERROR_MODULE_DOM_BLUETOOTH 37 #define NS_ERROR_MODULE_SIGNED_APP 38 +#define NS_ERROR_MODULE_DOM_ANIM 39 /* NS_ERROR_MODULE_GENERAL should be used by modules that do not * care if return code values overlap. Callers of methods that