diff --git a/docshell/base/crashtests/crashtests.list b/docshell/base/crashtests/crashtests.list index bb65b1a703..aab882f152 100644 --- a/docshell/base/crashtests/crashtests.list +++ b/docshell/base/crashtests/crashtests.list @@ -6,9 +6,9 @@ load 430628-1.html load 432114-1.html load 432114-2.html load 436900-1.html -asserts(0-3) load 436900-2.html # bug 566159 +asserts(0-1) load 436900-2.html # bug 566159 load 500328-1.html load 514779-1.xhtml load 614499-1.html load 678872-1.html -skip-if(Android||B2G||browserIsRemote) pref(dom.disable_open_during_load,false) load 914521.html +skip-if(Android||B2G) pref(dom.disable_open_during_load,false) load 914521.html diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp index 672d67ccd2..cdee486e43 100644 --- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -75,7 +75,7 @@ Animation::SetTimeline(AnimationTimeline* aTimeline) } if (mTimeline) { - mTimeline->RemoveAnimation(*this); + mTimeline->NotifyAnimationUpdated(*this); } mTimeline = aTimeline; @@ -362,15 +362,18 @@ Animation::SetCurrentTimeAsDouble(const Nullable& aCurrentTime, void Animation::Tick() { - // Since we are not guaranteed to get only one call per refresh driver tick, - // it's possible that mPendingReadyTime is set to a time in the future. - // In that case, we should wait until the next refresh driver tick before - // resuming. + // Finish pending if we have a pending ready time, but only if we also + // have an active timeline. if (mPendingState != PendingState::NotPending && !mPendingReadyTime.IsNull() && mTimeline && - !mTimeline->GetCurrentTime().IsNull() && - mPendingReadyTime.Value() <= mTimeline->GetCurrentTime().Value()) { + !mTimeline->GetCurrentTime().IsNull()) { + // Even though mPendingReadyTime is initialized using TimeStamp::Now() + // during the *previous* tick of the refresh driver, it can still be + // ahead of the *current* timeline time when we are using the + // vsync timer so we need to clamp it to the timeline time. + mPendingReadyTime.SetValue(std::min(mTimeline->GetCurrentTime().Value(), + mPendingReadyTime.Value())); FinishPendingAt(mPendingReadyTime.Value()); mPendingReadyTime.SetNull(); } @@ -577,7 +580,7 @@ Animation::CanThrottle() const void Animation::ComposeStyle(RefPtr& aStyleRule, nsCSSPropertySet& aSetProperties, - bool& aNeedsRefreshes) + bool& aStyleChanging) { if (!mEffect) { return; @@ -585,9 +588,8 @@ Animation::ComposeStyle(RefPtr& aStyleRule, AnimationPlayState playState = PlayState(); if (playState == AnimationPlayState::Running || - playState == AnimationPlayState::Pending || - HasEndEventToQueue()) { - aNeedsRefreshes = true; + playState == AnimationPlayState::Pending) { + aStyleChanging = true; } if (!IsInEffect()) { @@ -845,39 +847,8 @@ Animation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag) UpdateFinishedState(aSeekFlag, aSyncNotifyFlag); UpdateEffect(); - // Unconditionally Add/Remove from the timeline. This is ok because if the - // animation has already been added/removed (which will be true more often - // than not) the work done by AnimationTimeline/DocumentTimeline is still - // negligible and its easier than trying to detect whenever we are switching - // to/from being relevant. - // - // We need to do this after calling UpdateEffect since it updates some - // cached state used by IsRelevant. - // - // Note that we only store relevant animations on the timeline since they - // are the only ones that need ticks and are the only ones returned from - // AnimationTimeline::GetAnimations. Storing any more than that would mean - // that we fail to garbage collect irrelevant animations since the timeline - // keeps a strong reference to each animation. - // - // Once we tick animations from the their timeline, and once we expect - // timelines to go in and out of being inactive, we will also need to store - // non-idle animations that are waiting for their timeline to become active - // on their timeline (as otherwise once the timeline becomes active it will - // have no way of notifying its animations). For now, however, we can - // simply store just the relevant animations. if (mTimeline) { - // FIXME: Once we expect animations to go back and forth betweeen being - // inactive and active, we will need to store more than just relevant - // animations on the timeline. This is because an animation might be - // deemed irrelevant because its timeline is inactive. If it is removed - // from the timeline at that point the timeline will have no way of - // getting the animation to add itself again once it becomes active. - if (IsRelevant()) { - mTimeline->AddAnimation(*this); - } else { - mTimeline->RemoveAnimation(*this); - } + mTimeline->NotifyAnimationUpdated(*this); } } diff --git a/dom/animation/Animation.h b/dom/animation/Animation.h index f0bf1929a4..4bbdfa9258 100644 --- a/dom/animation/Animation.h +++ b/dom/animation/Animation.h @@ -140,6 +140,12 @@ public: virtual void CancelFromStyle() { DoCancel(); } virtual void Tick(); + bool NeedsTicks() const + { + AnimationPlayState playState = PlayState(); + return playState == AnimationPlayState::Running || + playState == AnimationPlayState::Pending; + } /** * Set the time to use for starting or pausing a pending animation. @@ -279,26 +285,13 @@ public: * if any. * Any properties already contained in |aSetProperties| are not changed. Any * properties that are changed are added to |aSetProperties|. - * |aNeedsRefreshes| will be set to true if this animation expects to update + * |aStyleChanging| will be set to true if this animation expects to update * the style rule on the next refresh driver tick as well (because it * is running and has an effect to sample). */ void ComposeStyle(RefPtr& aStyleRule, nsCSSPropertySet& aSetProperties, - bool& aNeedsRefreshes); - - - // FIXME: Because we currently determine if we need refresh driver ticks - // during restyling (specifically ComposeStyle above) and not necessarily - // during a refresh driver tick, we can arrive at a situation where we - // have finished running an animation but are waiting until the next tick - // to queue the final end event. This method tells us when we are in that - // situation so we can avoid unregistering from the refresh driver until - // we've finished dispatching events. - // - // This is a temporary measure until bug 1195180 is done and we can do all - // our registering and unregistering within a tick callback. - virtual bool HasEndEventToQueue() const { return false; } + bool& aStyleChanging); void NotifyEffectTimingUpdated(); diff --git a/dom/animation/AnimationEffectReadonly.h b/dom/animation/AnimationEffectReadonly.h index 9986c8059a..7c38ca8bc3 100644 --- a/dom/animation/AnimationEffectReadonly.h +++ b/dom/animation/AnimationEffectReadonly.h @@ -4,24 +4,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef mozilla_dom_AnimationEffect_h -#define mozilla_dom_AnimationEffect_h +#ifndef mozilla_dom_AnimationEffectReadOnly_h +#define mozilla_dom_AnimationEffectReadOnly_h -#include "nsISupports.h" -#include "nsWrapperCache.h" +#include "mozilla/dom/BindingDeclarations.h" #include "nsCycleCollectionParticipant.h" -#include "nsCOMPtr.h" +#include "nsWrapperCache.h" namespace mozilla { namespace dom { -class AnimationEffectReadOnly - : public nsISupports - , public nsWrapperCache -{ -protected: - virtual ~AnimationEffectReadOnly() { } +struct ComputedTimingProperties; +class AnimationEffectReadOnly : public nsISupports, + public nsWrapperCache +{ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationEffectReadOnly) @@ -33,6 +30,13 @@ public: nsISupports* GetParentObject() const { return mParent; } + virtual void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const + { + } + +protected: + virtual ~AnimationEffectReadOnly() = default; + protected: nsCOMPtr mParent; }; @@ -40,4 +44,4 @@ protected: } // namespace dom } // namespace mozilla -#endif // mozilla_dom_AnimationEffect_h +#endif // mozilla_dom_AnimationEffectReadOnly_h diff --git a/dom/animation/AnimationTimeline.cpp b/dom/animation/AnimationTimeline.cpp index 8c11e9da73..cd020e5b00 100644 --- a/dom/animation/AnimationTimeline.cpp +++ b/dom/animation/AnimationTimeline.cpp @@ -10,7 +10,8 @@ namespace mozilla { namespace dom { -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationTimeline, mWindow, mAnimations) +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationTimeline, mWindow, + mAnimationOrder) NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationTimeline) NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationTimeline) @@ -31,13 +32,16 @@ AnimationTimeline::GetAnimations(AnimationSequence& aAnimations) } } - for (auto iter = mAnimations.Iter(); !iter.Done(); iter.Next()) { - Animation* animation = iter.Get()->GetKey(); + aAnimations.SetCapacity(mAnimationOrder.Length()); - MOZ_ASSERT(animation->IsRelevant(), - "Animations registered with a timeline should be relevant"); - MOZ_ASSERT(animation->GetTimeline() == this, - "Animation should refer to this timeline"); + for (Animation* animation : mAnimationOrder) { + + // Skip animations which are no longer relevant or which have been + // associated with another timeline. These animations will be removed + // on the next tick. + if (!animation->IsRelevant() || animation->GetTimeline() != this) { + continue; + } // Bug 1174575: Until we implement a suitable PseudoElement interface we // don't have anything to return for the |target| attribute of @@ -59,15 +63,14 @@ AnimationTimeline::GetAnimations(AnimationSequence& aAnimations) } void -AnimationTimeline::AddAnimation(Animation& aAnimation) +AnimationTimeline::NotifyAnimationUpdated(Animation& aAnimation) { - mAnimations.PutEntry(&aAnimation); -} + if (mAnimations.Contains(&aAnimation)) { + return; + } -void -AnimationTimeline::RemoveAnimation(Animation& aAnimation) -{ - mAnimations.RemoveEntry(&aAnimation); + mAnimations.PutEntry(&aAnimation); + mAnimationOrder.AppendElement(&aAnimation); } } // namespace dom diff --git a/dom/animation/AnimationTimeline.h b/dom/animation/AnimationTimeline.h index 2f435edb27..24708ec1d4 100644 --- a/dom/animation/AnimationTimeline.h +++ b/dom/animation/AnimationTimeline.h @@ -17,6 +17,12 @@ #include "nsIGlobalObject.h" #include "nsTHashtable.h" +// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to +// GetTickCount(). +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + namespace mozilla { namespace dom { @@ -77,15 +83,29 @@ public: virtual TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const = 0; - void AddAnimation(Animation& aAnimation); - void RemoveAnimation(Animation& aAnimation); + /** + * Inform this timeline that |aAnimation| which is or was observing the + * timeline, has been updated. This serves as both the means to associate + * AND disassociate animations with a timeline. The timeline itself will + * determine if it needs to begin, continue or stop tracking this animation. + */ + virtual void NotifyAnimationUpdated(Animation& aAnimation); protected: nsCOMPtr mWindow; // Animations observing this timeline - typedef nsTHashtable> AnimationSet; - AnimationSet mAnimations; + // + // We store them in (a) a hashset for quick lookup, and (b) an array + // to maintain a fixed sampling order. + // + // The array keeps a strong reference to each animation in order + // to save some addref/release traffic and because we never dereference + // the pointers in the hashset. + typedef nsTHashtable> AnimationSet; + typedef nsTArray> AnimationArray; + AnimationSet mAnimations; + AnimationArray mAnimationOrder; }; } // namespace dom diff --git a/dom/animation/ComputedTimingFunction.cpp b/dom/animation/ComputedTimingFunction.cpp new file mode 100644 index 0000000000..2d7c321d82 --- /dev/null +++ b/dom/animation/ComputedTimingFunction.cpp @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ComputedTimingFunction.h" +#include "nsStyleUtil.h" + +namespace mozilla { + +void +ComputedTimingFunction::Init(const nsTimingFunction &aFunction) +{ + mType = aFunction.mType; + if (nsTimingFunction::IsSplineType(mType)) { + mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1, + aFunction.mFunc.mX2, aFunction.mFunc.mY2); + } else { + mSteps = aFunction.mSteps; + mStepSyntax = aFunction.mStepSyntax; + } +} + +static inline double +StepEnd(uint32_t aSteps, double aPortion) +{ + MOZ_ASSERT(0.0 <= aPortion && aPortion <= 1.0, "out of range"); + uint32_t step = uint32_t(aPortion * aSteps); // floor + return double(step) / double(aSteps); +} + +double +ComputedTimingFunction::GetValue(double aPortion) const +{ + if (HasSpline()) { + return mTimingFunction.GetSplineValue(aPortion); + } + if (mType == nsTimingFunction::Type::StepStart) { + // There are diagrams in the spec that seem to suggest this check + // and the bounds point should not be symmetric with StepEnd, but + // should actually step up at rather than immediately after the + // fraction points. However, we rely on rounding negative values + // up to zero, so we can't do that. And it's not clear the spec + // really meant it. + return 1.0 - StepEnd(mSteps, 1.0 - aPortion); + } + MOZ_ASSERT(mType == nsTimingFunction::Type::StepEnd, "bad type"); + return StepEnd(mSteps, aPortion); +} + +int32_t +ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const +{ + if (mType != aRhs.mType) { + return int32_t(mType) - int32_t(aRhs.mType); + } + + if (mType == nsTimingFunction::Type::CubicBezier) { + int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction); + if (order != 0) { + return order; + } + } else if (mType == nsTimingFunction::Type::StepStart || + mType == nsTimingFunction::Type::StepEnd) { + if (mSteps != aRhs.mSteps) { + return int32_t(mSteps) - int32_t(aRhs.mSteps); + } + if (mStepSyntax != aRhs.mStepSyntax) { + return int32_t(mStepSyntax) - int32_t(aRhs.mStepSyntax); + } + } + + return 0; +} + +void +ComputedTimingFunction::AppendToString(nsAString& aResult) const +{ + switch (mType) { + case nsTimingFunction::Type::CubicBezier: + nsStyleUtil::AppendCubicBezierTimingFunction(mTimingFunction.X1(), + mTimingFunction.Y1(), + mTimingFunction.X2(), + mTimingFunction.Y2(), + aResult); + break; + case nsTimingFunction::Type::StepStart: + case nsTimingFunction::Type::StepEnd: + nsStyleUtil::AppendStepsTimingFunction(mType, mSteps, mStepSyntax, + aResult); + break; + default: + nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult); + break; + } +} + +} // namespace mozilla diff --git a/dom/animation/ComputedTimingFunction.h b/dom/animation/ComputedTimingFunction.h new file mode 100644 index 0000000000..fa1b335b3a --- /dev/null +++ b/dom/animation/ComputedTimingFunction.h @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_ComputedTimingFunction_h +#define mozilla_ComputedTimingFunction_h + +#include "nsSMILKeySpline.h" // nsSMILKeySpline +#include "nsStyleStruct.h" // nsTimingFunction + +namespace mozilla { + +class ComputedTimingFunction +{ +public: + void Init(const nsTimingFunction &aFunction); + double GetValue(double aPortion) const; + const nsSMILKeySpline* GetFunction() const + { + NS_ASSERTION(HasSpline(), "Type mismatch"); + return &mTimingFunction; + } + nsTimingFunction::Type GetType() const { return mType; } + bool HasSpline() const { return nsTimingFunction::IsSplineType(mType); } + uint32_t GetSteps() const { return mSteps; } + nsTimingFunction::StepSyntax GetStepSyntax() const { return mStepSyntax; } + bool operator==(const ComputedTimingFunction& aOther) const + { + return mType == aOther.mType && + (HasSpline() ? + mTimingFunction == aOther.mTimingFunction : + (mSteps == aOther.mSteps && + mStepSyntax == aOther.mStepSyntax)); + } + bool operator!=(const ComputedTimingFunction& aOther) const + { + return !(*this == aOther); + } + int32_t Compare(const ComputedTimingFunction& aRhs) const; + void AppendToString(nsAString& aResult) const; + +private: + nsTimingFunction::Type mType; + nsSMILKeySpline mTimingFunction; + uint32_t mSteps; + nsTimingFunction::StepSyntax mStepSyntax; +}; + +} // namespace mozilla + +#endif // mozilla_dom_AnimationEffectReadOnly_h diff --git a/dom/animation/DocumentTimeline.cpp b/dom/animation/DocumentTimeline.cpp index 8412833644..a0c48a3c2a 100644 --- a/dom/animation/DocumentTimeline.cpp +++ b/dom/animation/DocumentTimeline.cpp @@ -8,10 +8,11 @@ #include "mozilla/dom/DocumentTimelineBinding.h" #include "AnimationUtils.h" #include "nsContentUtils.h" +#include "nsDOMMutationObserver.h" +#include "nsDOMNavigationTiming.h" #include "nsIPresShell.h" #include "nsPresContext.h" #include "nsRefreshDriver.h" -#include "nsDOMNavigationTiming.h" namespace mozilla { namespace dom { @@ -91,6 +92,86 @@ DocumentTimeline::ToTimelineTime(const TimeStamp& aTimeStamp) const return result; } +void +DocumentTimeline::NotifyAnimationUpdated(Animation& aAnimation) +{ + AnimationTimeline::NotifyAnimationUpdated(aAnimation); + + if (!mIsObservingRefreshDriver) { + nsRefreshDriver* refreshDriver = GetRefreshDriver(); + if (refreshDriver) { + refreshDriver->AddRefreshObserver(this, Flush_Style); + mIsObservingRefreshDriver = true; + } + } +} + +void +DocumentTimeline::WillRefresh(mozilla::TimeStamp aTime) +{ + MOZ_ASSERT(mIsObservingRefreshDriver); + + bool needsTicks = false; + AnimationArray animationsToKeep(mAnimationOrder.Length()); + + nsAutoAnimationMutationBatch mb(mDocument); + + for (Animation* animation : mAnimationOrder) { + // Skip any animations that are longer need associated with this timeline. + if (animation->GetTimeline() != this) { + mAnimations.RemoveEntry(animation); + continue; + } + + needsTicks |= animation->NeedsTicks(); + // Even if |animation| doesn't need future ticks, we should still + // Tick it this time around since it might just need a one-off tick in + // order to dispatch events. + animation->Tick(); + + if (animation->IsRelevant() || animation->NeedsTicks()) { + animationsToKeep.AppendElement(animation); + } else { + mAnimations.RemoveEntry(animation); + } + } + + mAnimationOrder.SwapElements(animationsToKeep); + + if (!needsTicks) { + // If another refresh driver observer destroys the nsPresContext, + // nsRefreshDriver will detect it and we won't be called. + MOZ_ASSERT(GetRefreshDriver(), + "Refresh driver should still be valid inside WillRefresh"); + GetRefreshDriver()->RemoveRefreshObserver(this, Flush_Style); + mIsObservingRefreshDriver = false; + } +} + +void +DocumentTimeline::NotifyRefreshDriverCreated(nsRefreshDriver* aDriver) +{ + MOZ_ASSERT(!mIsObservingRefreshDriver, + "Timeline should not be observing the refresh driver before" + " it is created"); + + if (!mAnimationOrder.IsEmpty()) { + aDriver->AddRefreshObserver(this, Flush_Style); + mIsObservingRefreshDriver = true; + } +} + +void +DocumentTimeline::NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver) +{ + if (!mIsObservingRefreshDriver) { + return; + } + + aDriver->RemoveRefreshObserver(this, Flush_Style); + mIsObservingRefreshDriver = false; +} + TimeStamp DocumentTimeline::ToTimeStamp(const TimeDuration& aTimeDuration) const { diff --git a/dom/animation/DocumentTimeline.h b/dom/animation/DocumentTimeline.h index dcec9046bd..afd6fcef07 100644 --- a/dom/animation/DocumentTimeline.h +++ b/dom/animation/DocumentTimeline.h @@ -17,17 +17,24 @@ struct JSContext; namespace mozilla { namespace dom { -class DocumentTimeline final : public AnimationTimeline +class DocumentTimeline final + : public AnimationTimeline + , public nsARefreshObserver { public: explicit DocumentTimeline(nsIDocument* aDocument) : AnimationTimeline(aDocument->GetParentObject()) , mDocument(aDocument) + , mIsObservingRefreshDriver(false) { } protected: - virtual ~DocumentTimeline() { } + virtual ~DocumentTimeline() + { + MOZ_ASSERT(!mIsObservingRefreshDriver, "Timeline should have disassociated" + " from the refresh driver before being destroyed"); + } public: NS_DECL_ISUPPORTS_INHERITED @@ -50,6 +57,14 @@ public: override; TimeStamp ToTimeStamp(const TimeDuration& aTimelineTime) const override; + void NotifyAnimationUpdated(Animation& aAnimation) override; + + // nsARefreshObserver methods + void WillRefresh(TimeStamp aTime) override; + + void NotifyRefreshDriverCreated(nsRefreshDriver* aDriver); + void NotifyRefreshDriverDestroying(nsRefreshDriver* aDriver); + protected: TimeStamp GetCurrentTimeStamp() const; nsRefreshDriver* GetRefreshDriver() const; @@ -60,6 +75,7 @@ protected: // we don't have a refresh driver (e.g. because we are in a display:none // iframe). mutable TimeStamp mLastRefreshDriverTime; + bool mIsObservingRefreshDriver; }; } // namespace dom diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 3e2dce93bb..8ab75a8687 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -5,6 +5,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/KeyframeEffect.h" +#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" #include "mozilla/dom/KeyframeEffectBinding.h" #include "mozilla/dom/PropertyIndexedKeyframesBinding.h" #include "mozilla/FloatingPoint.h" @@ -15,99 +16,75 @@ #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags #include "nsCSSValue.h" #include "nsStyleUtil.h" +#include // std::max namespace mozilla { -void -ComputedTimingFunction::Init(const nsTimingFunction &aFunction) +// Helper functions for generating a ComputedTimingProperties dictionary +static dom::FillMode +ConvertFillMode(uint8_t aFill) { - mType = aFunction.mType; - if (nsTimingFunction::IsSplineType(mType)) { - mTimingFunction.Init(aFunction.mFunc.mX1, aFunction.mFunc.mY1, - aFunction.mFunc.mX2, aFunction.mFunc.mY2); - } else { - mSteps = aFunction.mSteps; - mStepSyntax = aFunction.mStepSyntax; - } -} - -static inline double -StepEnd(uint32_t aSteps, double aPortion) -{ - MOZ_ASSERT(0.0 <= aPortion && aPortion <= 1.0, "out of range"); - uint32_t step = uint32_t(aPortion * aSteps); // floor - return double(step) / double(aSteps); -} - -double -ComputedTimingFunction::GetValue(double aPortion) const -{ - if (HasSpline()) { - return mTimingFunction.GetSplineValue(aPortion); - } - if (mType == nsTimingFunction::Type::StepStart) { - // There are diagrams in the spec that seem to suggest this check - // and the bounds point should not be symmetric with StepEnd, but - // should actually step up at rather than immediately after the - // fraction points. However, we rely on rounding negative values - // up to zero, so we can't do that. And it's not clear the spec - // really meant it. - return 1.0 - StepEnd(mSteps, 1.0 - aPortion); - } - MOZ_ASSERT(mType == nsTimingFunction::Type::StepEnd, "bad type"); - return StepEnd(mSteps, aPortion); -} - -int32_t -ComputedTimingFunction::Compare(const ComputedTimingFunction& aRhs) const -{ - if (mType != aRhs.mType) { - return int32_t(mType) - int32_t(aRhs.mType); - } - - if (mType == nsTimingFunction::Type::CubicBezier) { - int32_t order = mTimingFunction.Compare(aRhs.mTimingFunction); - if (order != 0) { - return order; - } - } else if (mType == nsTimingFunction::Type::StepStart || - mType == nsTimingFunction::Type::StepEnd) { - if (mSteps != aRhs.mSteps) { - return int32_t(mSteps) - int32_t(aRhs.mSteps); - } - if (mStepSyntax != aRhs.mStepSyntax) { - return int32_t(mStepSyntax) - int32_t(aRhs.mStepSyntax); - } - } - - return 0; -} - -void -ComputedTimingFunction::AppendToString(nsAString& aResult) const -{ - switch (mType) { - case nsTimingFunction::Type::CubicBezier: - nsStyleUtil::AppendCubicBezierTimingFunction(mTimingFunction.X1(), - mTimingFunction.Y1(), - mTimingFunction.X2(), - mTimingFunction.Y2(), - aResult); - break; - case nsTimingFunction::Type::StepStart: - case nsTimingFunction::Type::StepEnd: - nsStyleUtil::AppendStepsTimingFunction(mType, mSteps, mStepSyntax, - aResult); - break; + switch (aFill) { + case NS_STYLE_ANIMATION_FILL_MODE_NONE: + return dom::FillMode::None; + case NS_STYLE_ANIMATION_FILL_MODE_FORWARDS: + return dom::FillMode::Forwards; + case NS_STYLE_ANIMATION_FILL_MODE_BACKWARDS: + return dom::FillMode::Backwards; + case NS_STYLE_ANIMATION_FILL_MODE_BOTH: + return dom::FillMode::Both; default: - nsStyleUtil::AppendCubicBezierKeywordTimingFunction(mType, aResult); - break; + MOZ_ASSERT(false, "The mapping of FillMode is not correct"); + return dom::FillMode::None; } } -// In the Web Animations model, the iteration progress can be outside the range -// [0.0, 1.0] but it shouldn't be Infinity. -const double ComputedTiming::kNullProgress = PositiveInfinity(); +static dom::PlaybackDirection +ConvertPlaybackDirection(uint8_t aDirection) +{ + switch (aDirection) { + case NS_STYLE_ANIMATION_DIRECTION_NORMAL: + return dom::PlaybackDirection::Normal; + case NS_STYLE_ANIMATION_DIRECTION_REVERSE: + return dom::PlaybackDirection::Reverse; + case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE: + return dom::PlaybackDirection::Alternate; + case NS_STYLE_ANIMATION_DIRECTION_ALTERNATE_REVERSE: + return dom::PlaybackDirection::Alternate_reverse; + default: + MOZ_ASSERT(false, "The mapping of PlaybackDirection is not correct"); + return dom::PlaybackDirection::Normal; + } +} + +static void +GetComputedTimingDictionary(const ComputedTiming& aComputedTiming, + const Nullable& aLocalTime, + const AnimationTiming& aTiming, + dom::ComputedTimingProperties& aRetVal) +{ + // AnimationEffectTimingProperties + aRetVal.mDelay = aTiming.mDelay.ToMilliseconds(); + aRetVal.mFill = ConvertFillMode(aTiming.mFillMode); + aRetVal.mIterations = aTiming.mIterationCount; + aRetVal.mDuration.SetAsUnrestrictedDouble() = aTiming.mIterationDuration.ToMilliseconds(); + aRetVal.mDirection = ConvertPlaybackDirection(aTiming.mDirection); + + // ComputedTimingProperties + aRetVal.mActiveDuration = aComputedTiming.mActiveDuration.ToMilliseconds(); + aRetVal.mEndTime + = std::max(aRetVal.mDelay + aRetVal.mActiveDuration + aRetVal.mEndDelay, 0.0); + aRetVal.mLocalTime = dom::AnimationUtils::TimeDurationToDouble(aLocalTime); + aRetVal.mProgress = aComputedTiming.mProgress; + if (!aRetVal.mProgress.IsNull()) { + // Convert the returned currentIteration into Infinity if we set + // (uint64_t) aComputedTiming.mCurrentIteration to UINT64_MAX + double iteration = aComputedTiming.mCurrentIteration == UINT64_MAX + ? PositiveInfinity() + : static_cast(aComputedTiming.mCurrentIteration); + aRetVal.mCurrentIteration.SetValue(iteration); + } +} namespace dom { @@ -171,6 +148,16 @@ KeyframeEffectReadOnly::GetLocalTime() const return result; } +void +KeyframeEffectReadOnly::GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const +{ + const Nullable currentTime = GetLocalTime(); + GetComputedTimingDictionary(GetComputedTimingAt(currentTime, mTiming), + currentTime, + mTiming, + aRetVal); +} + ComputedTiming KeyframeEffectReadOnly::GetComputedTimingAt( const Nullable& aLocalTime, @@ -206,10 +193,10 @@ KeyframeEffectReadOnly::GetComputedTimingAt( // Get the normalized time within the active interval. StickyTimeDuration activeTime; if (localTime >= aTiming.mDelay + result.mActiveDuration) { - result.mPhase = ComputedTiming::AnimationPhase_After; + result.mPhase = ComputedTiming::AnimationPhase::After; if (!aTiming.FillsForwards()) { // The animation isn't active or filling at this time. - result.mProgress = ComputedTiming::kNullProgress; + result.mProgress.SetNull(); return result; } activeTime = result.mActiveDuration; @@ -219,17 +206,17 @@ KeyframeEffectReadOnly::GetComputedTimingAt( aTiming.mIterationCount != 0.0 && aTiming.mIterationCount == floor(aTiming.mIterationCount); } else if (localTime < aTiming.mDelay) { - result.mPhase = ComputedTiming::AnimationPhase_Before; + result.mPhase = ComputedTiming::AnimationPhase::Before; if (!aTiming.FillsBackwards()) { // The animation isn't active or filling at this time. - result.mProgress = ComputedTiming::kNullProgress; + result.mProgress.SetNull(); return result; } // activeTime is zero } else { MOZ_ASSERT(result.mActiveDuration != zeroDuration, "How can we be in the middle of a zero-duration interval?"); - result.mPhase = ComputedTiming::AnimationPhase_Active; + result.mPhase = ComputedTiming::AnimationPhase::Active; activeTime = localTime - aTiming.mDelay; } @@ -245,8 +232,8 @@ KeyframeEffectReadOnly::GetComputedTimingAt( if (isEndOfFinalIteration) { result.mCurrentIteration = aTiming.mIterationCount == NS_IEEEPositiveInfinity() - ? UINT64_MAX // FIXME: When we return this via the API we'll need - // to make sure it ends up being infinity. + ? UINT64_MAX // In GetComputedTimingDictionary(), we will convert this + // into Infinity. : static_cast(aTiming.mIterationCount) - 1; } else if (activeTime == zeroDuration) { // If the active time is zero we're either in the first iteration @@ -254,7 +241,7 @@ KeyframeEffectReadOnly::GetComputedTimingAt( // iteration duration of zero that is filling forwards (but we're not at // the exact end of an iteration since we deal with that above). result.mCurrentIteration = - result.mPhase == ComputedTiming::AnimationPhase_After + result.mPhase == ComputedTiming::AnimationPhase::After ? static_cast(aTiming.mIterationCount) // floor : 0; } else { @@ -263,19 +250,21 @@ KeyframeEffectReadOnly::GetComputedTimingAt( } // Normalize the iteration time into a fraction of the iteration duration. - if (result.mPhase == ComputedTiming::AnimationPhase_Before) { - result.mProgress = 0.0; - } else if (result.mPhase == ComputedTiming::AnimationPhase_After) { - result.mProgress = isEndOfFinalIteration - ? 1.0 - : fmod(aTiming.mIterationCount, 1.0f); + if (result.mPhase == ComputedTiming::AnimationPhase::Before) { + result.mProgress.SetValue(0.0); + } else if (result.mPhase == ComputedTiming::AnimationPhase::After) { + double progress = isEndOfFinalIteration + ? 1.0 + : fmod(aTiming.mIterationCount, 1.0f); + result.mProgress.SetValue(progress); } else { // We are in the active phase so the iteration duration can't be zero. MOZ_ASSERT(aTiming.mIterationDuration != zeroDuration, "In the active phase of a zero-duration animation?"); - result.mProgress = aTiming.mIterationDuration == TimeDuration::Forever() - ? 0.0 - : iterationTime / aTiming.mIterationDuration; + double progress = aTiming.mIterationDuration == TimeDuration::Forever() + ? 0.0 + : iterationTime / aTiming.mIterationDuration; + result.mProgress.SetValue(progress); } bool thisIterationReverse = false; @@ -294,7 +283,7 @@ KeyframeEffectReadOnly::GetComputedTimingAt( break; } if (thisIterationReverse) { - result.mProgress = 1.0 - result.mProgress; + result.mProgress.SetValue(1.0 - result.mProgress.Value()); } return result; @@ -324,7 +313,7 @@ KeyframeEffectReadOnly::IsInPlay() const return false; } - return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase_Active; + return GetComputedTiming().mPhase == ComputedTiming::AnimationPhase::Active; } // https://w3c.github.io/web-animations/#current @@ -336,8 +325,8 @@ KeyframeEffectReadOnly::IsCurrent() const } ComputedTiming computedTiming = GetComputedTiming(); - return computedTiming.mPhase == ComputedTiming::AnimationPhase_Before || - computedTiming.mPhase == ComputedTiming::AnimationPhase_Active; + return computedTiming.mPhase == ComputedTiming::AnimationPhase::Before || + computedTiming.mPhase == ComputedTiming::AnimationPhase::Active; } // https://w3c.github.io/web-animations/#in-effect @@ -345,7 +334,7 @@ bool KeyframeEffectReadOnly::IsInEffect() const { ComputedTiming computedTiming = GetComputedTiming(); - return computedTiming.mProgress != ComputedTiming::kNullProgress; + return !computedTiming.mProgress.IsNull(); } void @@ -391,12 +380,13 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, // If the progress is null, we don't have fill data for the current // time so we shouldn't animate. - if (computedTiming.mProgress == ComputedTiming::kNullProgress) { + if (computedTiming.mProgress.IsNull()) { return; } - MOZ_ASSERT(0.0 <= computedTiming.mProgress && - computedTiming.mProgress <= 1.0, + MOZ_ASSERT(!computedTiming.mProgress.IsNull() && + 0.0 <= computedTiming.mProgress.Value() && + computedTiming.mProgress.Value() <= 1.0, "iteration progress should be in [0-1]"); for (size_t propIdx = 0, propEnd = mProperties.Length(); @@ -434,7 +424,7 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, // FIXME: Maybe cache the current segment? const AnimationPropertySegment *segment = prop.mSegments.Elements(), *segmentEnd = segment + prop.mSegments.Length(); - while (segment->mToKey < computedTiming.mProgress) { + while (segment->mToKey < computedTiming.mProgress.Value()) { MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys"); ++segment; if (segment == segmentEnd) { @@ -458,7 +448,7 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, } double positionInSegment = - (computedTiming.mProgress - segment->mFromKey) / + (computedTiming.mProgress.Value() - segment->mFromKey) / (segment->mToKey - segment->mFromKey); double valuePosition = segment->mTimingFunction.GetValue(positionInSegment); diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index 41e83411f8..7b4d629d7d 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -13,7 +13,8 @@ #include "nsIDocument.h" #include "nsWrapperCache.h" #include "mozilla/Attributes.h" -#include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords +#include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction +#include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords #include "mozilla/StickyTimeDuration.h" #include "mozilla/StyleAnimationValue.h" #include "mozilla/TimeStamp.h" @@ -21,8 +22,6 @@ #include "mozilla/dom/Element.h" #include "mozilla/dom/KeyframeBinding.h" #include "mozilla/dom/Nullable.h" -#include "nsSMILKeySpline.h" -#include "nsStyleStruct.h" // for nsTimingFunction struct JSContext; class nsCSSPropertySet; @@ -31,6 +30,10 @@ namespace mozilla { class AnimValuesStyleRule; +namespace dom { +struct ComputedTimingProperties; +} + /** * Input timing parameters. * @@ -72,73 +75,25 @@ struct AnimationTiming */ struct ComputedTiming { - ComputedTiming() - : mProgress(kNullProgress) - , mCurrentIteration(0) - , mPhase(AnimationPhase_Null) - { } - - static const double kNullProgress; - // The total duration of the animation including all iterations. // Will equal StickyTimeDuration::Forever() if the animation repeats // indefinitely. - StickyTimeDuration mActiveDuration; - + StickyTimeDuration mActiveDuration; // Progress towards the end of the current iteration. If the effect is // being sampled backwards, this will go from 1.0 to 0.0. - // Will be kNullProgress if the animation is neither animating nor + // Will be null if the animation is neither animating nor // filling at the sampled time. - double mProgress; + Nullable mProgress; + // Zero-based iteration index (meaningless if mProgress is null). + uint64_t mCurrentIteration = 0; - // Zero-based iteration index (meaningless if mProgress is kNullProgress). - uint64_t mCurrentIteration; - - enum { - // Not sampled (null sample time) - AnimationPhase_Null, - // Sampled prior to the start of the active interval - AnimationPhase_Before, - // Sampled within the active interval - AnimationPhase_Active, - // Sampled after (or at) the end of the active interval - AnimationPhase_After - } mPhase; -}; - -class ComputedTimingFunction -{ -public: - typedef nsTimingFunction::Type Type; - typedef nsTimingFunction::StepSyntax StepSyntax; - void Init(const nsTimingFunction &aFunction); - double GetValue(double aPortion) const; - const nsSMILKeySpline* GetFunction() const { - NS_ASSERTION(HasSpline(), "Type mismatch"); - return &mTimingFunction; - } - Type GetType() const { return mType; } - bool HasSpline() const { return nsTimingFunction::IsSplineType(mType); } - uint32_t GetSteps() const { return mSteps; } - StepSyntax GetStepSyntax() const { return mStepSyntax; } - bool operator==(const ComputedTimingFunction& aOther) const { - return mType == aOther.mType && - (HasSpline() ? - mTimingFunction == aOther.mTimingFunction : - (mSteps == aOther.mSteps && - mStepSyntax == aOther.mStepSyntax)); - } - bool operator!=(const ComputedTimingFunction& aOther) const { - return !(*this == aOther); - } - int32_t Compare(const ComputedTimingFunction& aRhs) const; - void AppendToString(nsAString& aResult) const; - -private: - Type mType; - nsSMILKeySpline mTimingFunction; - uint32_t mSteps; - StepSyntax mStepSyntax; + enum class AnimationPhase { + Null, // Not sampled (null sample time) + Before, // Sampled prior to the start of the active interval + Active, // Sampled within the active interval + After // Sampled after (or at) the end of the active interval + }; + AnimationPhase mPhase = AnimationPhase::Null; }; struct AnimationPropertySegment @@ -263,8 +218,8 @@ public: // active duration are calculated. All other members of the returned object // are given a null/initial value. // - // This function returns ComputedTiming::kNullProgress for the mProgress - // member of the return value if the animation should not be run + // This function returns a null mProgress member of the return value + // if the animation should not be run // (because it is not currently active and is not filling at this time). static ComputedTiming GetComputedTimingAt(const Nullable& aLocalTime, @@ -272,11 +227,15 @@ public: // Shortcut for that gets the computed timing using the current local time as // calculated from the timeline time. - ComputedTiming GetComputedTiming(const AnimationTiming* aTiming - = nullptr) const { + ComputedTiming + GetComputedTiming(const AnimationTiming* aTiming = nullptr) const + { return GetComputedTimingAt(GetLocalTime(), aTiming ? *aTiming : mTiming); } + void + GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const override; + // Return the duration of the active interval for the given timing parameters. static StickyTimeDuration ActiveDuration(const AnimationTiming& aTiming); diff --git a/dom/animation/moz.build b/dom/animation/moz.build index 4f4af71b08..d36d97e28d 100644 --- a/dom/animation/moz.build +++ b/dom/animation/moz.build @@ -18,6 +18,7 @@ EXPORTS.mozilla.dom += [ EXPORTS.mozilla += [ 'AnimationComparator.h', 'AnimationUtils.h', + 'ComputedTimingFunction.h', 'PendingAnimationTracker.h', ] @@ -25,9 +26,14 @@ UNIFIED_SOURCES += [ 'Animation.cpp', 'AnimationEffectReadOnly.cpp', 'AnimationTimeline.cpp', + 'ComputedTimingFunction.cpp', 'DocumentTimeline.cpp', 'KeyframeEffect.cpp', 'PendingAnimationTracker.cpp', ] FINAL_LIBRARY = 'xul' + +LOCAL_INCLUDES += [ + '/dom/base', +] diff --git a/dom/animation/test/css-animations/file_animation-computed-timing.html b/dom/animation/test/css-animations/file_animation-computed-timing.html new file mode 100644 index 0000000000..6a94b23a4d --- /dev/null +++ b/dom/animation/test/css-animations/file_animation-computed-timing.html @@ -0,0 +1,566 @@ + + + + + + + diff --git a/dom/animation/test/css-animations/file_animation-currenttime.html b/dom/animation/test/css-animations/file_animation-currenttime.html index eca14dedbd..65a5f23296 100644 --- a/dom/animation/test/css-animations/file_animation-currenttime.html +++ b/dom/animation/test/css-animations/file_animation-currenttime.html @@ -441,6 +441,24 @@ async_test(function(t) { animation.currentTime = currentTimeForAfterPhase(animation.timeline); }, 'Redundant change, after -> active, then back'); +async_test(function(t) { + var div = addDiv(t, {'class': 'animated-div'}); + var eventWatcher = new EventWatcher(t, div, CSS_ANIM_EVENTS); + div.style.animation = ANIM_PROPERTY_VAL; + var animation = div.getAnimations()[0]; + + animation.pause(); + animation.currentTime = currentTimeForAfterPhase(animation.timeline); + + eventWatcher.wait_for(['animationstart', + 'animationend']).then(t.step_func(function() { + animation.currentTime = currentTimeForActivePhase(animation.timeline); + return eventWatcher.wait_for('animationstart'); + })).then(t.step_func(function() { + t.done(); + })); + +}, 'Seeking finished -> paused dispatches animationstart'); async_test(function(t) { var div = addDiv(t, {'class': 'animated-div'}); diff --git a/dom/animation/test/css-animations/file_animation-reverse.html b/dom/animation/test/css-animations/file_animation-reverse.html index c217471cb0..037ffd2c70 100644 --- a/dom/animation/test/css-animations/file_animation-reverse.html +++ b/dom/animation/test/css-animations/file_animation-reverse.html @@ -14,7 +14,11 @@ async_test(function(t) { var div = addDiv(t, { style: 'animation: anim 100s infinite' }); var animation = div.getAnimations()[0]; - animation.ready.then(t.step_func_done(function() { + // Wait a frame because if currentTime is still 0 when we call + // reverse(), it will throw (per spec). + animation.ready.then(waitForFrame).then(t.step_func_done(function() { + assert_greater_than(animation.currentTime, 0, + 'currentTime expected to be greater than 0, one frame after starting'); var previousPlaybackRate = animation.playbackRate; animation.reverse(); assert_equals(animation.playbackRate, -previousPlaybackRate, diff --git a/dom/animation/test/css-animations/test_animation-computed-timing.html b/dom/animation/test/css-animations/test_animation-computed-timing.html new file mode 100644 index 0000000000..c1b40aaf36 --- /dev/null +++ b/dom/animation/test/css-animations/test_animation-computed-timing.html @@ -0,0 +1,16 @@ + + + + + +
+ + diff --git a/dom/animation/test/css-transitions/file_animation-computed-timing.html b/dom/animation/test/css-transitions/file_animation-computed-timing.html new file mode 100644 index 0000000000..2dac82d753 --- /dev/null +++ b/dom/animation/test/css-transitions/file_animation-computed-timing.html @@ -0,0 +1,315 @@ + + + + + + + diff --git a/dom/animation/test/css-transitions/test_animation-computed-timing.html b/dom/animation/test/css-transitions/test_animation-computed-timing.html new file mode 100644 index 0000000000..c1b40aaf36 --- /dev/null +++ b/dom/animation/test/css-transitions/test_animation-computed-timing.html @@ -0,0 +1,16 @@ + + + + + +
+ + diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini index a7a28f5219..a447ff855e 100644 --- a/dom/animation/test/mochitest.ini +++ b/dom/animation/test/mochitest.ini @@ -6,6 +6,8 @@ support-files = support-files = css-animations/file_animations-dynamic-changes.html [css-animations/test_animation-cancel.html] support-files = css-animations/file_animation-cancel.html +[css-animations/test_animation-computed-timing.html] +support-files = css-animations/file_animation-computed-timing.html [css-animations/test_animation-currenttime.html] support-files = css-animations/file_animation-currenttime.html [css-animations/test_animation-finish.html] @@ -41,6 +43,8 @@ support-files = css-animations/file_element-get-animations.html support-files = css-animations/file_timeline-get-animations.html [css-transitions/test_animation-cancel.html] support-files = css-transitions/file_animation-cancel.html +[css-transitions/test_animation-computed-timing.html] +support-files = css-transitions/file_animation-computed-timing.html [css-transitions/test_animation-currenttime.html] support-files = css-transitions/file_animation-currenttime.html [css-transitions/test_animation-finished.html] diff --git a/dom/animation/test/testcommon.js b/dom/animation/test/testcommon.js index 424951c4a5..30c70e9c07 100644 --- a/dom/animation/test/testcommon.js +++ b/dom/animation/test/testcommon.js @@ -63,7 +63,8 @@ function flushComputedStyle(elem) { if (opener) { for (var funcName of ["async_test", "assert_not_equals", "assert_equals", "assert_approx_equals", "assert_less_than", - "assert_less_than_equal", "assert_between_inclusive", + "assert_less_than_equal", "assert_greater_than", + "assert_between_inclusive", "assert_true", "assert_false", "assert_class_string", "assert_throws", "assert_unreached", "test"]) { diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index fbc4df9e01..7adfcbcf7a 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1910,20 +1910,20 @@ Element::GetSMILOverrideStyle() return slots->mSMILOverrideStyle; } -css::StyleRule* -Element::GetSMILOverrideStyleRule() +css::Declaration* +Element::GetSMILOverrideStyleDeclaration() { Element::nsDOMSlots *slots = GetExistingDOMSlots(); - return slots ? slots->mSMILOverrideStyleRule.get() : nullptr; + return slots ? slots->mSMILOverrideStyleDeclaration.get() : nullptr; } nsresult -Element::SetSMILOverrideStyleRule(css::StyleRule* aStyleRule, - bool aNotify) +Element::SetSMILOverrideStyleDeclaration(css::Declaration* aDeclaration, + bool aNotify) { Element::nsDOMSlots *slots = DOMSlots(); - slots->mSMILOverrideStyleRule = aStyleRule; + slots->mSMILOverrideStyleDeclaration = aDeclaration; if (aNotify) { nsIDocument* doc = GetComposedDoc(); @@ -1958,18 +1958,18 @@ Element::IsInteractiveHTMLContent(bool aIgnoreTabindex) const return false; } -css::StyleRule* -Element::GetInlineStyleRule() +css::Declaration* +Element::GetInlineStyleDeclaration() { return nullptr; } nsresult -Element::SetInlineStyleRule(css::StyleRule* aStyleRule, - const nsAString* aSerialized, - bool aNotify) +Element::SetInlineStyleDeclaration(css::Declaration* aDeclaration, + const nsAString* aSerialized, + bool aNotify) { - NS_NOTYETIMPLEMENTED("Element::SetInlineStyleRule"); + NS_NOTYETIMPLEMENTED("Element::SetInlineStyleDeclaration"); return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/dom/base/Element.h b/dom/base/Element.h index 5fa5c8dc6c..41c2af1e82 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -226,31 +226,31 @@ public: void ClearStyleStateLocks(); /** - * Get the inline style rule, if any, for this element. + * Get the inline style declaration, if any, for this element. */ - virtual css::StyleRule* GetInlineStyleRule(); + virtual css::Declaration* GetInlineStyleDeclaration(); /** - * Set the inline style rule for this element. This will send an appropriate - * AttributeChanged notification if aNotify is true. + * Set the inline style declaration for this element. This will send + * an appropriate AttributeChanged notification if aNotify is true. */ - virtual nsresult SetInlineStyleRule(css::StyleRule* aStyleRule, - const nsAString* aSerialized, - bool aNotify); + virtual nsresult SetInlineStyleDeclaration(css::Declaration* aDeclaration, + const nsAString* aSerialized, + bool aNotify); /** - * Get the SMIL override style rule for this element. If the rule hasn't been - * created, this method simply returns null. + * Get the SMIL override style declaration for this element. If the + * rule hasn't been created, this method simply returns null. */ - virtual css::StyleRule* GetSMILOverrideStyleRule(); + virtual css::Declaration* GetSMILOverrideStyleDeclaration(); /** - * Set the SMIL override style rule for this element. If aNotify is true, this - * method will notify the document's pres context, so that the style changes - * will be noticed. + * Set the SMIL override style declaration for this element. If + * aNotify is true, this method will notify the document's pres + * context, so that the style changes will be noticed. */ - virtual nsresult SetSMILOverrideStyleRule(css::StyleRule* aStyleRule, - bool aNotify); + virtual nsresult SetSMILOverrideStyleDeclaration(css::Declaration* aDeclaration, + bool aNotify); /** * Returns a new nsISMILAttr that allows the caller to animate the given diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp index 3c73e9657d..7c11b7d6da 100644 --- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -614,7 +614,7 @@ FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) c // - mStyle // - mDataSet // - mSMILOverrideStyle - // - mSMILOverrideStyleRule + // - mSMILOverrideStyleDeclaration // - mChildrenList // - mClassList diff --git a/dom/base/FragmentOrElement.h b/dom/base/FragmentOrElement.h index d7c27fb634..dc29fc63fa 100644 --- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -286,9 +286,9 @@ public: nsCOMPtr mSMILOverrideStyle; /** - * Holds any SMIL override style rules for this element. + * Holds any SMIL override style declaration for this element. */ - RefPtr mSMILOverrideStyleRule; + RefPtr mSMILOverrideStyleDeclaration; /** * An object implementing nsIDOMMozNamedAttrMap for this content (attributes) diff --git a/dom/base/crashtests/713417.html b/dom/base/crashtests/713417-1.html similarity index 100% rename from dom/base/crashtests/713417.html rename to dom/base/crashtests/713417-1.html diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index bbc2211af5..a4d67018e0 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -1,3 +1,4 @@ +load 43040-1.html load 90613-1.html load 116848-1.html load 149320-1.html @@ -66,7 +67,6 @@ load 418928-1.html load 420620-1.html load 424276-1.html load 426987-1.html -load 43040-1.html load 439206-1.html load 443538-1.svg load 448615-1.html @@ -146,8 +146,8 @@ load 706283-1.html load 708405-1.html load 709384.html load 709954.html +load 713417-1.html load 713417-2.html -load 713417.html load 715056.html load 741163-1.html load 745495.html @@ -183,7 +183,7 @@ load 847127.html load 849601.html load 849727.html load 849732.html -skip-if(Android) load 851353-1.html +load 851353-1.html load 852381.html load 863950.html load 864448.html @@ -193,14 +193,14 @@ load 930250.html load 942979.html load 973401.html load 978646.html -pref(dom.webcomponents.enabled,true) load 1027461-1.html pref(dom.webcomponents.enabled,true) load 1024428-1.html load 1026714.html +pref(dom.webcomponents.enabled,true) load 1027461-1.html pref(dom.webcomponents.enabled,true) load 1029710.html -HTTP(..) load xhr_abortinprogress.html -load xhr_empty_datauri.html -load xhr_html_nullresponse.html -load structured_clone_container_throws.html load 1154598.xhtml load 1157995.html load 1181619.html +load structured_clone_container_throws.html +HTTP(..) load xhr_abortinprogress.html +load xhr_empty_datauri.html +load xhr_html_nullresponse.html diff --git a/dom/base/nsAttrValue.cpp b/dom/base/nsAttrValue.cpp index cae81b83f1..622da120fc 100644 --- a/dom/base/nsAttrValue.cpp +++ b/dom/base/nsAttrValue.cpp @@ -17,7 +17,6 @@ #include "nsIAtom.h" #include "nsUnicharUtils.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/css/StyleRule.h" #include "mozilla/css/Declaration.h" #include "nsContentUtils.h" #include "nsReadableUtils.h" @@ -73,13 +72,13 @@ void MiscContainer::Cache() { // Not implemented for anything else yet. - MOZ_ASSERT(mType == nsAttrValue::eCSSStyleRule); + MOZ_ASSERT(mType == nsAttrValue::eCSSDeclaration); MOZ_ASSERT(IsRefCounted()); MOZ_ASSERT(mValue.mRefCount > 0); MOZ_ASSERT(!mValue.mCached); - css::StyleRule* rule = mValue.mCSSStyleRule; - nsHTMLCSSStyleSheet* sheet = rule->GetHTMLCSSStyleSheet(); + css::Declaration* declaration = mValue.mCSSDeclaration; + nsHTMLCSSStyleSheet* sheet = declaration->GetHTMLCSSStyleSheet(); if (!sheet) { return; } @@ -94,17 +93,14 @@ MiscContainer::Cache() mValue.mCached = 1; // This has to be immutable once it goes into the cache. - css::Declaration* decl = rule->GetDeclaration(); - if (decl) { - decl->SetImmutable(); - } + declaration->SetImmutable(); } void MiscContainer::Evict() { // Not implemented for anything else yet. - MOZ_ASSERT(mType == nsAttrValue::eCSSStyleRule); + MOZ_ASSERT(mType == nsAttrValue::eCSSDeclaration); MOZ_ASSERT(IsRefCounted()); MOZ_ASSERT(mValue.mRefCount == 0); @@ -112,8 +108,8 @@ MiscContainer::Evict() return; } - css::StyleRule* rule = mValue.mCSSStyleRule; - nsHTMLCSSStyleSheet* sheet = rule->GetHTMLCSSStyleSheet(); + css::Declaration* declaration = mValue.mCSSDeclaration; + nsHTMLCSSStyleSheet* sheet = declaration->GetHTMLCSSStyleSheet(); MOZ_ASSERT(sheet); nsString str; @@ -149,7 +145,7 @@ nsAttrValue::nsAttrValue(nsIAtom* aValue) SetTo(aValue); } -nsAttrValue::nsAttrValue(css::StyleRule* aValue, const nsAString* aSerialized) +nsAttrValue::nsAttrValue(css::Declaration* aValue, const nsAString* aSerialized) : mBits(0) { SetTo(aValue, aSerialized); @@ -314,7 +310,7 @@ nsAttrValue::SetTo(const nsAttrValue& aOther) cont->mValue.mColor = otherCont->mValue.mColor; break; } - case eCSSStyleRule: + case eCSSDeclaration: { MOZ_CRASH("These should be refcounted!"); } @@ -421,12 +417,12 @@ nsAttrValue::SetTo(double aValue, const nsAString* aSerialized) } void -nsAttrValue::SetTo(css::StyleRule* aValue, const nsAString* aSerialized) +nsAttrValue::SetTo(css::Declaration* aValue, const nsAString* aSerialized) { MiscContainer* cont = EnsureEmptyMiscContainer(); MOZ_ASSERT(cont->mValue.mRefCount == 0); - NS_ADDREF(cont->mValue.mCSSStyleRule = aValue); - cont->mType = eCSSStyleRule; + NS_ADDREF(cont->mValue.mCSSDeclaration = aValue); + cont->mType = eCSSDeclaration; NS_ADDREF(cont); SetMiscAtomOrString(aSerialized); MOZ_ASSERT(cont->mValue.mRefCount == 1); @@ -639,12 +635,11 @@ nsAttrValue::ToString(nsAString& aResult) const break; } - case eCSSStyleRule: + case eCSSDeclaration: { aResult.Truncate(); MiscContainer *container = GetMiscContainer(); - css::Declaration *decl = - container->mValue.mCSSStyleRule->GetDeclaration(); + css::Declaration *decl = container->mValue.mCSSDeclaration; if (decl) { decl->ToString(aResult); } @@ -889,9 +884,9 @@ nsAttrValue::HashValue() const { return cont->mValue.mColor; } - case eCSSStyleRule: + case eCSSDeclaration: { - return NS_PTR_TO_INT32(cont->mValue.mCSSStyleRule); + return NS_PTR_TO_INT32(cont->mValue.mCSSDeclaration); } // Intentionally identical, so that loading the image does not change the // hash code. @@ -998,9 +993,10 @@ nsAttrValue::Equals(const nsAttrValue& aOther) const } break; } - case eCSSStyleRule: + case eCSSDeclaration: { - return thisCont->mValue.mCSSStyleRule == otherCont->mValue.mCSSStyleRule; + return thisCont->mValue.mCSSDeclaration == + otherCont->mValue.mCSSDeclaration; } case eURL: { @@ -1690,13 +1686,12 @@ nsAttrValue::ParseStyleAttribute(const nsAString& aString, css::Loader* cssLoader = ownerDoc->CSSLoader(); nsCSSParser cssParser(cssLoader); - RefPtr rule; - cssParser.ParseStyleAttribute(aString, docURI, baseURI, - aElement->NodePrincipal(), - getter_AddRefs(rule)); - if (rule) { - rule->SetHTMLCSSStyleSheet(sheet); - SetTo(rule, &aString); + RefPtr declaration = + cssParser.ParseStyleAttribute(aString, docURI, baseURI, + aElement->NodePrincipal()); + if (declaration) { + declaration->SetHTMLCSSStyleSheet(sheet); + SetTo(declaration, &aString); if (cachingAllowed) { MiscContainer* cont = GetMiscContainer(); cont->Cache(); @@ -1716,13 +1711,13 @@ nsAttrValue::SetMiscAtomOrString(const nsAString* aValue) "Trying to re-set atom or string!"); if (aValue) { uint32_t len = aValue->Length(); - // * We're allowing eCSSStyleRule attributes to store empty strings as it + // * We're allowing eCSSDeclaration attributes to store empty strings as it // can be beneficial to store an empty style attribute as a parsed rule. // * We're allowing enumerated values because sometimes the empty // string corresponds to a particular enumerated value, especially // for enumerated values that are not limited enumerated. // Add other types as needed. - NS_ASSERTION(len || Type() == eCSSStyleRule || Type() == eEnum, + NS_ASSERTION(len || Type() == eCSSDeclaration || Type() == eEnum, "Empty string?"); MiscContainer* cont = GetMiscContainer(); if (len <= NS_ATTRVALUE_MAX_STRINGLENGTH_ATOM) { @@ -1786,12 +1781,12 @@ nsAttrValue::ClearMiscContainer() } else { switch (cont->mType) { - case eCSSStyleRule: + case eCSSDeclaration: { MOZ_ASSERT(cont->mValue.mRefCount == 1); cont->Release(); cont->Evict(); - NS_RELEASE(cont->mValue.mCSSStyleRule); + NS_RELEASE(cont->mValue.mCSSDeclaration); break; } case eURL: @@ -1927,10 +1922,10 @@ nsAttrValue::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const n += str ? str->SizeOfIncludingThisIfUnshared(aMallocSizeOf) : 0; } - if (Type() == eCSSStyleRule && container->mValue.mCSSStyleRule) { - // TODO: mCSSStyleRule might be owned by another object which would + if (Type() == eCSSDeclaration && container->mValue.mCSSDeclaration) { + // TODO: mCSSDeclaration might be owned by another object which would // make us count them twice, bug 677493. - //n += container->mCSSStyleRule->SizeOfIncludingThis(aMallocSizeOf); + //n += container->mCSSDeclaration->SizeOfIncludingThis(aMallocSizeOf); } else if (Type() == eAtomArray && container->mValue.mAtomArray) { // Don't measure each nsIAtom, they are measured separatly. n += container->mValue.mAtomArray->ShallowSizeOfIncludingThis(aMallocSizeOf); diff --git a/dom/base/nsAttrValue.h b/dom/base/nsAttrValue.h index c1dee8b7a1..98a740c7b8 100644 --- a/dom/base/nsAttrValue.h +++ b/dom/base/nsAttrValue.h @@ -35,7 +35,7 @@ struct MiscContainer; namespace mozilla { namespace css { -class StyleRule; +class Declaration; struct URLValue; struct ImageValue; } // namespace css @@ -94,7 +94,7 @@ public: ePercent = 0x0F, // 1111 // Values below here won't matter, they'll be always stored in the 'misc' // struct. - eCSSStyleRule = 0x10 + eCSSDeclaration = 0x10 ,eURL = 0x11 ,eImage = 0x12 ,eAtomArray = 0x13 @@ -120,7 +120,7 @@ public: nsAttrValue(const nsAttrValue& aOther); explicit nsAttrValue(const nsAString& aValue); explicit nsAttrValue(nsIAtom* aValue); - nsAttrValue(mozilla::css::StyleRule* aValue, const nsAString* aSerialized); + nsAttrValue(mozilla::css::Declaration* aValue, const nsAString* aSerialized); explicit nsAttrValue(const nsIntMargin& aValue); ~nsAttrValue(); @@ -145,7 +145,7 @@ public: void SetTo(int16_t aInt); void SetTo(int32_t aInt, const nsAString* aSerialized); void SetTo(double aValue, const nsAString* aSerialized); - void SetTo(mozilla::css::StyleRule* aValue, const nsAString* aSerialized); + void SetTo(mozilla::css::Declaration* aValue, const nsAString* aSerialized); void SetTo(mozilla::css::URLValue* aValue, const nsAString* aSerialized); void SetTo(const nsIntMargin& aValue); void SetTo(const nsSVGAngle& aValue, const nsAString* aSerialized); @@ -196,7 +196,7 @@ public: inline int16_t GetEnumValue() const; inline float GetPercentValue() const; inline AtomArray* GetAtomArrayValue() const; - inline mozilla::css::StyleRule* GetCSSStyleRuleValue() const; + inline mozilla::css::Declaration* GetCSSDeclarationValue() const; inline mozilla::css::URLValue* GetURLValue() const; inline mozilla::css::ImageValue* GetImageValue() const; inline double GetDoubleValue() const; diff --git a/dom/base/nsAttrValueInlines.h b/dom/base/nsAttrValueInlines.h index e392491d5f..7c8cb21ecf 100644 --- a/dom/base/nsAttrValueInlines.h +++ b/dom/base/nsAttrValueInlines.h @@ -25,7 +25,7 @@ struct MiscContainer ValueType mType; // mStringBits points to either nsIAtom* or nsStringBuffer* and is used when - // mType isn't mCSSStyleRule. + // mType isn't eCSSDeclaration. // Note eStringBase and eAtomBase is used also to handle the type of // mStringBits. uintptr_t mStringBits; @@ -36,7 +36,7 @@ struct MiscContainer nscolor mColor; uint32_t mEnumValue; int32_t mPercent; - mozilla::css::StyleRule* mCSSStyleRule; + mozilla::css::Declaration* mCSSDeclaration; mozilla::css::URLValue* mURL; mozilla::css::ImageValue* mImage; nsAttrValue::AtomArray* mAtomArray; @@ -86,7 +86,7 @@ struct MiscContainer // Nothing stops us from refcounting (and sharing) other types of // MiscContainer (except eDoubleValue types) but there's no compelling // reason to - return mType == nsAttrValue::eCSSStyleRule; + return mType == nsAttrValue::eCSSDeclaration; } inline int32_t AddRef() { @@ -146,11 +146,11 @@ nsAttrValue::GetAtomArrayValue() const return GetMiscContainer()->mValue.mAtomArray; } -inline mozilla::css::StyleRule* -nsAttrValue::GetCSSStyleRuleValue() const +inline mozilla::css::Declaration* +nsAttrValue::GetCSSDeclarationValue() const { - NS_PRECONDITION(Type() == eCSSStyleRule, "wrong type"); - return GetMiscContainer()->mValue.mCSSStyleRule; + NS_PRECONDITION(Type() == eCSSDeclaration, "wrong type"); + return GetMiscContainer()->mValue.mCSSDeclaration; } inline mozilla::css::URLValue* @@ -198,7 +198,7 @@ nsAttrValue::StoresOwnData() const return true; } ValueType t = Type(); - return t != eCSSStyleRule && !IsSVGType(t); + return t != eCSSDeclaration && !IsSVGType(t); } inline void diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index a2e4f3c0c6..b1edf724ba 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -330,13 +330,6 @@ nsIdentifierMapEntry::RemoveContentChangeCallback(nsIDocument::IDTargetObserver } } -// XXX Workaround for bug 980560 to maintain the existing broken semantics -template<> -struct nsIStyleRule::COMTypeInfo { - static const nsIID kIID; -}; -const nsIID nsIStyleRule::COMTypeInfo::kIID = NS_ISTYLE_RULE_IID; - namespace mozilla { namespace dom { @@ -4393,7 +4386,8 @@ FindSheet(const nsCOMArray& aSheets, nsIURI* aSheetURI) } nsresult -nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetURI) +nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, + nsIURI* aSheetURI) { NS_PRECONDITION(aSheetURI, "null arg"); @@ -4402,11 +4396,29 @@ nsDocument::LoadAdditionalStyleSheet(additionalSheetType aType, nsIURI* aSheetUR return NS_ERROR_INVALID_ARG; // Loading the sheet sync. - RefPtr loader = new mozilla::css::Loader(); + RefPtr loader = new css::Loader(); + + css::SheetParsingMode parsingMode; + switch (aType) { + case nsIDocument::eAgentSheet: + parsingMode = css::eAgentSheetFeatures; + break; + + case nsIDocument::eUserSheet: + parsingMode = css::eUserSheetFeatures; + break; + + case nsIDocument::eAuthorSheet: + parsingMode = css::eAuthorSheetFeatures; + break; + + default: + MOZ_CRASH("impossible value for aType"); + } RefPtr sheet; - nsresult rv = loader->LoadSheetSync(aSheetURI, aType == eAgentSheet, - true, getter_AddRefs(sheet)); + nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, + getter_AddRefs(sheet)); NS_ENSURE_SUCCESS(rv, rv); sheet->SetOwningDocument(this); @@ -5191,51 +5203,51 @@ nsDocument::DocumentStatesChanged(EventStates aStateMask) void nsDocument::StyleRuleChanged(nsIStyleSheet* aSheet, - nsIStyleRule* aOldStyleRule, - nsIStyleRule* aNewStyleRule) + css::Rule* aOldStyleRule, + css::Rule* aNewStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleChanged, (this, aSheet, aOldStyleRule, aNewStyleRule)); if (StyleSheetChangeEventsEnabled()) { - nsCOMPtr rule = do_QueryInterface(aNewStyleRule); DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleChanged", mRule, - rule ? rule->GetDOMRule() : nullptr); + aNewStyleRule ? aNewStyleRule->GetDOMRule() + : nullptr); } } void nsDocument::StyleRuleAdded(nsIStyleSheet* aSheet, - nsIStyleRule* aStyleRule) + css::Rule* aStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleAdded, (this, aSheet, aStyleRule)); if (StyleSheetChangeEventsEnabled()) { - nsCOMPtr rule = do_QueryInterface(aStyleRule); DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleAdded", mRule, - rule ? rule->GetDOMRule() : nullptr); + aStyleRule ? aStyleRule->GetDOMRule() + : nullptr); } } void nsDocument::StyleRuleRemoved(nsIStyleSheet* aSheet, - nsIStyleRule* aStyleRule) + css::Rule* aStyleRule) { NS_DOCUMENT_NOTIFY_OBSERVERS(StyleRuleRemoved, (this, aSheet, aStyleRule)); if (StyleSheetChangeEventsEnabled()) { - nsCOMPtr rule = do_QueryInterface(aStyleRule); DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleRemoved", mRule, - rule ? rule->GetDOMRule() : nullptr); + aStyleRule ? aStyleRule->GetDOMRule() + : nullptr); } } @@ -9829,7 +9841,10 @@ nsresult nsDocument::LoadChromeSheetSync(nsIURI* uri, bool isAgentSheet, CSSStyleSheet** sheet) { - return CSSLoader()->LoadSheetSync(uri, isAgentSheet, isAgentSheet, sheet); + css::SheetParsingMode mode = + isAgentSheet ? css::eAgentSheetFeatures + : css::eAuthorSheetFeatures; + return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, sheet); } class nsDelayedEventDispatcher : public nsRunnable diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h index 2496b8aae8..1124adc3d5 100644 --- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -872,12 +872,12 @@ public: mozilla::EventStates aStateMask) override; virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet, - nsIStyleRule* aOldStyleRule, - nsIStyleRule* aNewStyleRule) override; + mozilla::css::Rule* aOldStyleRule, + mozilla::css::Rule* aNewStyleRule) override; virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) override; + mozilla::css::Rule* aStyleRule) override; virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) override; + mozilla::css::Rule* aStyleRule) override; virtual void FlushPendingNotifications(mozFlushType aType) override; virtual void FlushExternalResources(mozFlushType aType) override; diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h index fa70b1dedf..b72b9172e2 100644 --- a/dom/base/nsIDocument.h +++ b/dom/base/nsIDocument.h @@ -71,7 +71,6 @@ class nsIRequest; class nsIRunnable; class nsIStreamListener; class nsIStructuredCloneContainer; -class nsIStyleRule; class nsIStyleSheet; class nsIURI; class nsIVariant; @@ -100,6 +99,7 @@ template class OwningNonNull; namespace css { class Loader; class ImageLoader; +class Rule; } // namespace css namespace gfx { @@ -160,8 +160,8 @@ struct FullScreenOptions { } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0x5f51e18c, 0x9e0e, 0x4dc0, \ - { 0x9f, 0x08, 0x7a, 0x32, 0x65, 0x52, 0xea, 0x11 } } +{ 0x4307abe8, 0x5386, 0x4024, \ + { 0xa2, 0xfe, 0x4a, 0x80, 0xe8, 0x47, 0x46, 0x90 } } // Enum for requesting a particular type of document when creating a doc enum DocumentFlavor { @@ -1267,12 +1267,12 @@ public: // Observation hooks for style data to propagate notifications // to document observers virtual void StyleRuleChanged(nsIStyleSheet* aStyleSheet, - nsIStyleRule* aOldStyleRule, - nsIStyleRule* aNewStyleRule) = 0; + mozilla::css::Rule* aOldStyleRule, + mozilla::css::Rule* aNewStyleRule) = 0; virtual void StyleRuleAdded(nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) = 0; + mozilla::css::Rule* aStyleRule) = 0; virtual void StyleRuleRemoved(nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) = 0; + mozilla::css::Rule* aStyleRule) = 0; /** * Flush notifications for this document and its parent documents diff --git a/dom/base/nsIDocumentObserver.h b/dom/base/nsIDocumentObserver.h index 311d1ddf4d..6b3d6d1893 100644 --- a/dom/base/nsIDocumentObserver.h +++ b/dom/base/nsIDocumentObserver.h @@ -12,12 +12,17 @@ class nsIContent; class nsIStyleSheet; -class nsIStyleRule; class nsIDocument; +namespace mozilla { +namespace css { +class Rule; +} // namespace css +} // namespace mozilla + #define NS_IDOCUMENT_OBSERVER_IID \ -{ 0x900bc4bc, 0x8b6c, 0x4cba, \ - { 0x82, 0xfa, 0x56, 0x8a, 0x80, 0xff, 0xfd, 0x3e } } +{ 0xce1d53d0, 0x4739, 0x44e5, \ + { 0xb4, 0xae, 0x60, 0xe8, 0x82, 0xcb, 0x73, 0x1b } } typedef uint32_t nsUpdateType; @@ -156,8 +161,8 @@ public: */ virtual void StyleRuleChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, - nsIStyleRule* aOldStyleRule, - nsIStyleRule* aNewStyleRule) = 0; + mozilla::css::Rule* aOldStyleRule, + mozilla::css::Rule* aNewStyleRule) = 0; /** * A StyleRule has just been added to a style sheet. @@ -172,7 +177,7 @@ public: */ virtual void StyleRuleAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) = 0; + mozilla::css::Rule* aStyleRule) = 0; /** * A StyleRule has just been removed from a style sheet. @@ -187,7 +192,7 @@ public: */ virtual void StyleRuleRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) = 0; + mozilla::css::Rule* aStyleRule) = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID) @@ -232,18 +237,18 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID) #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED \ virtual void StyleRuleChanged(nsIDocument* aDocument, \ nsIStyleSheet* aStyleSheet, \ - nsIStyleRule* aOldStyleRule, \ - nsIStyleRule* aNewStyleRule) override; + mozilla::css::Rule* aOldStyleRule, \ + mozilla::css::Rule* aNewStyleRule) override; #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED \ virtual void StyleRuleAdded(nsIDocument* aDocument, \ nsIStyleSheet* aStyleSheet, \ - nsIStyleRule* aStyleRule) override; + mozilla::css::Rule* aStyleRule) override; #define NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED \ virtual void StyleRuleRemoved(nsIDocument* aDocument, \ nsIStyleSheet* aStyleSheet, \ - nsIStyleRule* aStyleRule) override; + mozilla::css::Rule* aStyleRule) override; #define NS_DECL_NSIDOCUMENTOBSERVER \ NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE \ @@ -321,20 +326,20 @@ _class::StyleSheetApplicableStateChanged(nsIDocument* aDocument, \ void \ _class::StyleRuleChanged(nsIDocument* aDocument, \ nsIStyleSheet* aStyleSheet, \ - nsIStyleRule* aOldStyleRule, \ - nsIStyleRule* aNewStyleRule) \ + mozilla::css::Rule* aOldStyleRule, \ + mozilla::css::Rule* aNewStyleRule) \ { \ } \ void \ _class::StyleRuleAdded(nsIDocument* aDocument, \ nsIStyleSheet* aStyleSheet, \ - nsIStyleRule* aStyleRule) \ + mozilla::css::Rule* aStyleRule) \ { \ } \ void \ _class::StyleRuleRemoved(nsIDocument* aDocument, \ nsIStyleSheet* aStyleSheet, \ - nsIStyleRule* aStyleRule) \ + mozilla::css::Rule* aStyleRule) \ { \ } diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 27ff9d5ffa..0a9ccf61f9 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -22,6 +22,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" +#include "mozilla/css/StyleRule.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/Event.h" #include "mozilla/dom/ShadowRoot.h" diff --git a/dom/base/nsStyledElement.cpp b/dom/base/nsStyledElement.cpp index fd0d1df8dd..af2d218866 100644 --- a/dom/base/nsStyledElement.cpp +++ b/dom/base/nsStyledElement.cpp @@ -14,7 +14,7 @@ #include "nsDOMCSSAttrDeclaration.h" #include "nsServiceManagerUtils.h" #include "nsIDocument.h" -#include "mozilla/css/StyleRule.h" +#include "mozilla/css/Declaration.h" #include "nsCSSParser.h" #include "mozilla/css/Loader.h" #include "nsIDOMMutationEvent.h" @@ -45,9 +45,9 @@ nsStyledElementNotElementCSSInlineStyle::ParseAttribute(int32_t aNamespaceID, } nsresult -nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aStyleRule, - const nsAString* aSerialized, - bool aNotify) +nsStyledElementNotElementCSSInlineStyle::SetInlineStyleDeclaration(css::Declaration* aDeclaration, + const nsAString* aSerialized, + bool aNotify) { SetMayHaveStyle(); bool modification = false; @@ -77,7 +77,7 @@ nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aSty modification = !!mAttrsAndChildren.GetAttr(nsGkAtoms::style); } - nsAttrValue attrValue(aStyleRule, aSerialized); + nsAttrValue attrValue(aDeclaration, aSerialized); // XXXbz do we ever end up with ADDITION here? I doubt it. uint8_t modType = modification ? @@ -89,16 +89,16 @@ nsStyledElementNotElementCSSInlineStyle::SetInlineStyleRule(css::StyleRule* aSty aNotify, kDontCallAfterSetAttr); } -css::StyleRule* -nsStyledElementNotElementCSSInlineStyle::GetInlineStyleRule() +css::Declaration* +nsStyledElementNotElementCSSInlineStyle::GetInlineStyleDeclaration() { if (!MayHaveStyle()) { return nullptr; } const nsAttrValue* attrVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); - if (attrVal && attrVal->Type() == nsAttrValue::eCSSStyleRule) { - return attrVal->GetCSSStyleRuleValue(); + if (attrVal && attrVal->Type() == nsAttrValue::eCSSDeclaration) { + return attrVal->GetCSSDeclarationValue(); } return nullptr; @@ -131,13 +131,13 @@ nsStyledElementNotElementCSSInlineStyle::ReparseStyleAttribute(bool aForceInData } const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); - if (oldVal && oldVal->Type() != nsAttrValue::eCSSStyleRule) { + if (oldVal && oldVal->Type() != nsAttrValue::eCSSDeclaration) { nsAttrValue attrValue; nsAutoString stringValue; oldVal->ToString(stringValue); ParseStyleAttribute(stringValue, attrValue, aForceInDataDoc); - // Don't bother going through SetInlineStyleRule, we don't want to fire off - // mutation events or document notifications anyway + // Don't bother going through SetInlineStyleDeclaration; we don't + // want to fire off mutation events or document notifications anyway nsresult rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue); NS_ENSURE_SUCCESS(rv, rv); } diff --git a/dom/base/nsStyledElement.h b/dom/base/nsStyledElement.h index b850d2d633..e4672a68da 100644 --- a/dom/base/nsStyledElement.h +++ b/dom/base/nsStyledElement.h @@ -19,7 +19,7 @@ namespace mozilla { namespace css { -class StyleRule; +class Declaration; } // namespace css } // namespace mozilla @@ -35,11 +35,11 @@ protected: {} public: - // nsIContent interface methods - virtual mozilla::css::StyleRule* GetInlineStyleRule() override; - virtual nsresult SetInlineStyleRule(mozilla::css::StyleRule* aStyleRule, - const nsAString* aSerialized, - bool aNotify) override; + // Element interface methods + virtual mozilla::css::Declaration* GetInlineStyleDeclaration() override; + virtual nsresult SetInlineStyleDeclaration(mozilla::css::Declaration* aDeclaration, + const nsAString* aSerialized, + bool aNotify) override; nsICSSDeclaration* Style(); diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp index 8cd2b1ff5d..ec0c5716ca 100644 --- a/dom/base/nsTreeSanitizer.cpp +++ b/dom/base/nsTreeSanitizer.cpp @@ -1071,17 +1071,12 @@ nsTreeSanitizer::MustPrune(int32_t aNamespace, } bool -nsTreeSanitizer::SanitizeStyleRule(mozilla::css::StyleRule *aRule, - nsAutoString &aRuleText) +nsTreeSanitizer::SanitizeStyleDeclaration(mozilla::css::Declaration* aDeclaration, + nsAutoString& aRuleText) { - bool didSanitize = false; - aRuleText.Truncate(); - mozilla::css::Declaration* style = aRule->GetDeclaration(); - if (style) { - didSanitize = style->HasProperty(eCSSProperty_binding); - style->RemoveProperty(eCSSProperty_binding); - style->ToString(aRuleText); - } + bool didSanitize = aDeclaration->HasProperty(eCSSProperty_binding); + aDeclaration->RemoveProperty(eCSSProperty_binding); + aDeclaration->ToString(aRuleText); return didSanitize; } @@ -1103,7 +1098,8 @@ nsTreeSanitizer::SanitizeStyleSheet(const nsAString& aOriginal, // Create the CSS parser, and parse the CSS text. nsCSSParser parser(nullptr, sheet); rv = parser.ParseSheet(aOriginal, aDocument->GetDocumentURI(), aBaseURI, - aDocument->NodePrincipal(), 0, false); + aDocument->NodePrincipal(), 0, + mozilla::css::eAuthorSheetFeatures); NS_ENSURE_SUCCESS(rv, true); // Mark the sheet as complete. MOZ_ASSERT(!sheet->IsModified(), @@ -1139,7 +1135,8 @@ nsTreeSanitizer::SanitizeStyleSheet(const nsAString& aOriginal, RefPtr styleRule = do_QueryObject(rule); NS_ASSERTION(styleRule, "Must be a style rule"); nsAutoString decl; - bool sanitized = SanitizeStyleRule(styleRule, decl); + bool sanitized = + SanitizeStyleDeclaration(styleRule->GetDeclaration(), decl); didSanitize = sanitized || didSanitize; if (!sanitized) { styleRule->GetCssText(decl); @@ -1161,10 +1158,7 @@ nsTreeSanitizer::SanitizeAttributes(mozilla::dom::Element* aElement, { uint32_t ac = aElement->GetAttrCount(); - nsresult rv; - for (int32_t i = ac - 1; i >= 0; --i) { - rv = NS_OK; const nsAttrName* attrName = aElement->GetAttrNameAt(i); int32_t attrNs = attrName->NamespaceID(); nsCOMPtr attrLocal = attrName->LocalName(); @@ -1176,17 +1170,14 @@ nsTreeSanitizer::SanitizeAttributes(mozilla::dom::Element* aElement, // Pass the CSS Loader object to the parser, to allow parser error // reports to include the outer window ID. nsCSSParser parser(document->CSSLoader()); - RefPtr rule; nsAutoString value; aElement->GetAttr(attrNs, attrLocal, value); - rv = parser.ParseStyleAttribute(value, - document->GetDocumentURI(), - baseURI, - document->NodePrincipal(), - getter_AddRefs(rule)); - if (NS_SUCCEEDED(rv)) { + RefPtr decl = + parser.ParseStyleAttribute(value, document->GetDocumentURI(), + baseURI, document->NodePrincipal()); + if (decl) { nsAutoString cleanValue; - if (SanitizeStyleRule(rule, cleanValue)) { + if (SanitizeStyleDeclaration(decl, cleanValue)) { aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style, cleanValue, diff --git a/dom/base/nsTreeSanitizer.h b/dom/base/nsTreeSanitizer.h index b33c11332b..b8700d775f 100644 --- a/dom/base/nsTreeSanitizer.h +++ b/dom/base/nsTreeSanitizer.h @@ -154,12 +154,12 @@ class MOZ_STACK_CLASS nsTreeSanitizer { * removes that property from the rule and reserializes in case the * property was found. * - * @param aRule The style rule to check + * @param aDeclaration The style declaration to check * @param aRuleText the serialized mutated rule if the method returns true * @return true if the rule was modified and false otherwise */ - bool SanitizeStyleRule(mozilla::css::StyleRule* aRule, - nsAutoString &aRuleText); + bool SanitizeStyleDeclaration(mozilla::css::Declaration* aDeclaration, + nsAutoString& aRuleText); /** * Parses a style sheet and reserializes it with the 'binding' property diff --git a/dom/bindings/crashtests/crashtests.list b/dom/bindings/crashtests/crashtests.list index 559d590a05..3b517dd885 100644 --- a/dom/bindings/crashtests/crashtests.list +++ b/dom/bindings/crashtests/crashtests.list @@ -1,11 +1,11 @@ -skip-if(1) load 769464.html # bug 823822 - assert often leaks into other tests +skip load 769464.html # bug 823822 - assert often leaks into other tests load 822340-1.html load 822340-2.html load 832899.html -load 860591.html load 860551.html -load 862610.html +load 860591.html load 862092.html +load 862610.html load 869038.html load 949940.html load 1010658-1.html diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index e9bc139b80..5f098325e5 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -2152,14 +2152,11 @@ CanvasRenderingContext2D::SetShadowColor(const nsAString& shadowColor) // filters // -static already_AddRefed -CreateStyleRule(nsINode* aNode, +static already_AddRefed +CreateDeclaration(nsINode* aNode, const nsCSSProperty aProp1, const nsAString& aValue1, bool* aChanged1, - const nsCSSProperty aProp2, const nsAString& aValue2, bool* aChanged2, - ErrorResult& error) + const nsCSSProperty aProp2, const nsAString& aValue2, bool* aChanged2) { - RefPtr rule; - nsIPrincipal* principal = aNode->NodePrincipal(); nsIDocument* document = aNode->OwnerDoc(); @@ -2170,38 +2167,32 @@ CreateStyleRule(nsINode* aNode, // to include the outer window ID. nsCSSParser parser(document->CSSLoader()); - error = parser.ParseStyleAttribute(EmptyString(), docURL, baseURL, - principal, getter_AddRefs(rule)); - if (error.Failed()) { - return nullptr; - } + RefPtr declaration = + parser.ParseStyleAttribute(EmptyString(), docURL, baseURL, principal); if (aProp1 != eCSSProperty_UNKNOWN) { parser.ParseProperty(aProp1, aValue1, docURL, baseURL, principal, - rule->GetDeclaration(), aChanged1, false); + declaration, aChanged1, false); } if (aProp2 != eCSSProperty_UNKNOWN) { parser.ParseProperty(aProp2, aValue2, docURL, baseURL, principal, - rule->GetDeclaration(), aChanged2, false); + declaration, aChanged2, false); } - rule->RuleMatched(); - - return rule.forget(); + declaration->SetImmutable(); + return declaration.forget(); } -static already_AddRefed -CreateFontStyleRule(const nsAString& aFont, - nsINode* aNode, - bool* aOutFontChanged, - ErrorResult& error) +static already_AddRefed +CreateFontDeclaration(const nsAString& aFont, + nsINode* aNode, + bool* aOutFontChanged) { bool lineHeightChanged; - return CreateStyleRule(aNode, + return CreateDeclaration(aNode, eCSSProperty_font, aFont, aOutFontChanged, - eCSSProperty_line_height, NS_LITERAL_STRING("normal"), &lineHeightChanged, - error); + eCSSProperty_line_height, NS_LITERAL_STRING("normal"), &lineHeightChanged); } static already_AddRefed @@ -2222,13 +2213,9 @@ GetFontParentStyleContext(Element* aElement, nsIPresShell* presShell, // otherwise inherit from default (10px sans-serif) bool changed; - RefPtr parentRule = - CreateFontStyleRule(NS_LITERAL_STRING("10px sans-serif"), - presShell->GetDocument(), &changed, error); - - if (error.Failed()) { - return nullptr; - } + RefPtr parentRule = + CreateFontDeclaration(NS_LITERAL_STRING("10px sans-serif"), + presShell->GetDocument(), &changed); nsTArray> parentRules; parentRules.AppendElement(parentRule); @@ -2243,13 +2230,12 @@ GetFontParentStyleContext(Element* aElement, nsIPresShell* presShell, } static bool -PropertyIsInheritOrInitial(StyleRule* aRule, const nsCSSProperty aProperty) +PropertyIsInheritOrInitial(Declaration* aDeclaration, const nsCSSProperty aProperty) { - css::Declaration* declaration = aRule->GetDeclaration(); // We know the declaration is not !important, so we can use // GetNormalBlock(). const nsCSSValue* filterVal = - declaration->GetNormalBlock()->ValueFor(aProperty); + aDeclaration->GetNormalBlock()->ValueFor(aProperty); return (!filterVal || (filterVal->GetUnit() == eCSSUnit_Unset || filterVal->GetUnit() == eCSSUnit_Inherit || filterVal->GetUnit() == eCSSUnit_Initial)); @@ -2262,13 +2248,9 @@ GetFontStyleContext(Element* aElement, const nsAString& aFont, ErrorResult& error) { bool fontParsedSuccessfully = false; - RefPtr rule = - CreateFontStyleRule(aFont, presShell->GetDocument(), - &fontParsedSuccessfully, error); - - if (error.Failed()) { - return nullptr; - } + RefPtr decl = + CreateFontDeclaration(aFont, presShell->GetDocument(), + &fontParsedSuccessfully); if (!fontParsedSuccessfully) { // We got a syntax error. The spec says this value must be ignored. @@ -2279,7 +2261,7 @@ GetFontStyleContext(Element* aElement, const nsAString& aFont, // 'inherit' and 'initial'. The easiest way to check for this is to look // at font-size-adjust, which the font shorthand resets to either 'none' or // '-moz-system-font'. - if (PropertyIsInheritOrInitial(rule, eCSSProperty_font_size_adjust)) { + if (PropertyIsInheritOrInitial(decl, eCSSProperty_font_size_adjust)) { return nullptr; } @@ -2300,7 +2282,7 @@ GetFontStyleContext(Element* aElement, const nsAString& aFont, "GetFontParentStyleContext should have returned an error if the presshell is being destroyed."); nsTArray> rules; - rules.AppendElement(rule); + rules.AppendElement(decl); // add a rule to prevent text zoom from affecting the style rules.AppendElement(new nsDisableTextZoomStyleRule); @@ -2312,38 +2294,32 @@ GetFontStyleContext(Element* aElement, const nsAString& aFont, // parsed (including having line-height removed). (Older drafts of // the spec required font sizes be converted to pixels, but that no // longer seems to be required.) - rule->GetDeclaration()->GetValue(eCSSProperty_font, aOutUsedFont); + decl->GetValue(eCSSProperty_font, aOutUsedFont); return sc.forget(); } -static already_AddRefed -CreateFilterStyleRule(const nsAString& aFilter, - nsINode* aNode, - bool* aOutFilterChanged, - ErrorResult& error) +static already_AddRefed +CreateFilterDeclaration(const nsAString& aFilter, + nsINode* aNode, + bool* aOutFilterChanged) { bool dummy; - return CreateStyleRule(aNode, + return CreateDeclaration(aNode, eCSSProperty_filter, aFilter, aOutFilterChanged, - eCSSProperty_UNKNOWN, EmptyString(), &dummy, - error); + eCSSProperty_UNKNOWN, EmptyString(), &dummy); } static already_AddRefed -ResolveStyleForFilterRule(const nsAString& aFilterString, - nsIPresShell* aPresShell, - nsStyleContext* aParentContext, - ErrorResult& error) +ResolveStyleForFilter(const nsAString& aFilterString, + nsIPresShell* aPresShell, + nsStyleContext* aParentContext, + ErrorResult& error) { nsIDocument* document = aPresShell->GetDocument(); bool filterChanged = false; - RefPtr rule = - CreateFilterStyleRule(aFilterString, document, &filterChanged, error); - - if (error.Failed()) { - return nullptr; - } + RefPtr decl = + CreateFilterDeclaration(aFilterString, document, &filterChanged); if (!filterChanged) { // Refuse to accept the filter, but do not throw an error. @@ -2352,12 +2328,12 @@ ResolveStyleForFilterRule(const nsAString& aFilterString, // In addition to unparseable values, the spec says we need to reject // 'inherit' and 'initial'. - if (PropertyIsInheritOrInitial(rule, eCSSProperty_filter)) { + if (PropertyIsInheritOrInitial(decl, eCSSProperty_filter)) { return nullptr; } nsTArray> rules; - rules.AppendElement(rule); + rules.AppendElement(decl); RefPtr sc = aPresShell->StyleSet()->ResolveStyleForRules(aParentContext, rules); @@ -2392,7 +2368,7 @@ CanvasRenderingContext2D::ParseFilter(const nsAString& aString, } RefPtr sc = - ResolveStyleForFilterRule(aString, presShell, parentContext, error); + ResolveStyleForFilter(aString, presShell, parentContext, error); if (!sc) { return false; diff --git a/dom/canvas/crashtests/crashtests.list b/dom/canvas/crashtests/crashtests.list index 5f52a9ca3c..5704451480 100644 --- a/dom/canvas/crashtests/crashtests.list +++ b/dom/canvas/crashtests/crashtests.list @@ -1,18 +1,16 @@ +load 0px-size-font-667225.html +load 0px-size-font-shadow.html load 360293-1.html load 421715-1.html load 553938-1.html load 647480.html load 727547.html -load 0px-size-font-667225.html -load 0px-size-font-shadow.html -load texImage2D.html load 729116.html load 745699-1.html load 746813-1.html -# this test crashes in a bunch places still -#load 745818-large-source.html load 743499-negative-size.html -skip-if(Android) load 767337-1.html +skip-if(Android||B2G) load 745818-large-source.html # Bug XXX - Crashes Android/B2G mid-run w/o a stack +load 767337-1.html skip-if(Android||B2G) load 780392-1.html # bug 833371 for B2G skip-if(Android||B2G) skip-if(gtkWidget&&isDebugBuild) load 789933-1.html # bug 833371 for B2G, bug 1155252 for linux load 794463-1.html diff --git a/dom/events/crashtests/crashtests.list b/dom/events/crashtests/crashtests.list index 1f22621e43..88948dfdba 100644 --- a/dom/events/crashtests/crashtests.list +++ b/dom/events/crashtests/crashtests.list @@ -8,10 +8,10 @@ load 682637-1.html load 1033343.html load 1035654-1.html load 1035654-2.html +needs-focus load 1072137-1.html load 1143972-1.html load 1190036-1.html -needs-focus load 1072137-1.html load eventctor-nulldictionary.html load eventctor-nullstorage.html -load recursive-onload.html load recursive-DOMNodeInserted.html +load recursive-onload.html diff --git a/dom/html/crashtests/crashtests.list b/dom/html/crashtests/crashtests.list index 3dde0eadc8..93cadf893e 100644 --- a/dom/html/crashtests/crashtests.list +++ b/dom/html/crashtests/crashtests.list @@ -1,3 +1,4 @@ +load 68912-1.html load 257818-1.html load 285166-1.html load 294235-1.html @@ -49,7 +50,6 @@ load 673853.html load 680922-1.xul load 682058.xhtml load 682460.html -load 68912-1.html load 738744.xhtml load 741218.json load 741250.xhtml diff --git a/dom/html/nsGenericHTMLElement.cpp b/dom/html/nsGenericHTMLElement.cpp index 066087eea3..0819d7a58d 100644 --- a/dom/html/nsGenericHTMLElement.cpp +++ b/dom/html/nsGenericHTMLElement.cpp @@ -20,7 +20,7 @@ #include "nsQueryObject.h" #include "nsIContentInlines.h" #include "nsIContentViewer.h" -#include "mozilla/css/StyleRule.h" +#include "mozilla/css/Declaration.h" #include "nsIDocument.h" #include "nsIDocumentEncoder.h" #include "nsIDOMHTMLDocument.h" @@ -35,7 +35,6 @@ #include "nsHTMLStyleSheet.h" #include "nsIHTMLDocument.h" #include "nsPIDOMWindow.h" -#include "nsIStyleRule.h" #include "nsIURL.h" #include "nsEscape.h" #include "nsIFrameInlines.h" @@ -226,15 +225,14 @@ nsGenericHTMLElement::CopyInnerTo(Element* aDst) value->ToString(valStr); if (name->Equals(nsGkAtoms::style, kNameSpaceID_None) && - value->Type() == nsAttrValue::eCSSStyleRule) { + value->Type() == nsAttrValue::eCSSDeclaration) { // We can't just set this as a string, because that will fail // to reparse the string into style data until the node is // inserted into the document. Clone the Rule instead. - RefPtr ruleClone = value->GetCSSStyleRuleValue()->Clone(); - RefPtr styleRule = do_QueryObject(ruleClone); - NS_ENSURE_TRUE(styleRule, NS_ERROR_UNEXPECTED); + RefPtr declClone = + new css::Declaration(*value->GetCSSDeclarationValue()); - rv = aDst->SetInlineStyleRule(styleRule, &valStr, false); + rv = aDst->SetInlineStyleDeclaration(declClone, &valStr, false); NS_ENSURE_SUCCESS(rv, rv); continue; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 4502f9f107..189debdf79 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -24,6 +24,7 @@ #include +#include "mozilla/a11y/PDocAccessible.h" #include "AppProcessChecker.h" #include "AudioChannelService.h" #include "BlobParent.h" @@ -5363,3 +5364,19 @@ ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aD nsDependentString(aData)); return NS_OK; } + +bool +ContentParent::HandleWindowsMessages(const Message& aMsg) const +{ + MOZ_ASSERT(aMsg.is_sync()); + + // a11y messages can be triggered by windows messages, which means if we + // allow handling windows messages while we wait for the response to a sync + // a11y message we can reenter the ipc message sending code. + if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() && + a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) { + return false; + } + + return true; +} diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 0d1b35f54d..2c0d9e4c1d 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -409,6 +409,8 @@ public: virtual bool DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override; + virtual bool HandleWindowsMessages(const Message& aMsg) const override; + bool HasGamepadListener() const { return mHasGamepadListener; } void SetNuwaParent(NuwaParent* aNuwaParent) { mNuwaParent = aNuwaParent; } diff --git a/dom/media/test/crashtests/crashtests.list b/dom/media/test/crashtests/crashtests.list index 9c7790e6f4..b7c78873fb 100644 --- a/dom/media/test/crashtests/crashtests.list +++ b/dom/media/test/crashtests/crashtests.list @@ -5,8 +5,8 @@ load 466945-1.html load 468763-1.html load 474744-1.html HTTP load 481136-1.html # needs to be HTTP to recognize the ogg as an audio file? -load 493915-1.html load 492286-1.xhtml +load 493915-1.html load 495794-1.html load 576612-1.html load 752784-1.html diff --git a/dom/plugins/ipc/PluginModuleParent.cpp b/dom/plugins/ipc/PluginModuleParent.cpp index 20bc44e61f..a712d753c7 100755 --- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -361,7 +361,7 @@ PRCList PluginModuleMapping::sModuleListHead = bool PluginModuleMapping::sIsLoadModuleOnStack = false; -} // anonymous namespace +} // namespace void mozilla::plugins::TerminatePlugin(uint32_t aPluginId, diff --git a/dom/smil/crashtests/crashtests.list b/dom/smil/crashtests/crashtests.list index 729d679a0e..f783bb49ed 100644 --- a/dom/smil/crashtests/crashtests.list +++ b/dom/smil/crashtests/crashtests.list @@ -12,9 +12,9 @@ load 541297-1.svg load 547333-1.svg load 548899-1.svg load 551620-1.svg +load 554141-1.svg load 554202-1.svg load 554202-2.svg -load 554141-1.svg load 555026-1.svg load 556841-1.svg load 572938-1.svg @@ -29,8 +29,8 @@ load 594653-1.svg load 596796-1.svg load 605345-1.svg load 606101-1.svg -load 608549-1.svg load 608295-1.html +load 608549-1.svg load 611927-1.svg load 615002-1.svg load 615872-1.svg diff --git a/dom/smil/nsSMILAnimationController.cpp b/dom/smil/nsSMILAnimationController.cpp index 80b3847107..a3b51170bc 100644 --- a/dom/smil/nsSMILAnimationController.cpp +++ b/dom/smil/nsSMILAnimationController.cpp @@ -719,7 +719,7 @@ nsSMILAnimationController::AddStyleUpdatesTo(RestyleTracker& aTracker) } // mIsCSS true means that the rules are the ones returned from - // Element::GetSMILOverrideStyleRule (via nsSMILCSSProperty objects), + // Element::GetSMILOverrideStyleDeclaration (via nsSMILCSSProperty objects), // and mIsCSS false means the rules are nsSMILMappedAttribute objects // returned from nsSVGElement::GetAnimatedContentStyleRule. nsRestyleHint rshint = key.mIsCSS ? eRestyle_StyleAttribute_Animations diff --git a/dom/svg/SVGDocument.cpp b/dom/svg/SVGDocument.cpp index 5bd0de8123..a1e09a9fae 100644 --- a/dom/svg/SVGDocument.cpp +++ b/dom/svg/SVGDocument.cpp @@ -147,7 +147,9 @@ SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded() NS_NewURI(getter_AddRefs(uri), spec); if (uri) { RefPtr cssSheet; - cssLoader->LoadSheetSync(uri, true, true, getter_AddRefs(cssSheet)); + cssLoader->LoadSheetSync(uri, + mozilla::css::eAgentSheetFeatures, + true, getter_AddRefs(cssSheet)); if (cssSheet) { EnsureOnDemandBuiltInUASheet(cssSheet); } diff --git a/dom/svg/crashtests/crashtests.list b/dom/svg/crashtests/crashtests.list index e214064aa5..5a8f8cb0ee 100644 --- a/dom/svg/crashtests/crashtests.list +++ b/dom/svg/crashtests/crashtests.list @@ -35,7 +35,7 @@ load 409811-1.html load 410659-1.svg load 410659-2.svg load 410659-3.svg -asserts(0-5) load 412104-1.svg +asserts(3-4) load 412104-1.svg # bug 903785 load 413174-1.svg load 414188-1.svg load 427325-1.svg @@ -57,7 +57,6 @@ load 601406-1.svg load 603145-1.svg load 613899-1.svg load 613899-2.svg -load zero-size-image.svg load 719779-1.svg load 723441-1.html load 751515-1.svg diff --git a/dom/svg/nsSVGElement.cpp b/dom/svg/nsSVGElement.cpp index 9db080a4cb..a8a9ba1e55 100644 --- a/dom/svg/nsSVGElement.cpp +++ b/dom/svg/nsSVGElement.cpp @@ -257,7 +257,7 @@ nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, } const nsAttrValue* oldVal = mAttrsAndChildren.GetAttr(nsGkAtoms::style); - if (oldVal && oldVal->Type() == nsAttrValue::eCSSStyleRule) { + if (oldVal && oldVal->Type() == nsAttrValue::eCSSDeclaration) { // we need to force a reparse because the baseURI of the document // may have changed, and in particular because we may be clones of // XBL anonymous content now being bound to the document we should @@ -269,8 +269,8 @@ nsSVGElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, oldVal->ToString(stringValue); // Force in data doc, since we already have a style rule ParseStyleAttribute(stringValue, attrValue, true); - // Don't bother going through SetInlineStyleRule, we don't want to fire off - // mutation events or document notifications anyway + // Don't bother going through SetInlineStyleDeclaration; we don't + // want to fire off mutation events or document notifications anyway rv = mAttrsAndChildren.SetAndSwapAttr(nsGkAtoms::style, attrValue); NS_ENSURE_SUCCESS(rv, rv); } @@ -903,8 +903,9 @@ nsSVGElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker) UpdateContentStyleRule(); if (mContentStyleRule) { - mContentStyleRule->RuleMatched(); - aRuleWalker->Forward(mContentStyleRule); + css::Declaration* declaration = mContentStyleRule->GetDeclaration(); + declaration->SetImmutable(); + aRuleWalker->Forward(declaration); } return NS_OK; @@ -927,8 +928,9 @@ nsSVGElement::WalkAnimatedContentStyleRules(nsRuleWalker* aRuleWalker) animContentStyleRule = GetAnimatedContentStyleRule(); } if (animContentStyleRule) { - animContentStyleRule->RuleMatched(); - aRuleWalker->Forward(animContentStyleRule); + css::Declaration* declaration = animContentStyleRule->GetDeclaration(); + declaration->SetImmutable(); + aRuleWalker->Forward(declaration); } } } diff --git a/dom/webidl/AnimationEffectReadonly.webidl b/dom/webidl/AnimationEffectReadonly.webidl index d8f556d34f..a71bb6c354 100644 --- a/dom/webidl/AnimationEffectReadonly.webidl +++ b/dom/webidl/AnimationEffectReadonly.webidl @@ -10,9 +10,45 @@ * liability, trademark and document use rules apply. */ +enum FillMode { + "none", + "forwards", + "backwards", + "both", + "auto" +}; + +enum PlaybackDirection { + "normal", + "reverse", + "alternate", + "alternate-reverse" +}; + +dictionary AnimationEffectTimingProperties { + double delay = 0.0; + double endDelay = 0.0; + FillMode fill = "auto"; + double iterationStart = 0.0; + unrestricted double iterations = 1.0; + (unrestricted double or DOMString) duration = "auto"; + PlaybackDirection direction = "normal"; + DOMString easing = "linear"; +}; + +dictionary ComputedTimingProperties : AnimationEffectTimingProperties { + unrestricted double endTime = 0.0; + unrestricted double activeDuration = 0.0; + double? localTime = null; + unrestricted double? progress = null; + unrestricted double? currentIteration = null; +}; + [Func="nsDocument::IsWebAnimationsEnabled"] interface AnimationEffectReadOnly { // Not yet implemented: // readonly attribute AnimationEffectTimingReadOnly timing; - // readonly attribute ComputedTimingProperties computedTiming; + + [BinaryName="getComputedTimingAsDict"] + ComputedTimingProperties getComputedTiming(); }; diff --git a/dom/workers/test/1158031.html b/dom/workers/test/crashtests/1158031.html similarity index 100% rename from dom/workers/test/1158031.html rename to dom/workers/test/crashtests/1158031.html diff --git a/dom/workers/test/943516.html b/dom/workers/test/crashtests/943516.html similarity index 100% rename from dom/workers/test/943516.html rename to dom/workers/test/crashtests/943516.html diff --git a/dom/workers/test/crashtests.list b/dom/workers/test/crashtests/crashtests.list similarity index 100% rename from dom/workers/test/crashtests.list rename to dom/workers/test/crashtests/crashtests.list diff --git a/dom/xbl/crashtests/crashtests.list b/dom/xbl/crashtests/crashtests.list index 746a52f567..ab582fcdc3 100644 --- a/dom/xbl/crashtests/crashtests.list +++ b/dom/xbl/crashtests/crashtests.list @@ -18,9 +18,9 @@ load 404125-1.xhtml load 406900-1.xul load 406904-1.xhtml load 406904-2.xhtml +load 415192-1.xul load 415301-1.xul load 418133-1.xhtml -load 415192-1.xul load 420233-1.xhtml load 421997-1.xhtml load 432813-1.xhtml @@ -35,6 +35,6 @@ load 493123-1.xhtml load 495354-1.xhtml load 507628-1.xhtml load 507991-1.xhtml -load set-field-bad-this.xhtml load 830614-1.xul load 895805-1.xhtml +load set-field-bad-this.xhtml diff --git a/dom/xslt/crashtests/crashtests.list b/dom/xslt/crashtests/crashtests.list index 29e2a6e858..06daebc8a8 100644 --- a/dom/xslt/crashtests/crashtests.list +++ b/dom/xslt/crashtests/crashtests.list @@ -12,7 +12,7 @@ load 528488.xml load 528963.xml load 545927.html load 601543.html -load 603844.html load 602115.html +load 603844.html load 667315.xml load 1089049.html diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index 81a87d45ff..2ca0409b78 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -384,15 +384,14 @@ nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const nsAttrValue attrValue; // Style rules need to be cloned. - if (originalValue->Type() == nsAttrValue::eCSSStyleRule) { - RefPtr ruleClone = - originalValue->GetCSSStyleRuleValue()->Clone(); + if (originalValue->Type() == nsAttrValue::eCSSDeclaration) { + RefPtr declClone = + new css::Declaration(*originalValue->GetCSSDeclarationValue()); nsString stringValue; originalValue->ToString(stringValue); - RefPtr styleRule = do_QueryObject(ruleClone); - attrValue.SetTo(styleRule, &stringValue); + attrValue.SetTo(declClone, &stringValue); } else { attrValue.SetTo(*originalValue); } @@ -1860,15 +1859,14 @@ nsXULElement::MakeHeavyweight(nsXULPrototypeElement* aPrototype) nsAttrValue attrValue; // Style rules need to be cloned. - if (protoattr->mValue.Type() == nsAttrValue::eCSSStyleRule) { - RefPtr ruleClone = - protoattr->mValue.GetCSSStyleRuleValue()->Clone(); + if (protoattr->mValue.Type() == nsAttrValue::eCSSDeclaration) { + RefPtr declClone = new css::Declaration( + *protoattr->mValue.GetCSSDeclarationValue()); nsString stringValue; protoattr->mValue.ToString(stringValue); - RefPtr styleRule = do_QueryObject(ruleClone); - attrValue.SetTo(styleRule, &stringValue); + attrValue.SetTo(declClone, &stringValue); } else { attrValue.SetTo(protoattr->mValue); } @@ -2435,7 +2433,6 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue, } else if (mAttributes[aPos].mName.Equals(nsGkAtoms::style)) { mHasStyleAttribute = true; // Parse the element's 'style' attribute - RefPtr rule; nsCSSParser parser; @@ -2443,14 +2440,14 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue, // TODO: If we implement Content Security Policy for chrome documents // as has been discussed, the CSP should be checked here to see if // inline styles are allowed to be applied. - parser.ParseStyleAttribute(aValue, aDocumentURI, aDocumentURI, - // This is basically duplicating what - // nsINode::NodePrincipal() does - mNodeInfo->NodeInfoManager()-> - DocumentPrincipal(), - getter_AddRefs(rule)); - if (rule) { - mAttributes[aPos].mValue.SetTo(rule, &aValue); + RefPtr declaration = + parser.ParseStyleAttribute(aValue, aDocumentURI, aDocumentURI, + // This is basically duplicating what + // nsINode::NodePrincipal() does + mNodeInfo->NodeInfoManager()-> + DocumentPrincipal()); + if (declaration) { + mAttributes[aPos].mValue.SetTo(declaration, &aValue); return NS_OK; } diff --git a/editor/crashtests.list b/editor/crashtests.list deleted file mode 100644 index 5c8a04b68a..0000000000 --- a/editor/crashtests.list +++ /dev/null @@ -1,7 +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/. - -include libeditor/crashtests/crashtests.list -include composer/crashtests/crashtests.list -include txmgr/tests/crashtests/crashtests.list diff --git a/editor/libeditor/crashtests/crashtests.list b/editor/libeditor/crashtests/crashtests.list index 03bf9d7ca9..9a59044dea 100644 --- a/editor/libeditor/crashtests/crashtests.list +++ b/editor/libeditor/crashtests/crashtests.list @@ -43,17 +43,17 @@ asserts(0-1) load 716456-1.html load 759748.html load 761861.html load 762183.html -load 769008-1.html load 766305.html load 766360.html load 766387.html load 766413.html -load 766845.xhtml load 766795.html +load 766845.xhtml load 767169.html -load 769967.xhtml load 768748.html load 768765.html +load 769008-1.html +load 769967.xhtml needs-focus load 771749.html load 772282.html load 776323.html diff --git a/editor/libeditor/nsHTMLCSSUtils.cpp b/editor/libeditor/nsHTMLCSSUtils.cpp index 63f631f507..5cbc37636f 100644 --- a/editor/libeditor/nsHTMLCSSUtils.cpp +++ b/editor/libeditor/nsHTMLCSSUtils.cpp @@ -516,15 +516,15 @@ nsHTMLCSSUtils::GetCSSInlinePropertyBase(nsINode* aNode, nsIAtom* aProperty, } MOZ_ASSERT(aStyleType == eSpecified); - RefPtr rule = element->GetInlineStyleRule(); - if (!rule) { + RefPtr decl = element->GetInlineStyleDeclaration(); + if (!decl) { return NS_OK; } nsCSSProperty prop = nsCSSProps::LookupProperty(nsDependentAtomString(aProperty), nsCSSProps::eEnabledForAllContent); MOZ_ASSERT(prop != eCSSProperty_UNKNOWN); - rule->GetDeclaration()->GetValue(prop, aValue); + decl->GetValue(prop, aValue); return NS_OK; } diff --git a/editor/libeditor/nsHTMLEditor.cpp b/editor/libeditor/nsHTMLEditor.cpp index d4cdd62394..2004941a59 100644 --- a/editor/libeditor/nsHTMLEditor.cpp +++ b/editor/libeditor/nsHTMLEditor.cpp @@ -2842,7 +2842,8 @@ nsHTMLEditor::AddOverrideStyleSheet(const nsAString& aURL) RefPtr sheet; // Editor override style sheets may want to style Gecko anonymous boxes rv = ps->GetDocument()->CSSLoader()-> - LoadSheetSync(uaURI, true, true, getter_AddRefs(sheet)); + LoadSheetSync(uaURI, mozilla::css::eAgentSheetFeatures, true, + getter_AddRefs(sheet)); // Synchronous loads should ALWAYS return completed NS_ENSURE_TRUE(sheet, NS_ERROR_NULL_POINTER); diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp index a36f01483f..df974ca6a5 100644 --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -538,19 +538,20 @@ SampleAnimations(Layer* aLayer, TimeStamp aPoint) dom::KeyframeEffectReadOnly::GetComputedTimingAt( Nullable(elapsedDuration), timing); - MOZ_ASSERT(0.0 <= computedTiming.mProgress && - computedTiming.mProgress <= 1.0, + MOZ_ASSERT(!computedTiming.mProgress.IsNull() && + 0.0 <= computedTiming.mProgress.Value() && + computedTiming.mProgress.Value() <= 1.0, "iteration progress should be in [0-1]"); int segmentIndex = 0; AnimationSegment* segment = animation.segments().Elements(); - while (segment->endPortion() < computedTiming.mProgress) { + while (segment->endPortion() < computedTiming.mProgress.Value()) { ++segment; ++segmentIndex; } double positionInSegment = - (computedTiming.mProgress - segment->startPortion()) / + (computedTiming.mProgress.Value() - segment->startPortion()) / (segment->endPortion() - segment->startPortion()); double portion = diff --git a/gfx/tests/crashtests/crashtests.list b/gfx/tests/crashtests/crashtests.list index d526e78bba..b577715a31 100644 --- a/gfx/tests/crashtests/crashtests.list +++ b/gfx/tests/crashtests/crashtests.list @@ -25,16 +25,16 @@ load 377232-1.xhtml load 377461-1.xhtml load 383473-1.html load 383872-1.svg -load 385289-1.xhtml skip-if(Android&&smallScreen) load 385228-1.svg # bug 523255 / bug 385228, nexus-s Android 2.3.6 skip load 385228-2.svg # bug 523255 / bug 385228 +load 385289-1.xhtml load 385417-1.html load 385417-2.html load 385423-1.html load 385423-2.html load 385719-1.html load 389326-1.html -skip load 390476.html # bug 585185 +load 390476.html load 393746-1.xhtml load 393749-1.html load 393822-1.html @@ -92,8 +92,8 @@ load 593526.xul load 594654-1.xhtml load 595727-1.html load 624198.xhtml -load 633453-1.html load 633322-1.html +load 633453-1.html load 665218.html load 686190-1.html load 693143-1.html diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index a83f825a4f..2e0858cb0e 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -916,6 +916,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) return false; } + bool handleWindowsMessages = mListener->HandleWindowsMessages(*aMsg); mLink->SendMessage(msg.forget()); while (true) { @@ -936,7 +937,7 @@ MessageChannel::Send(Message* aMsg, Message* aReply) MOZ_ASSERT(!mTimedOutMessageSeqno); - bool maybeTimedOut = !WaitForSyncNotify(); + bool maybeTimedOut = !WaitForSyncNotify(handleWindowsMessages); if (!Connected()) { ReportConnectionError("MessageChannel::SendAndWait"); @@ -1583,7 +1584,7 @@ MessageChannel::WaitResponse(bool aWaitTimedOut) #ifndef OS_WIN bool -MessageChannel::WaitForSyncNotify() +MessageChannel::WaitForSyncNotify(bool /* aHandleWindowsMessages */) { PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ? PR_INTERVAL_NO_TIMEOUT : @@ -1601,7 +1602,7 @@ MessageChannel::WaitForSyncNotify() bool MessageChannel::WaitForInterruptNotify() { - return WaitForSyncNotify(); + return WaitForSyncNotify(true); } void diff --git a/ipc/glue/MessageChannel.h b/ipc/glue/MessageChannel.h index 6fa56dc74c..b11f05748a 100644 --- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -274,7 +274,7 @@ class MessageChannel : HasResultCodes // // So in sum: true is a meaningful return value; false isn't, // necessarily. - bool WaitForSyncNotify(); + bool WaitForSyncNotify(bool aHandleWindowsMessages); bool WaitForInterruptNotify(); bool WaitResponse(bool aWaitTimedOut); diff --git a/ipc/glue/MessageLink.h b/ipc/glue/MessageLink.h index 776d3da574..0663246744 100644 --- a/ipc/glue/MessageLink.h +++ b/ipc/glue/MessageLink.h @@ -94,6 +94,12 @@ class MessageListener return RIPChildWins; } + /** + * Return true if windows messages can be handled while waiting for a reply + * to a sync IPDL message. + */ + virtual bool HandleWindowsMessages(const Message& aMsg) const { return true; } + virtual void OnEnteredSyncSend() { } virtual void OnExitedSyncSend() { diff --git a/ipc/glue/WindowsMessageLoop.cpp b/ipc/glue/WindowsMessageLoop.cpp index c871336997..b463edd064 100644 --- a/ipc/glue/WindowsMessageLoop.cpp +++ b/ipc/glue/WindowsMessageLoop.cpp @@ -944,7 +944,7 @@ DeneuteredWindowRegion::~DeneuteredWindowRegion() } bool -MessageChannel::WaitForSyncNotify() +MessageChannel::WaitForSyncNotify(bool aHandleWindowsMessages) { mMonitor->AssertCurrentThreadOwns(); @@ -952,7 +952,7 @@ MessageChannel::WaitForSyncNotify() // Use a blocking wait if this channel does not require // Windows message deferral behavior. - if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) { + if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION) || !aHandleWindowsMessages) { PRIntervalTime timeout = (kNoTimeout == mTimeoutMs) ? PR_INTERVAL_NO_TIMEOUT : PR_MillisecondsToInterval(mTimeoutMs); @@ -1088,7 +1088,7 @@ MessageChannel::WaitForInterruptNotify() // Re-use sync notification wait code if this channel does not require // Windows message deferral behavior. if (!(mFlags & REQUIRE_DEFERRED_MESSAGE_PROTECTION)) { - return WaitForSyncNotify(); + return WaitForSyncNotify(true); } if (!InterruptStackDepth() && !AwaitingIncomingMessage()) { diff --git a/js/src/jit-test/tests/basic/bug1219363.js b/js/src/jit-test/tests/basic/bug1219363.js new file mode 100644 index 0000000000..03e4008ab0 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1219363.js @@ -0,0 +1,9 @@ +var x = [1, 2, , 4] +x[100000] = 1; +var y = Object.create(x); +y.a = 1; +y.b = 1; +var arr = []; +for (var z in y) + arr.push(z); +assertEq(arr.join(), "a,b,0,1,3,100000"); diff --git a/js/src/jit-test/tests/basic/unboxed-property-enumeration.js b/js/src/jit-test/tests/basic/unboxed-property-enumeration.js new file mode 100644 index 0000000000..142d932dd3 --- /dev/null +++ b/js/src/jit-test/tests/basic/unboxed-property-enumeration.js @@ -0,0 +1,24 @@ +function O() { + this.x = 1; + this.y = 2; +} +function testUnboxed() { + var arr = []; + for (var i=0; i<100; i++) + arr.push(new O); + + var o = arr[arr.length-1]; + o[0] = 0; + o[2] = 2; + var sym = Symbol(); + o[sym] = 1; + o.z = 3; + Object.defineProperty(o, '3', {value:1,enumerable:false,configurable:false,writable:false}); + o[4] = 4; + + var props = Reflect.ownKeys(o); + assertEq(props[props.length-1], sym); + + assertEq(Object.getOwnPropertyNames(o).join(""), "0234xyz"); +} +testUnboxed(); diff --git a/js/src/jit-test/tests/ctypes/conversion-pointer.js b/js/src/jit-test/tests/ctypes/conversion-pointer.js index cfdcc8ed87..d2583b8789 100644 --- a/js/src/jit-test/tests/ctypes/conversion-pointer.js +++ b/js/src/jit-test/tests/ctypes/conversion-pointer.js @@ -17,7 +17,7 @@ function test() { "can't convert the object ({}) to the type test_struct.ptr"); assertTypeErrorMessage(() => { test_struct.ptr().value = [1, 2]; }, "can't convert the array [1, 2] to the type test_struct.ptr"); - assertTypeErrorMessage(() => { test_struct.ptr().value = Int8Array([1, 2]); }, + assertTypeErrorMessage(() => { test_struct.ptr().value = new Int8Array([1, 2]); }, "can't convert the typed array ({0:1, 1:2}) to the type test_struct.ptr"); // contents setter diff --git a/js/src/jit-test/tests/gc/oomInArrayProtoTest.js b/js/src/jit-test/tests/gc/oomInArrayProtoTest.js new file mode 100644 index 0000000000..9471013940 --- /dev/null +++ b/js/src/jit-test/tests/gc/oomInArrayProtoTest.js @@ -0,0 +1,24 @@ +load(libdir + 'oomTest.js'); + +function arrayProtoOutOfRange() { + function f(obj) { + return typeof obj[15]; + } + + function test() { + var a = [1, 2]; + a.__proto__ = {15: 1337}; + var b = [1, 2, 3, 4]; + + for (var i = 0; i < 1000; i++) { + var r = f(i % 2 ? a : b); + assertEq(r, i % 2 ? "number" : "undefined"); + } + } + + test(); + test(); + test(); +} + +oomTest(arrayProtoOutOfRange); diff --git a/js/src/jit-test/tests/ion/bug1132290.js b/js/src/jit-test/tests/ion/bug1132290.js index 69acffd243..0799d150b7 100644 --- a/js/src/jit-test/tests/ion/bug1132290.js +++ b/js/src/jit-test/tests/ion/bug1132290.js @@ -1,6 +1,6 @@ f = function() { - v = Uint8Array() + v = new Uint8Array() function f(x) { return x + v[0] | 0 } diff --git a/js/src/jit-test/tests/ion/bug1138740.js b/js/src/jit-test/tests/ion/bug1138740.js index 0c3acf3078..195717c976 100644 --- a/js/src/jit-test/tests/ion/bug1138740.js +++ b/js/src/jit-test/tests/ion/bug1138740.js @@ -1,6 +1,6 @@ with({}){} -x = Int8Array(1) +x = new Int8Array(1) function f(y) { x[0] = y } diff --git a/js/src/jit-test/tests/ion/bug1216157.js b/js/src/jit-test/tests/ion/bug1216157.js new file mode 100644 index 0000000000..6fa2e17a58 --- /dev/null +++ b/js/src/jit-test/tests/ion/bug1216157.js @@ -0,0 +1,12 @@ +if (!('oomAfterAllocations' in this)) + quit(); +gcslice(0); // Start IGC, but don't mark anything. +function f(str) { + for (var i = 0; i < 10; i++) { + arr = /foo(ba(r))?/.exec(str); + var x = arr[oomAfterAllocations(100)] + " " + arr[1] + " " + 1899; + } +} +try { + f("foo"); +} catch(e) {} diff --git a/js/src/jit-test/tests/modules/bug-1217593.js b/js/src/jit-test/tests/modules/bug-1217593.js new file mode 100644 index 0000000000..ebf210b38e --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1217593.js @@ -0,0 +1,6 @@ +enableOsiPointRegisterChecks(); +function f() { + return this; +} +f(); +f(); diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 4f4166739f..be23876fe4 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1441,6 +1441,8 @@ JitCompartment::generateRegExpExecStub(JSContext* cx) Linker linker(masm); AutoFlushICache afc("RegExpExecStub"); JitCode* code = linker.newCode(cx, OTHER_CODE); + if (!code) + return nullptr; #ifdef JS_ION_PERF writePerfSpewerJitCodeProfile(code, "RegExpExecStub"); @@ -1572,6 +1574,8 @@ JitCompartment::generateRegExpTestStub(JSContext* cx) Linker linker(masm); AutoFlushICache afc("RegExpTestStub"); JitCode* code = linker.newCode(cx, OTHER_CODE); + if (!code) + return nullptr; #ifdef JS_ION_PERF writePerfSpewerJitCodeProfile(code, "RegExpTestStub"); @@ -5099,13 +5103,13 @@ void CodeGenerator::visitComputeThis(LComputeThis* lir) { ValueOperand value = ToValue(lir, LComputeThis::ValueIndex); - Register output = ToRegister(lir->output()); + ValueOperand output = ToOutValue(lir); - OutOfLineCode* ool = oolCallVM(BoxNonStrictThisInfo, lir, ArgList(value), StoreValueTo(value)); + OutOfLineCode* ool = oolCallVM(BoxNonStrictThisInfo, lir, ArgList(value), StoreValueTo(output)); masm.branchTestObject(Assembler::NotEqual, value, ool->entry()); + masm.moveValue(value, output); masm.bind(ool->rejoin()); - masm.unboxObject(value, output); } void @@ -8029,7 +8033,8 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints) : FrameSizeClass::FromDepth(frameDepth_).frameSize(); // We encode safepoints after the OSI-point offsets have been determined. - encodeSafepoints(); + if (!encodeSafepoints()) + return false; AutoDiscardIonCode discardIonCode(cx, &recompileInfo); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 02570b2cad..b04b1fd86d 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -381,7 +381,7 @@ LIRGenerator::visitReturnFromCtor(MReturnFromCtor* ins) void LIRGenerator::visitComputeThis(MComputeThis* ins) { - MOZ_ASSERT(ins->type() == MIRType_Object); + MOZ_ASSERT(ins->type() == MIRType_Value); MOZ_ASSERT(ins->input()->type() == MIRType_Value); LComputeThis* lir = new(alloc()) LComputeThis(); @@ -391,7 +391,7 @@ LIRGenerator::visitComputeThis(MComputeThis* ins) // they aren't clobbered. useBox(lir, LComputeThis::ValueIndex, ins->input()); - define(lir, ins); + defineBox(lir, ins); assignSafepoint(lir, ins); } diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 72552d3830..26e0bda336 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -6718,7 +6718,7 @@ class MStringSplit } }; -// Returns an object to use as |this| value. See also ComputeThis and +// Returns the value to use as |this| value. See also ComputeThis and // BoxNonStrictThis in Interpreter.h. class MComputeThis : public MUnaryInstruction, @@ -6727,7 +6727,7 @@ class MComputeThis explicit MComputeThis(MDefinition* def) : MUnaryInstruction(def) { - setResultType(MIRType_Object); + setResultType(MIRType_Value); } public: diff --git a/js/src/jit/Safepoints.h b/js/src/jit/Safepoints.h index 783ad65e68..714d2f72d6 100644 --- a/js/src/jit/Safepoints.h +++ b/js/src/jit/Safepoints.h @@ -57,6 +57,9 @@ class SafepointWriter const uint8_t* buffer() const { return stream_.buffer(); } + bool oom() const { + return stream_.oom(); + } }; class SafepointReader diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index b5511830b4..a8b1b44582 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -621,7 +621,7 @@ CodeGeneratorShared::assignBailoutId(LSnapshot* snapshot) return bailouts_.append(snapshot->snapshotOffset()); } -void +bool CodeGeneratorShared::encodeSafepoints() { for (SafepointIndex& index : safepointIndices_) { @@ -632,6 +632,8 @@ CodeGeneratorShared::encodeSafepoints() index.resolve(); } + + return !safepoints_.oom(); } bool diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index 94d10fcc1a..70abe8b222 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -302,7 +302,7 @@ class CodeGeneratorShared : public LElementVisitor // Encode all encountered safepoints in CG-order, and resolve |indices| for // safepoint offsets. - void encodeSafepoints(); + bool encodeSafepoints(); // Fixup offsets of native-to-bytecode map. bool createNativeToBytecodeScriptList(JSContext* cx); @@ -522,7 +522,7 @@ class CodeGeneratorShared : public LElementVisitor inline void verifyHeapAccessDisassembly(uint32_t begin, uint32_t end, bool isLoad, Scalar::Type type, unsigned numElems, - const Operand &mem, LAllocation alloc); + const Operand& mem, LAllocation alloc); }; // An out-of-line path is generated at the end of the function. diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 6741aac75b..a90e6c280b 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -1459,7 +1459,7 @@ class LReturnFromCtor : public LInstructionHelper<1, BOX_PIECES + 1, 0> static const size_t ObjectIndex = BOX_PIECES; }; -class LComputeThis : public LInstructionHelper<1, BOX_PIECES, 0> +class LComputeThis : public LInstructionHelper { public: LIR_HEADER(ComputeThis) diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index 3b5d6b24f5..9b27317683 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -135,6 +135,33 @@ Enumerate(JSContext* cx, HandleObject pobj, jsid id, return props->append(id); } +static bool +EnumerateExtraProperties(JSContext* cx, HandleObject obj, unsigned flags, Maybe& ht, + AutoIdVector* props) +{ + MOZ_ASSERT(obj->getOps()->enumerate); + + AutoIdVector properties(cx); + bool enumerableOnly = !(flags & JSITER_HIDDEN); + if (!obj->getOps()->enumerate(cx, obj, properties, enumerableOnly)) + return false; + + RootedId id(cx); + for (size_t n = 0; n < properties.length(); n++) { + id = properties[n]; + + // The enumerate hook does not indicate whether the properties + // it returns are enumerable or not. Since we already passed + // `enumerableOnly` to the hook to filter out non-enumerable + // properties, it doesn't really matter what we pass here. + bool enumerable = true; + if (!Enumerate(cx, obj, id, enumerable, flags, ht, props)) + return false; + } + + return true; +} + static bool SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp) { @@ -147,13 +174,14 @@ SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp) static bool EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe& ht, - AutoIdVector* props) + AutoIdVector* props, Handle unboxed = nullptr) { bool enumerateSymbols; if (flags & JSITER_SYMBOLSONLY) { enumerateSymbols = true; } else { /* Collect any dense elements from this object. */ + size_t firstElemIndex = props->length(); size_t initlen = pobj->getDenseInitializedLength(); const Value* vp = pobj->getDenseElements(); bool hasHoles = false; @@ -179,7 +207,10 @@ EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags // Collect any sparse elements from this object. bool isIndexed = pobj->isIndexed(); if (isIndexed) { - size_t numElements = props->length(); + // If the dense elements didn't have holes, we don't need to include + // them in the sort. + if (!hasHoles) + firstElemIndex = props->length(); for (Shape::Range r(pobj->lastProperty()); !r.empty(); r.popFront()) { Shape& shape = r.front(); @@ -191,12 +222,10 @@ EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags } } - // If the dense elements didn't have holes, we don't need to include - // them in the sort. - size_t startIndex = hasHoles ? 0 : numElements; + MOZ_ASSERT(firstElemIndex <= props->length()); - jsid* ids = props->begin() + startIndex; - size_t n = props->length() - startIndex; + jsid* ids = props->begin() + firstElemIndex; + size_t n = props->length() - firstElemIndex; AutoIdVector tmp(cx); if (!tmp.resize(n)) @@ -207,6 +236,16 @@ EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags return false; } + if (unboxed) { + // If |unboxed| is set then |pobj| is the expando for an unboxed + // plain object we are enumerating. Add the unboxed properties + // themselves here since they are all property names that were + // given to the object before any of the expando's properties. + MOZ_ASSERT(pobj->is()); + if (!EnumerateExtraProperties(cx, unboxed, flags, ht, props)) + return false; + } + size_t initialLength = props->length(); /* Collect all unique property names from this object's shape. */ @@ -331,28 +370,23 @@ Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props) RootedObject pobj(cx, pobj_); do { - if (JSNewEnumerateOp enumerate = pobj->getOps()->enumerate) { - AutoIdVector properties(cx); - bool enumerableOnly = !(flags & JSITER_HIDDEN); - if (!enumerate(cx, pobj, properties, enumerableOnly)) - return false; - - RootedId id(cx); - for (size_t n = 0; n < properties.length(); n++) { - id = properties[n]; - - // The enumerate hook does not indicate whether the properties - // it returns are enumerable or not. Since we already passed - // `enumerableOnly` to the hook to filter out non-enumerable - // properties, it doesn't really matter what we pass here. - bool enumerable = true; - if (!Enumerate(cx, pobj, id, enumerable, flags, ht, props)) + if (pobj->getOps()->enumerate) { + if (pobj->is() && pobj->as().maybeExpando()) { + // Special case unboxed objects with an expando object. + RootedNativeObject expando(cx, pobj->as().maybeExpando()); + if (!EnumerateNativeProperties(cx, expando, flags, ht, props, + pobj.as())) + { return false; - } - - if (pobj->isNative()) { - if (!EnumerateNativeProperties(cx, pobj.as(), flags, ht, props)) + } + } else { + if (!EnumerateExtraProperties(cx, pobj, flags, ht, props)) return false; + + if (pobj->isNative()) { + if (!EnumerateNativeProperties(cx, pobj.as(), flags, ht, props)) + return false; + } } } else if (pobj->isNative()) { // Give the object a chance to resolve all lazy properties diff --git a/js/src/tests/ecma_6/ArrayBuffer/constructorNotCallable.js b/js/src/tests/ecma_6/ArrayBuffer/constructorNotCallable.js new file mode 100644 index 0000000000..9df97fe867 --- /dev/null +++ b/js/src/tests/ecma_6/ArrayBuffer/constructorNotCallable.js @@ -0,0 +1,8 @@ +assertThrowsInstanceOf(() => ArrayBuffer(), TypeError); +assertThrowsInstanceOf(() => ArrayBuffer(1), TypeError); +assertThrowsInstanceOf(() => ArrayBuffer.call(null), TypeError); +assertThrowsInstanceOf(() => ArrayBuffer.apply(null, []), TypeError); +assertThrowsInstanceOf(() => Reflect.apply(ArrayBuffer, null, []), TypeError); + +if (typeof reportCompare === 'function') + reportCompare(0,0,"OK"); diff --git a/js/src/tests/ecma_6/TypedArray/constructor-not-callable.js b/js/src/tests/ecma_6/TypedArray/constructor-not-callable.js new file mode 100644 index 0000000000..fb5e2331b0 --- /dev/null +++ b/js/src/tests/ecma_6/TypedArray/constructor-not-callable.js @@ -0,0 +1,22 @@ +const constructors = [ + Int8Array, + Uint8Array, + Uint8ClampedArray, + Int16Array, + Uint16Array, + Int32Array, + Uint32Array, + Float32Array, + Float64Array +]; + +for (var constructor of constructors) { + assertThrowsInstanceOf(() => constructor(), TypeError); + assertThrowsInstanceOf(() => constructor(1), TypeError); + assertThrowsInstanceOf(() => constructor.call(null), TypeError); + assertThrowsInstanceOf(() => constructor.apply(null, []), TypeError); + assertThrowsInstanceOf(() => Reflect.apply(constructor, null, []), TypeError); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_6/TypedArray/entries.js b/js/src/tests/ecma_6/TypedArray/entries.js index 72099efd2a..30ee8fd50c 100644 --- a/js/src/tests/ecma_6/TypedArray/entries.js +++ b/js/src/tests/ecma_6/TypedArray/entries.js @@ -30,7 +30,7 @@ for (var constructor of constructors) { if (typeof newGlobal === "function") { var entries = newGlobal()[constructor.name].prototype.entries; assertDeepEq([...entries.call(new constructor(2))], [[0, 0], [1, 0]]); - arr = newGlobal()[constructor.name](2); + arr = new (newGlobal()[constructor.name])(2); assertEq([...constructor.prototype.entries.call(arr)].toString(), "0,0,1,0"); } diff --git a/js/src/tests/ecma_6/TypedArray/keys.js b/js/src/tests/ecma_6/TypedArray/keys.js index 46b476d98f..5494d7eb4d 100644 --- a/js/src/tests/ecma_6/TypedArray/keys.js +++ b/js/src/tests/ecma_6/TypedArray/keys.js @@ -30,7 +30,7 @@ for (var constructor of constructors) { if (typeof newGlobal === "function") { var keys = newGlobal()[constructor.name].prototype.keys; assertDeepEq([...keys.call(new constructor(2))], [0, 1]); - arr = newGlobal()[constructor.name](2); + arr = new (newGlobal()[constructor.name])(2); assertEq([...constructor.prototype.keys.call(arr)].toString(), "0,1"); } diff --git a/js/src/tests/ecma_6/TypedArray/values.js b/js/src/tests/ecma_6/TypedArray/values.js index 2635a6abfc..b66aaec0e7 100644 --- a/js/src/tests/ecma_6/TypedArray/values.js +++ b/js/src/tests/ecma_6/TypedArray/values.js @@ -31,7 +31,7 @@ for (var constructor of constructors) { if (typeof newGlobal === "function") { var values = newGlobal()[constructor.name].prototype.values; assertDeepEq([...values.call(new constructor([42, 36]))], [42, 36]); - arr = newGlobal()[constructor.name]([42, 36]); + arr = new (newGlobal()[constructor.name])([42, 36]); assertEq([...constructor.prototype.values.call(arr)].toString(), "42,36"); } diff --git a/js/src/tests/ecma_7/SIMD/conversions.js b/js/src/tests/ecma_7/SIMD/conversions.js index 92ec237a53..8f30d7428c 100644 --- a/js/src/tests/ecma_7/SIMD/conversions.js +++ b/js/src/tests/ecma_7/SIMD/conversions.js @@ -206,8 +206,8 @@ function testFloat64x2FromFloat32x4Bits() { function testFloat64x2FromInt8x16Bits() { function expected(v) { - var i8 = Int8Array(16); - var f64 = Float64Array(i8.buffer); + var i8 = new Int8Array(16); + var f64 = new Float64Array(i8.buffer); var asArr = simdToArray(v); for (var i = 0; i < 16; i++) i8[i] = asArr[i]; return [f64[0], f64[1]]; @@ -225,8 +225,8 @@ function testFloat64x2FromInt8x16Bits() { function testFloat64x2FromInt16x8Bits() { function expected(v) { - var i16 = Int16Array(8); - var f64 = Float64Array(i16.buffer); + var i16 = new Int16Array(8); + var f64 = new Float64Array(i16.buffer); var asArr = simdToArray(v); for (var i = 0; i < 8; i++) i16[i] = asArr[i]; return [f64[0], f64[1]]; @@ -361,8 +361,8 @@ function testInt32x4FromFloat64x2Bits() { function testInt32x4FromInt8x16Bits() { function expected(v) { - var i8 = Int8Array(16); - var i32 = Int32Array(i8.buffer); + var i8 = new Int8Array(16); + var i32 = new Int32Array(i8.buffer); var asArr = simdToArray(v); for (var i = 0; i < 16; i++) i8[i] = asArr[i]; return [i32[0], i32[1], i32[2], i32[3]]; @@ -380,8 +380,8 @@ function testInt32x4FromInt8x16Bits() { function testInt32x4FromInt16x8Bits() { function expected(v) { - var i16 = Int16Array(8); - var i32 = Int32Array(i16.buffer); + var i16 = new Int16Array(8); + var i32 = new Int32Array(i16.buffer); var asArr = simdToArray(v); for (var i = 0; i < 8; i++) i16[i] = asArr[i]; return [i32[0], i32[1], i32[2], i32[3]]; @@ -398,8 +398,8 @@ function testInt32x4FromInt16x8Bits() { function testInt8x16FromFloat32x4Bits() { function expected(v) { - var f32 = Float32Array(4); - var i8 = Int8Array(f32.buffer); + var f32 = new Float32Array(4); + var i8 = new Int8Array(f32.buffer); var asArr = simdToArray(v); for (var i = 0; i < 4; i++) f32[i] = asArr[i]; return [i8[0], i8[1], i8[2], i8[3], i8[4], i8[5], i8[6], i8[7], @@ -416,8 +416,8 @@ function testInt8x16FromFloat32x4Bits() { function testInt8x16FromFloat64x2Bits() { function expected(v) { - var f64 = Float64Array(2); - var i8 = Int8Array(f64.buffer); + var f64 = new Float64Array(2); + var i8 = new Int8Array(f64.buffer); f64[0] = Float64x2.extractLane(v, 0); f64[1] = Float64x2.extractLane(v, 1); return [i8[0], i8[1], i8[2], i8[3], i8[4], i8[5], i8[6], i8[7], @@ -433,8 +433,8 @@ function testInt8x16FromFloat64x2Bits() { function testInt8x16FromInt16x8Bits() { function expected(v) { - var i16 = Int16Array(8); - var i8 = Int8Array(i16.buffer); + var i16 = new Int16Array(8); + var i8 = new Int8Array(i16.buffer); var asArr = simdToArray(v); for (var i = 0; i < 8; i++) i16[i] = asArr[i]; return [i8[0], i8[1], i8[2], i8[3], i8[4], i8[5], i8[6], i8[7], @@ -450,8 +450,8 @@ function testInt8x16FromInt16x8Bits() { function testInt8x16FromInt32x4Bits() { function expected(v) { - var i32 = Int32Array(4); - var i8 = Int8Array(i32.buffer); + var i32 = new Int32Array(4); + var i8 = new Int8Array(i32.buffer); var asArr = simdToArray(v); for (var i = 0; i < 4; i++) i32[i] = asArr[i]; return [i8[0], i8[1], i8[2], i8[3], i8[4], i8[5], i8[6], i8[7], @@ -467,8 +467,8 @@ function testInt8x16FromInt32x4Bits() { function testInt16x8FromFloat32x4Bits() { function expected(v) { - var f32 = Float32Array(4); - var i16 = Int16Array(f32.buffer); + var f32 = new Float32Array(4); + var i16 = new Int16Array(f32.buffer); var asArr = simdToArray(v); for (var i = 0; i < 4; i++) f32[i] = asArr[i]; return [i16[0], i16[1], i16[2], i16[3], i16[4], i16[5], i16[6], i16[7]]; @@ -484,8 +484,8 @@ function testInt16x8FromFloat32x4Bits() { function testInt16x8FromFloat64x2Bits() { function expected(v) { - var f64 = Float64Array(2); - var i16 = Int16Array(f64.buffer); + var f64 = new Float64Array(2); + var i16 = new Int16Array(f64.buffer); f64[0] = Float64x2.extractLane(v, 0); f64[1] = Float64x2.extractLane(v, 1); return [i16[0], i16[1], i16[2], i16[3], i16[4], i16[5], i16[6], i16[7]]; @@ -501,8 +501,8 @@ function testInt16x8FromFloat64x2Bits() { function testInt16x8FromInt8x16Bits() { function expected(v) { - var i8 = Int8Array(16); - var i16 = Int16Array(i8.buffer); + var i8 = new Int8Array(16); + var i16 = new Int16Array(i8.buffer); var asArr = simdToArray(v); for (var i = 0; i < 16; i++) i8[i] = asArr[i]; return [i16[0], i16[1], i16[2], i16[3], i16[4], i16[5], i16[6], i16[7]]; @@ -518,8 +518,8 @@ function testInt16x8FromInt8x16Bits() { function testInt16x8FromInt32x4Bits() { function expected(v) { - var i32 = Int32Array(4); - var i16 = Int16Array(i32.buffer); + var i32 = new Int32Array(4); + var i16 = new Int16Array(i32.buffer); var asArr = simdToArray(v); for (var i = 0; i < 4; i++) i32[i] = asArr[i]; return [i16[0], i16[1], i16[2], i16[3], i16[4], i16[5], i16[6], i16[7]]; diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 9cbbd78a6e..4dd73bd16a 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -459,7 +459,7 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); - if (!WarnIfNotConstructing(cx, args, "ArrayBuffer")) + if (!ThrowIfNotConstructing(cx, args, "ArrayBuffer")) return false; int32_t nbytes = 0; @@ -1041,17 +1041,19 @@ ArrayBufferObject::addView(JSContext* cx, JSObject* viewArg) static size_t VIEW_LIST_MAX_LENGTH = 500; bool -InnerViewTable::addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewObject* view) +InnerViewTable::addView(JSContext* cx, ArrayBufferObject* buffer, ArrayBufferViewObject* view) { // ArrayBufferObject entries are only added when there are multiple views. - MOZ_ASSERT(obj->firstView()); + MOZ_ASSERT(buffer->firstView()); - if (!map.initialized() && !map.init()) + if (!map.initialized() && !map.init()) { + ReportOutOfMemory(cx); return false; + } - Map::AddPtr p = map.lookupForAdd(obj); + Map::AddPtr p = map.lookupForAdd(buffer); - MOZ_ASSERT(!gc::IsInsideNursery(obj)); + MOZ_ASSERT(!gc::IsInsideNursery(buffer)); bool addToNursery = nurseryKeysValid && gc::IsInsideNursery(view); if (p) { @@ -1066,8 +1068,10 @@ InnerViewTable::addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewOb nurseryKeysValid = false; } else { for (size_t i = 0; i < views.length(); i++) { - if (gc::IsInsideNursery(views[i])) + if (gc::IsInsideNursery(views[i])) { addToNursery = false; + break; + } } } } @@ -1077,33 +1081,35 @@ InnerViewTable::addView(JSContext* cx, ArrayBufferObject* obj, ArrayBufferViewOb return false; } } else { - if (!map.add(p, obj, ViewVector())) + if (!map.add(p, buffer, ViewVector())) { + ReportOutOfMemory(cx); return false; - JS_ALWAYS_TRUE(p->value().append(view)); + } + MOZ_ALWAYS_TRUE(p->value().append(view)); } - if (addToNursery && !nurseryKeys.append(obj)) + if (addToNursery && !nurseryKeys.append(buffer)) nurseryKeysValid = false; return true; } InnerViewTable::ViewVector* -InnerViewTable::maybeViewsUnbarriered(ArrayBufferObject* obj) +InnerViewTable::maybeViewsUnbarriered(ArrayBufferObject* buffer) { if (!map.initialized()) return nullptr; - Map::Ptr p = map.lookup(obj); + Map::Ptr p = map.lookup(buffer); if (p) return &p->value(); return nullptr; } void -InnerViewTable::removeViews(ArrayBufferObject* obj) +InnerViewTable::removeViews(ArrayBufferObject* buffer) { - Map::Ptr p = map.lookup(obj); + Map::Ptr p = map.lookup(buffer); MOZ_ASSERT(p); map.remove(p); @@ -1567,7 +1573,7 @@ js::GetArrayBufferLengthAndData(JSObject* obj, uint32_t* length, uint8_t** data) } JSObject* -js::InitArrayBufferClass(JSContext *cx, HandleObject obj) +js::InitArrayBufferClass(JSContext* cx, HandleObject obj) { Rooted global(cx, cx->compartment()->maybeGlobal()); if (global->isStandardClassResolved(JSProto_ArrayBuffer)) diff --git a/js/src/vm/NativeObject-inl.h b/js/src/vm/NativeObject-inl.h index 5b51fb1275..5b96cba9d9 100644 --- a/js/src/vm/NativeObject-inl.h +++ b/js/src/vm/NativeObject-inl.h @@ -583,15 +583,6 @@ LookupPropertyInline(ExclusiveContext* cx, return true; } -inline bool -WarnIfNotConstructing(JSContext* cx, const CallArgs& args, const char* builtinName) -{ - if (args.isConstructing()) - return true; - return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, nullptr, - JSMSG_BUILTIN_CTOR_NO_NEW, builtinName); -} - inline bool ThrowIfNotConstructing(JSContext *cx, const CallArgs &args, const char *builtinName) { diff --git a/js/src/vm/TypeInference.cpp b/js/src/vm/TypeInference.cpp index acbca1960b..932abbe95a 100644 --- a/js/src/vm/TypeInference.cpp +++ b/js/src/vm/TypeInference.cpp @@ -4417,7 +4417,8 @@ TypeZone::beginSweep(FreeOp* fop, bool releaseTypes, AutoClearTypeInferenceState if (output.isValid()) { JSScript* script = output.script(); if (IsAboutToBeFinalizedUnbarriered(&script)) { - script->ionScript()->recompileInfoRef() = RecompileInfo(); + if (script->hasIonScript()) + script->ionScript()->recompileInfoRef() = RecompileInfo(); output.invalidate(); } else { CompilerOutput newOutput(script); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 8a63c8621d..3fcd783fbb 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -412,7 +412,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject { CallArgs args = CallArgsFromVp(argc, vp); - if (!WarnIfNotConstructing(cx, args, "typed array")) + if (!ThrowIfNotConstructing(cx, args, "typed array")) return false; JSObject* obj = create(cx, args); diff --git a/js/src/vm/UnboxedObject.cpp b/js/src/vm/UnboxedObject.cpp index ca955f1942..d8c3743825 100644 --- a/js/src/vm/UnboxedObject.cpp +++ b/js/src/vm/UnboxedObject.cpp @@ -893,17 +893,8 @@ UnboxedPlainObject::obj_watch(JSContext* cx, HandleObject obj, HandleId id, Hand UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& properties, bool enumerableOnly) { - UnboxedExpandoObject* expando = obj->as().maybeExpando(); - - // Add dense elements in the expando first, for consistency with plain objects. - if (expando) { - for (size_t i = 0; i < expando->getDenseInitializedLength(); i++) { - if (!expando->getDenseElement(i).isMagic(JS_ELEMENTS_HOLE)) { - if (!properties.append(INT_TO_JSID(i))) - return false; - } - } - } + // Ignore expando properties here, they are special-cased by the property + // enumeration code. const UnboxedLayout::PropertyVector& unboxed = obj->as().layout().properties(); for (size_t i = 0; i < unboxed.length(); i++) { @@ -911,19 +902,6 @@ UnboxedPlainObject::obj_enumerate(JSContext* cx, HandleObject obj, AutoIdVector& return false; } - if (expando) { - Vector ids(cx); - for (Shape::Range r(expando->lastProperty()); !r.empty(); r.popFront()) { - if (enumerableOnly && !r.front().enumerable()) - continue; - if (!ids.append(r.front().propid())) - return false; - } - ::Reverse(ids.begin(), ids.end()); - if (!properties.append(ids.begin(), ids.length())) - return false; - } - return true; } diff --git a/js/src/vm/make_opcode_doc.py b/js/src/vm/make_opcode_doc.py index 6c2647e60c..8e3be00b3f 100755 --- a/js/src/vm/make_opcode_doc.py +++ b/js/src/vm/make_opcode_doc.py @@ -292,24 +292,24 @@ def format_flags(flags): def print_opcode(opcode): names_template = '{name} [-{nuses}, +{ndefs}]{flags}' + opcodes = sorted([opcode] + opcode.group, + key=lambda opcode: opcode.name) names = map(lambda code: names_template.format(name=escape(code.name), nuses=override(code.nuses, opcode.nuses_override), ndefs=override(code.ndefs, opcode.ndefs_override), flags=format_flags(code.flags)), - sorted([opcode] + opcode.group, - key=lambda opcode: opcode.name)) - if len(opcode.group) == 0: + opcodes) + if len(opcodes) == 1: values = ['{value} (0x{value:02x})'.format(value=opcode.value)] else: values_template = '{name}: {value} (0x{value:02x})' values = map(lambda code: values_template.format(name=escape(code.name), value=code.value), - sorted([opcode] + opcode.group, - key=lambda opcode: opcode.name)) + opcodes) - print("""
{names}
+ print("""
{names}
@@ -323,7 +323,8 @@ def print_opcode(opcode): {desc} -""".format(names='
'.join(names), +""".format(id=opcodes[0].name, + names='
'.join(names), values='
'.join(values), operands=escape(opcode.operands) or " ", length=escape(override(opcode.length, diff --git a/js/xpconnect/crashtests/crashtests.list b/js/xpconnect/crashtests/crashtests.list index 1c8c52fc70..7325e26016 100644 --- a/js/xpconnect/crashtests/crashtests.list +++ b/js/xpconnect/crashtests/crashtests.list @@ -50,7 +50,7 @@ load 797583.html load 806751.html load 833856.html load 851418.html -load 854604.html load 854139.html +load 854604.html pref(dom.use_xbl_scopes_for_remote_xul,true) load 898939.html pref(security.fileuri.strict_origin_policy,false) load 938297.html diff --git a/js/xpconnect/loader/XPCOMUtils.jsm b/js/xpconnect/loader/XPCOMUtils.jsm index 8818c9c968..d339ffa292 100644 --- a/js/xpconnect/loader/XPCOMUtils.jsm +++ b/js/xpconnect/loader/XPCOMUtils.jsm @@ -144,7 +144,9 @@ this.XPCOMUtils = { countRef.value = _interfaces.length; return _interfaces; }, - getScriptableHelper: function XPCU_getScriptableHelper() null, + getScriptableHelper: function XPCU_getScriptableHelper() { + return null; + }, contractID: classInfo.contractID, classDescription: classInfo.classDescription, classID: classInfo.classID, diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index ef2394c289..5aae0c2fbc 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -1022,6 +1022,7 @@ xpc::CreateSandboxObject(JSContext* cx, MutableHandleValue vp, nsISupports* prin priv->allowWaivers = options.allowWaivers; priv->writeToGlobalPrototype = options.writeToGlobalPrototype; priv->isWebExtensionContentScript = options.isWebExtensionContentScript; + priv->waiveInterposition = options.waiveInterposition; // Set up the wantXrays flag, which indicates whether xrays are desired even // for same-origin access. @@ -1502,6 +1503,7 @@ SandboxOptions::Parse() ParseBoolean("wantComponents", &wantComponents) && ParseBoolean("wantExportHelpers", &wantExportHelpers) && ParseBoolean("isWebExtensionContentScript", &isWebExtensionContentScript) && + ParseBoolean("waiveInterposition", &waiveInterposition) && ParseString("sandboxName", sandboxName) && ParseObject("sameZoneAs", &sameZoneAs) && ParseBoolean("freshZone", &freshZone) && diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 395586f449..9e80782a85 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -190,6 +190,7 @@ CompartmentPrivate::CompartmentPrivate(JSCompartment* c) , writeToGlobalPrototype(false) , skipWriteToGlobalPrototype(false) , isWebExtensionContentScript(false) + , waiveInterposition(false) , universalXPConnectEnabled(false) , forcePermissiveCOWs(false) , scriptability(c) diff --git a/js/xpconnect/src/XPCWrappedNativeScope.cpp b/js/xpconnect/src/XPCWrappedNativeScope.cpp index 40862e589e..4aa51aeefc 100644 --- a/js/xpconnect/src/XPCWrappedNativeScope.cpp +++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp @@ -139,9 +139,11 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext* cx, JSAddonId* addonId = JS::AddonIdOfObject(aGlobal); if (gInterpositionMap) { bool isSystem = nsContentUtils::IsSystemPrincipal(principal); - if (InterpositionMap::Ptr p = gInterpositionMap->lookup(addonId)) { + bool waiveInterposition = priv->waiveInterposition; + InterpositionMap::Ptr interposition = gInterpositionMap->lookup(addonId); + if (!waiveInterposition && interposition) { MOZ_RELEASE_ASSERT(isSystem); - mInterposition = p->value(); + mInterposition = interposition->value(); } // We also want multiprocessCompatible add-ons to have a default interposition. if (!mInterposition && addonId && isSystem) { diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 5b262029a2..689eeee664 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3474,6 +3474,7 @@ public: , wantComponents(true) , wantExportHelpers(false) , isWebExtensionContentScript(false) + , waiveInterposition(false) , proto(cx) , addonId(cx) , writeToGlobalPrototype(false) @@ -3491,6 +3492,7 @@ public: bool wantComponents; bool wantExportHelpers; bool isWebExtensionContentScript; + bool waiveInterposition; JS::RootedObject proto; nsCString sandboxName; JS::RootedString addonId; @@ -3731,6 +3733,11 @@ public: // various bits of special compatibility behavior. bool isWebExtensionContentScript; + // Even if an add-on needs interposition, it does not necessary need it + // for every scope. If this flag is set we waive interposition for this + // scope. + bool waiveInterposition; + // This is only ever set during mochitest runs when enablePrivilege is called. // It's intended as a temporary stopgap measure until we can finish ripping out // enablePrivilege. Once set, this value is never unset (i.e., it doesn't follow diff --git a/js/xpconnect/tests/chrome/test_sandboxImport.xul b/js/xpconnect/tests/chrome/test_sandboxImport.xul index 8020c5bc3b..eb2f76e846 100644 --- a/js/xpconnect/tests/chrome/test_sandboxImport.xul +++ b/js/xpconnect/tests/chrome/test_sandboxImport.xul @@ -24,8 +24,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=533596 } var sandbox = new Components.utils.Sandbox("about:blank"); - sandbox.importFunction(function() "PASS", "foo"); - sandbox.importFunction(function bar() "PASS"); + sandbox.importFunction(function() { return "PASS"; }, "foo"); + sandbox.importFunction(function bar() { return "PASS"; }); sandbox.importFunction(checkWrapped); is(Components.utils.evalInSandbox("foo()", sandbox), "PASS", "importFunction works"); is(Components.utils.evalInSandbox("bar()", sandbox), "PASS", "importFunction works"); diff --git a/js/xpconnect/tests/mochitest/file_bug802557.html b/js/xpconnect/tests/mochitest/file_bug802557.html index 02b4aedce7..39f952bc5b 100644 --- a/js/xpconnect/tests/mochitest/file_bug802557.html +++ b/js/xpconnect/tests/mochitest/file_bug802557.html @@ -6,7 +6,9 @@ var gTS = window.location.toString; var gGHR = Object.getOwnPropertyDescriptor(window.location, 'href').get; function getTests(fromOuter) { - function loc() fromOuter ? window.location : location; + function loc() { + return fromOuter ? window.location : location; + } return { getLocationImplicit: function() { return loc() + ""; @@ -35,14 +37,14 @@ function getTests(fromOuter) { function mungeNames(obj, suffix) { var rv = {}; Object.getOwnPropertyNames(obj) - .forEach(function (name) rv[name + suffix] = obj[name]); + .forEach(name => rv[name + suffix] = obj[name]); return rv; } function mergeObjects(a, b) { var rv = {}; - Object.getOwnPropertyNames(a).forEach(function(name) rv[name] = a[name]); - Object.getOwnPropertyNames(b).forEach(function(name) rv[name] = b[name]); + Object.getOwnPropertyNames(a).forEach(name => rv[name] = a[name]); + Object.getOwnPropertyNames(b).forEach(name => rv[name] = b[name]); return rv; } diff --git a/js/xpconnect/tests/mochitest/test_bug478438.html b/js/xpconnect/tests/mochitest/test_bug478438.html index 2fd01ba4fe..76faa706c6 100644 --- a/js/xpconnect/tests/mochitest/test_bug478438.html +++ b/js/xpconnect/tests/mochitest/test_bug478438.html @@ -24,19 +24,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=478438 catch (e) { onAllow.opposite("unable " + infinitive, ": " + e) } } - testOne(function() iwin.focus, pass, + testOne(() => iwin.focus, pass, "to resolve/get allAccess property iwin.focus"); - testOne(function() iwin.focus(), pass, + testOne(() => iwin.focus(), pass, "to call allAccess method iwin.focus"); - testOne(function() iwin.alert, fail, + testOne(() => iwin.alert, fail, "to resolve/get restricted property iwin.alert"); - testOne(function() iwin.alert(), fail, + testOne(() => iwin.alert(), fail, "to call restricted method iwin.alert"); - testOne(function() iwin.location.toString(), fail, + testOne(() => iwin.location.toString(), fail, "to call restricted method iwin.location.toString"); testOne(function() { iwin.location = "http://example.org" }, pass, diff --git a/js/xpconnect/tests/unit/test_bug1150771.js b/js/xpconnect/tests/unit/test_bug1150771.js index 54796a9df5..8387b2cdf0 100644 --- a/js/xpconnect/tests/unit/test_bug1150771.js +++ b/js/xpconnect/tests/unit/test_bug1150771.js @@ -2,7 +2,7 @@ function run_test() { let Cu = Components.utils; let sandbox1 = new Cu.Sandbox(null); let sandbox2 = new Cu.Sandbox(null); -let arg = Cu.evalInSandbox('({ buf: ArrayBuffer(2) })', sandbox1); +let arg = Cu.evalInSandbox('({ buf: new ArrayBuffer(2) })', sandbox1); let clonedArg = Cu.cloneInto(Cu.waiveXrays(arg), sandbox2); do_check_eq(typeof Cu.waiveXrays(clonedArg).buf, "object"); diff --git a/js/xpconnect/tests/unit/test_bug809652.js b/js/xpconnect/tests/unit/test_bug809652.js index 30e985f203..66dd6d5504 100644 --- a/js/xpconnect/tests/unit/test_bug809652.js +++ b/js/xpconnect/tests/unit/test_bug809652.js @@ -21,7 +21,7 @@ function run_test() { for (var i = 0; i < 8; ++i) new Uint8Array(sb.ab)[i] = i * 10; sb.ta = []; - TypedArrays.forEach(function(f) sb.ta.push(new f(sb.ab))); + TypedArrays.forEach(f => sb.ta.push(new f(sb.ab))); sb.dv = new DataView(sb.ab); /* Things that should throw. */ diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 22861595e5..820c945aba 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -295,9 +295,9 @@ load 470851-1.xhtml load 471594-1.xhtml asserts-if(Android,2) load 473042.xhtml # bug 1034369 (may also cause a few assertions to be registered on the next test) asserts(0-5) load 474075.html # bug 847368 -load 479114-1.html load 477333-1.xhtml load 477731-1.html +load 479114-1.html load 479360-1.xhtml load 480686-1.html load 481806-1.html @@ -436,9 +436,9 @@ load 836990-1.html load 840480.html load 847242.html pref(layers.progressive-paint,false) pref(layers.low-precision-buffer,false) load 852293.html -load 860579-1.html pref(layers.force-active,true) load 859526-1.html pref(layers.force-active,true) load 859630-1.html +load 860579-1.html load 866588.html load 876092.html load 876221.html @@ -447,18 +447,18 @@ asserts-if(Android,2) asserts-if(!Android,4-6) load 898913.html # bug 847368 pref(layers.acceleration.disabled,true) pref(layers.force-active,true) load 919434.html pref(layout.css.sticky.enabled,true) load 926728.html load 930381.html -load 931464.html -load 936988-1.html load 931450.html load 931460-1.html +load 931464.html load 935765-1.html +load 936988-1.html load 942690.html load 973390-1.html load 1001237.html load 1043163-1.html load 1061028.html -load 1116104.html load 1107508-1.html +load 1116104.html load 1127198-1.html load 1140198.html load 1297835.html diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 19ee6b6cf3..b62d39f533 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -164,6 +164,7 @@ #include "Layers.h" #include "LayerTreeInvalidation.h" #include "mozilla/css/ImageLoader.h" +#include "mozilla/dom/DocumentTimeline.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include "nsCanvasFrame.h" @@ -974,6 +975,9 @@ PresShell::Init(nsIDocument* aDocument, animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver()); } + mDocument->Timeline()->NotifyRefreshDriverCreated(GetPresContext()-> + RefreshDriver()); + // Get our activeness from the docShell. QueryIsActive(); @@ -1270,6 +1274,8 @@ PresShell::Destroy() if (mDocument->HasAnimationController()) { mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd); } + + mDocument->Timeline()->NotifyRefreshDriverDestroying(rd); } if (mPresContext) { @@ -4571,8 +4577,8 @@ PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument, void PresShell::StyleRuleChanged(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, - nsIStyleRule* aOldStyleRule, - nsIStyleRule* aNewStyleRule) + mozilla::css::Rule* aOldStyleRule, + mozilla::css::Rule* aNewStyleRule) { RecordStyleSheetChange(aStyleSheet); } @@ -4580,7 +4586,7 @@ PresShell::StyleRuleChanged(nsIDocument *aDocument, void PresShell::StyleRuleAdded(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) + mozilla::css::Rule* aStyleRule) { RecordStyleSheetChange(aStyleSheet); } @@ -4588,7 +4594,7 @@ PresShell::StyleRuleAdded(nsIDocument *aDocument, void PresShell::StyleRuleRemoved(nsIDocument *aDocument, nsIStyleSheet* aStyleSheet, - nsIStyleRule* aStyleRule) + mozilla::css::Rule* aStyleRule) { RecordStyleSheetChange(aStyleSheet); } diff --git a/layout/base/nsStyleSheetService.cpp b/layout/base/nsStyleSheetService.cpp index 404ea74da5..00bea0a02c 100644 --- a/layout/base/nsStyleSheetService.cpp +++ b/layout/base/nsStyleSheetService.cpp @@ -180,17 +180,32 @@ nsresult nsStyleSheetService::LoadAndRegisterSheetInternal(nsIURI *aSheetURI, uint32_t aSheetType) { - NS_ENSURE_ARG(aSheetType == AGENT_SHEET || - aSheetType == USER_SHEET || - aSheetType == AUTHOR_SHEET); NS_ENSURE_ARG_POINTER(aSheetURI); + css::SheetParsingMode parsingMode; + switch (aSheetType) { + case AGENT_SHEET: + parsingMode = css::eAgentSheetFeatures; + break; + + case USER_SHEET: + parsingMode = css::eUserSheetFeatures; + break; + + case AUTHOR_SHEET: + parsingMode = css::eAuthorSheetFeatures; + break; + + default: + NS_WARNING("invalid sheet type argument"); + return NS_ERROR_INVALID_ARG; + } + RefPtr loader = new css::Loader(); RefPtr sheet; - // Allow UA sheets, but not user sheets, to use unsafe rules - nsresult rv = loader->LoadSheetSync(aSheetURI, aSheetType == AGENT_SHEET, - true, getter_AddRefs(sheet)); + nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, + getter_AddRefs(sheet)); NS_ENSURE_SUCCESS(rv, rv); if (!mSheets[aSheetType].AppendObject(sheet)) { @@ -219,18 +234,32 @@ NS_IMETHODIMP nsStyleSheetService::PreloadSheet(nsIURI *aSheetURI, uint32_t aSheetType, nsIDOMStyleSheet **aSheet) { - NS_ENSURE_ARG(aSheetType == AGENT_SHEET || - aSheetType == USER_SHEET || - aSheetType == AUTHOR_SHEET); - NS_ENSURE_ARG_POINTER(aSheetURI); NS_PRECONDITION(aSheet, "Null out param"); + NS_ENSURE_ARG_POINTER(aSheetURI); + css::SheetParsingMode parsingMode; + switch (aSheetType) { + case AGENT_SHEET: + parsingMode = css::eAgentSheetFeatures; + break; + + case USER_SHEET: + parsingMode = css::eUserSheetFeatures; + break; + + case AUTHOR_SHEET: + parsingMode = css::eAuthorSheetFeatures; + break; + + default: + NS_WARNING("invalid sheet type argument"); + return NS_ERROR_INVALID_ARG; + } RefPtr loader = new css::Loader(); - // Allow UA sheets, but not user sheets, to use unsafe rules RefPtr sheet; - nsresult rv = loader->LoadSheetSync(aSheetURI, aSheetType == AGENT_SHEET, - true, getter_AddRefs(sheet)); + nsresult rv = loader->LoadSheetSync(aSheetURI, parsingMode, true, + getter_AddRefs(sheet)); NS_ENSURE_SUCCESS(rv, rv); sheet.forget(aSheet); return NS_OK; diff --git a/layout/forms/crashtests/crashtests.list b/layout/forms/crashtests/crashtests.list index 8241012edd..aa6b97775d 100644 --- a/layout/forms/crashtests/crashtests.list +++ b/layout/forms/crashtests/crashtests.list @@ -27,7 +27,7 @@ asserts(4-10) load 378413-1.xhtml # bug 424225, bug 402850? load 380116-1.xhtml load 382212-1.xhtml load 382610-1.html -load 383887-1.html # bug 576434 +load 383887-1.html load 386554-1.html load 388374-1.xhtml load 388374-2.html @@ -44,15 +44,15 @@ load 455451-1.html load 457537-1.html load 457537-2.html load 478219-1.xhtml +skip-if(B2G) load 498698-1.html # bug 833371 load 513113-1.html load 538062-1.xhtml load 570624-1.html -skip-if(B2G) load 498698-1.html # bug 833371 asserts(1) load 578604-1.html # bug 584564 asserts(4-7) load 590302-1.xhtml # bug 584564 load 626014.xhtml load 639733.xhtml -asserts(0-1) load 669767.html +load 669767.html load 682684.xhtml load 865602.html load 944198.html diff --git a/layout/generic/WritingModes.h b/layout/generic/WritingModes.h index 36abe007dc..718cdedacd 100644 --- a/layout/generic/WritingModes.h +++ b/layout/generic/WritingModes.h @@ -24,9 +24,12 @@ // (In some cases, there are internal (private) methods that don't do this; // such methods should only be used by other methods that have already checked // the writing modes.) +// The check ignores the eSidewaysMask bit of writing mode, because this does +// not affect the interpretation of logical coordinates. #define CHECK_WRITING_MODE(param) \ - NS_ASSERTION(param == GetWritingMode(), "writing-mode mismatch") + NS_ASSERTION(param.IgnoreSideways() == GetWritingMode().IgnoreSideways(), \ + "writing-mode mismatch") namespace mozilla { @@ -259,6 +262,13 @@ public: */ bool IsSideways() const { return !!(mWritingMode & eSidewaysMask); } +#ifdef DEBUG // Used by CHECK_WRITING_MODE to compare modes without regard + // for the eSidewaysMask flag. + WritingMode IgnoreSideways() const { + return WritingMode(mWritingMode & ~eSidewaysMask); + } +#endif + static mozilla::PhysicalAxis PhysicalAxisForLogicalAxis( uint8_t aWritingModeValue, LogicalAxis aAxis) @@ -567,6 +577,19 @@ public: uint8_t GetBits() const { return mWritingMode; } + const char* DebugString() const { + return IsVertical() + ? IsVerticalLR() + ? IsBidiLTR() + ? IsSideways() ? "sw-lr-ltr" : "v-lr-ltr" + : IsSideways() ? "sw-lr-rtl" : "v-lr-rtl" + : IsBidiLTR() + ? IsSideways() ? "sw-rl-ltr" : "v-rl-ltr" + : IsSideways() ? "sw-rl-rtl" : "v-rl-rtl" + : IsBidiLTR() ? "h-ltr" : "h-rtl" + ; + } + private: friend class LogicalPoint; friend class LogicalSize; @@ -1954,4 +1977,28 @@ nsStylePosition::MaxBSizeDependsOnContainer(mozilla::WritingMode aWM) const : MaxHeightDependsOnContainer(); } +inline uint8_t +nsStyleDisplay::PhysicalFloats(mozilla::WritingMode aWM) const +{ + if (mFloats == NS_STYLE_FLOAT_INLINE_START) { + return aWM.IsBidiLTR() ? NS_STYLE_FLOAT_LEFT : NS_STYLE_FLOAT_RIGHT; + } + if (mFloats == NS_STYLE_FLOAT_INLINE_END) { + return aWM.IsBidiLTR() ? NS_STYLE_FLOAT_RIGHT : NS_STYLE_FLOAT_LEFT; + } + return mFloats; +} + +inline uint8_t +nsStyleDisplay::PhysicalBreakType(mozilla::WritingMode aWM) const +{ + if (mBreakType == NS_STYLE_CLEAR_INLINE_START) { + return aWM.IsBidiLTR() ? NS_STYLE_CLEAR_LEFT : NS_STYLE_CLEAR_RIGHT; + } + if (mBreakType == NS_STYLE_CLEAR_INLINE_END) { + return aWM.IsBidiLTR() ? NS_STYLE_CLEAR_RIGHT : NS_STYLE_CLEAR_LEFT; + } + return mBreakType; +} + #endif // WritingModes_h_ diff --git a/layout/generic/crashtests/1169420-1.html b/layout/generic/crashtests/1169420-1.html new file mode 100644 index 0000000000..73b8f8d64e --- /dev/null +++ b/layout/generic/crashtests/1169420-1.html @@ -0,0 +1,8 @@ + + + +
+
+
+ + diff --git a/layout/generic/crashtests/1169420-2.html b/layout/generic/crashtests/1169420-2.html new file mode 100644 index 0000000000..e096ed7f92 --- /dev/null +++ b/layout/generic/crashtests/1169420-2.html @@ -0,0 +1,8 @@ + + + +
+
+
+ + diff --git a/layout/generic/crashtests/1183431.html b/layout/generic/crashtests/1183431.html new file mode 100644 index 0000000000..e1d4c87c0c --- /dev/null +++ b/layout/generic/crashtests/1183431.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/generic/crashtests/1221112-1.html b/layout/generic/crashtests/1221112-1.html new file mode 100644 index 0000000000..24e60a37bf --- /dev/null +++ b/layout/generic/crashtests/1221112-1.html @@ -0,0 +1,32 @@ + + + + + + +
+
ItemWithOrderSet + diff --git a/layout/generic/crashtests/1221112-2.html b/layout/generic/crashtests/1221112-2.html new file mode 100644 index 0000000000..2ae372a9d1 --- /dev/null +++ b/layout/generic/crashtests/1221112-2.html @@ -0,0 +1,27 @@ + + + + + + +
+
NormalFlexItem + diff --git a/layout/generic/crashtests/1221874-1.html b/layout/generic/crashtests/1221874-1.html new file mode 100644 index 0000000000..85d5bab3c0 --- /dev/null +++ b/layout/generic/crashtests/1221874-1.html @@ -0,0 +1,16 @@ + + + + + + +Hello + diff --git a/layout/generic/crashtests/812879.html b/layout/generic/crashtests/812879-1.html similarity index 100% rename from layout/generic/crashtests/812879.html rename to layout/generic/crashtests/812879-1.html diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index 2f9f335460..cd038d4774 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -161,9 +161,9 @@ load 393956-2.html load 393956-3.html load 393956-4.html load 394237-1.html -load 394820-1.html load 394818-1.html load 394818-2.html +load 394820-1.html load 395316-1.html load 395450-1.xhtml load 397007-1.html @@ -507,23 +507,23 @@ load 807565-1.html load 807565-2.html load 810303.html load 810726.html -load 812879.html +load 812822-1.html +load 812879-1.html load 812879-2.html +load 812893.html load 814995.html load 822910.xhtml +load 824297-1.html load 825810-1.html load 825810-2.html -load 827076.html -load 840818.html -load 812822-1.html -load 812893.html -load 824297-1.html load 826483-1.html load 826532-1.html +load 827076.html load 827168-1.html load 836895.html load 837007.xhtml load 840787.html +load 840818.html load 842132-1.html load 842166.html load 844529-1.html @@ -590,6 +590,12 @@ load 1146107.html load 1146114.html load 1156222.html load 1157011.html +load 1169420-1.html +load 1169420-2.html +load 1183431.html +load 1221112-1.html +load 1221112-2.html +load 1221874-1.html load 1222783.xhtml load details-display-none-summary-1.html load details-display-none-summary-2.html diff --git a/layout/generic/crashtests/first-letter-638937.html b/layout/generic/crashtests/first-letter-638937-1.html similarity index 100% rename from layout/generic/crashtests/first-letter-638937.html rename to layout/generic/crashtests/first-letter-638937-1.html diff --git a/layout/generic/nsBlockFrame.cpp b/layout/generic/nsBlockFrame.cpp index a19d2f12f1..7e09ec1e1a 100644 --- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -6793,8 +6793,11 @@ nsBlockFrame::Init(nsIContent* aContent, // If the box is a block container, then it establishes a new block // formatting context. // (http://dev.w3.org/csswg/css-writing-modes/#block-flow) - if (GetParent() && StyleVisibility()->mWritingMode != - GetParent()->StyleVisibility()->mWritingMode) { + // If the box has contain: paint (or contain: strict), then it should also + // establish a formatting context. + if ((GetParent() && StyleVisibility()->mWritingMode != + GetParent()->StyleVisibility()->mWritingMode) || + StyleDisplay()->IsContainPaint()) { AddStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT); } diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 1ec53ba441..ef3a2123f5 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -1014,8 +1014,11 @@ IsOrderLEQWithDOMFallback(nsIFrame* aFrame1, // recognize generated content as being an actual sibling of other nodes. // We know where ::before and ::after nodes *effectively* insert in the DOM // tree, though (at the beginning & end), so we can just special-case them. - nsIAtom* pseudo1 = aFrame1->StyleContext()->GetPseudo(); - nsIAtom* pseudo2 = aFrame2->StyleContext()->GetPseudo(); + nsIAtom* pseudo1 = + nsPlaceholderFrame::GetRealFrameFor(aFrame1)->StyleContext()->GetPseudo(); + nsIAtom* pseudo2 = + nsPlaceholderFrame::GetRealFrameFor(aFrame2)->StyleContext()->GetPseudo(); + if (pseudo1 == nsCSSPseudoElements::before || pseudo2 == nsCSSPseudoElements::after) { // frame1 is ::before and/or frame2 is ::after => frame1 is LEQ frame2. diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index c8fd417ddc..1604c9b57f 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -5780,10 +5780,7 @@ nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) con mozilla::WritingMode wm = GetWritingMode(); if (wm.IsVertical() || !wm.IsBidiLTR()) { - aTo += nsPrintfCString(" wm=%s-%s: logical size={%d,%d}", - wm.IsVertical() ? wm.IsVerticalLR() ? "vlr" : "vrl" - : "htb", - wm.IsBidiLTR() ? "ltr" : "rtl", + aTo += nsPrintfCString(" wm=%s: logical size={%d,%d}", wm.DebugString(), ISize(), BSize()); } @@ -5793,12 +5790,9 @@ nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix, uint32_t aFlags) con if (pWM.IsVertical() || !pWM.IsBidiLTR()) { nsSize containerSize = parent->mRect.Size(); LogicalRect lr(pWM, mRect, containerSize); - aTo += nsPrintfCString(" parent wm=%s-%s, cs={%d,%d}, " + aTo += nsPrintfCString(" parent wm=%s, cs={%d,%d}, " " logicalRect={%d,%d,%d,%d}", - pWM.IsVertical() ? pWM.IsVerticalLR() - ? "vlr" : "vrl" - : "htb", - wm.IsBidiLTR() ? "ltr" : "rtl", + pWM.DebugString(), containerSize.width, containerSize.height, lr.IStart(pWM), lr.BStart(pWM), lr.ISize(pWM), lr.BSize(pWM)); diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 559951cae7..5e99cb189b 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -992,9 +992,9 @@ nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame, } nsIFrame* -nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, - nscoord& aCBIStartEdge, - nscoord& aCBISize) +nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, + nscoord& aCBIStartEdge, + LogicalSize& aCBSize) { aFrame = aFrame->GetContainingBlock(); NS_ASSERTION(aFrame != frame, "How did that happen?"); @@ -1018,7 +1018,7 @@ nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, WritingMode stateWM = state->GetWritingMode(); aCBIStartEdge = state->ComputedLogicalBorderPadding().ConvertTo(wm, stateWM).IStart(wm); - aCBISize = state->ComputedSize(wm).ISize(wm); + aCBSize = state->ComputedSize(wm); } else { /* Didn't find a reflow state for aFrame. Just compute the information we want, on the assumption that aFrame already knows its size. This really @@ -1027,7 +1027,7 @@ nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame, "aFrame shouldn't be in reflow; we'll lie if it is"); LogicalMargin borderPadding = aFrame->GetLogicalUsedBorderAndPadding(wm); aCBIStartEdge = borderPadding.IStart(wm); - aCBISize = aFrame->ISize(wm) - borderPadding.IStartEnd(wm); + aCBSize = aFrame->GetLogicalSize(wm) - borderPadding.Size(wm); } return aFrame; @@ -1077,71 +1077,81 @@ GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameTyp } /** - * aInsideBoxSizing returns the part of the horizontal padding, border, - * and margin that goes inside the edge given by box-sizing; + * aInsideBoxSizing returns the part of the padding, border, and margin + * in the aAxis dimension that goes inside the edge given by box-sizing; * aOutsideBoxSizing returns the rest. */ void -nsHTMLReflowState::CalculateInlineBorderPaddingMargin( - nscoord aContainingBlockISize, +nsHTMLReflowState::CalculateBorderPaddingMargin( + LogicalAxis aAxis, + nscoord aContainingBlockSize, nscoord* aInsideBoxSizing, nscoord* aOutsideBoxSizing) { WritingMode wm = GetWritingMode(); - mozilla::css::Side inlineStart = wm.PhysicalSide(eLogicalSideIStart); - mozilla::css::Side inlineEnd = wm.PhysicalSide(eLogicalSideIEnd); + mozilla::css::Side startSide = + wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeStart)); + mozilla::css::Side endSide = + wm.PhysicalSide(MakeLogicalSide(aAxis, eLogicalEdgeEnd)); - const LogicalMargin& border = - LogicalMargin(wm, mStyleBorder->GetComputedBorder()); - LogicalMargin padding(wm), margin(wm); + nsMargin styleBorder = mStyleBorder->GetComputedBorder(); + nscoord borderStartEnd = + styleBorder.Side(startSide) + styleBorder.Side(endSide); + + nscoord paddingStartEnd, marginStartEnd; // See if the style system can provide us the padding directly nsMargin stylePadding; if (mStylePadding->GetPadding(stylePadding)) { - padding = LogicalMargin(wm, stylePadding); + paddingStartEnd = + stylePadding.Side(startSide) + stylePadding.Side(endSide); } else { - // We have to compute the inline start and end values - padding.IStart(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStylePadding->mPadding.Get(inlineStart)); - padding.IEnd(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStylePadding->mPadding.Get(inlineEnd)); + // We have to compute the start and end values + nscoord start, end; + start = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStylePadding->mPadding.Get(startSide)); + end = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStylePadding->mPadding.Get(endSide)); + paddingStartEnd = start + end; } // See if the style system can provide us the margin directly nsMargin styleMargin; if (mStyleMargin->GetMargin(styleMargin)) { - margin = LogicalMargin(wm, styleMargin); + marginStartEnd = + styleMargin.Side(startSide) + styleMargin.Side(endSide); } else { - // We have to compute the left and right values - if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineStart)) { + nscoord start, end; + // We have to compute the start and end values + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(startSide)) { // XXX FIXME (or does CalculateBlockSideMargins do this?) - margin.IStart(wm) = 0; // just ignore + start = 0; // just ignore } else { - margin.IStart(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStyleMargin->mMargin.Get(inlineStart)); + start = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStyleMargin->mMargin.Get(startSide)); } - if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(inlineEnd)) { + if (eStyleUnit_Auto == mStyleMargin->mMargin.GetUnit(endSide)) { // XXX FIXME (or does CalculateBlockSideMargins do this?) - margin.IEnd(wm) = 0; // just ignore + end = 0; // just ignore } else { - margin.IEnd(wm) = nsLayoutUtils:: - ComputeCBDependentValue(aContainingBlockISize, - mStyleMargin->mMargin.Get(inlineEnd)); + end = nsLayoutUtils:: + ComputeCBDependentValue(aContainingBlockSize, + mStyleMargin->mMargin.Get(endSide)); } + marginStartEnd = start + end; } - nscoord outside = - padding.IStartEnd(wm) + border.IStartEnd(wm) + margin.IStartEnd(wm); + nscoord outside = paddingStartEnd + borderStartEnd + marginStartEnd; nscoord inside = 0; switch (mStylePosition->mBoxSizing) { case NS_STYLE_BOX_SIZING_BORDER: - inside += border.IStartEnd(wm); + inside += borderStartEnd; // fall through case NS_STYLE_BOX_SIZING_PADDING: - inside += padding.IStartEnd(wm); + inside += paddingStartEnd; } outside -= inside; *aInsideBoxSizing = inside; @@ -1175,8 +1185,12 @@ static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame, // Calculate the hypothetical box that the element would have if it were in // the flow. The values returned are relative to the padding edge of the -// absolute containing block, in the actual containing block's writing mode. -// cbrs->frame is the actual containing block +// absolute containing block. The writing-mode of the hypothetical box will +// have the same block direction as the absolute containing block, but may +// differ in inline-bidi direction. +// In the code below, |cbrs->frame| is the absolute containing block, while +// |containingBlock| is the nearest block container of the placeholder frame, +// which may be different from the absolute containing block. void nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, nsIFrame* aPlaceholderFrame, @@ -1189,16 +1203,20 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // Find the nearest containing block frame to the placeholder frame, // and its inline-start edge and width. - nscoord blockIStartContentEdge, blockContentISize; + nscoord blockIStartContentEdge; + // Dummy writing mode for blockContentSize, will be changed as needed by + // GetHypotheticalBoxContainer. + WritingMode cbwm = cbrs->GetWritingMode(); + LogicalSize blockContentSize(cbwm); nsIFrame* containingBlock = GetHypotheticalBoxContainer(aPlaceholderFrame, blockIStartContentEdge, - blockContentISize); + blockContentSize); + // Now blockContentSize is in containingBlock's writing mode. // If it's a replaced element and it has a 'auto' value for //'inline size', see if we can get the intrinsic size. This will allow // us to exactly determine both the inline edges WritingMode wm = containingBlock->GetWritingMode(); - aHypotheticalBox.mWritingMode = wm; nsStyleCoord styleISize = mStylePosition->ISize(wm); bool isAutoISize = styleISize.GetUnit() == eStyleUnit_Auto; @@ -1227,8 +1245,9 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // been in the flow. Note that we ignore any 'auto' and 'inherit' // values nscoord insideBoxSizing, outsideBoxSizing; - CalculateInlineBorderPaddingMargin(blockContentISize, - &insideBoxSizing, &outsideBoxSizing); + CalculateBorderPaddingMargin(eLogicalAxisInline, + blockContentSize.ISize(wm), + &insideBoxSizing, &outsideBoxSizing); if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoISize) { // It's a replaced element with an 'auto' inline size so the box @@ -1241,14 +1260,14 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, } else if (isAutoISize) { // The box inline size is the containing block inline size - boxISize = blockContentISize; + boxISize = blockContentSize.ISize(wm); knowBoxISize = true; } else { // We need to compute it. It's important we do this, because if it's // percentage based this computed value may be different from the computed // value calculated using the absolute containing block width - boxISize = ComputeISizeValue(blockContentISize, + boxISize = ComputeISizeValue(blockContentSize.ISize(wm), insideBoxSizing, outsideBoxSizing, styleISize) + insideBoxSizing + outsideBoxSizing; @@ -1260,7 +1279,6 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // space of its containing block // XXXbz the placeholder is not fully reflowed yet if our containing block is // relatively positioned... - WritingMode cbwm = cbrs->GetWritingMode(); nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW ? cbrs->ComputedSizeAsContainerIfConstrained() : containingBlock->GetSize(); @@ -1365,7 +1383,8 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // We can't compute the inline-end edge because we don't know the desired // inline-size. So instead use the end content edge of the block parent, // but remember it's not exact - aHypotheticalBox.mIEnd = blockIStartContentEdge + blockContentISize; + aHypotheticalBox.mIEnd = + blockIStartContentEdge + blockContentSize.ISize(wm); #ifdef DEBUG aHypotheticalBox.mIEndIsExact = false; #endif @@ -1408,9 +1427,7 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, // scroll, and thus avoid the resulting incremental reflow bugs. cbOffset = containingBlock->GetOffsetTo(cbrs->frame); } - nsSize cbrsSize = - cbrs->ComputedPhysicalSize() + - cbrs->ComputedLogicalBorderPadding().Size(cbwm).GetPhysicalSize(cbwm); + nsSize cbrsSize = cbrs->ComputedSizeAsContainerIfConstrained(); LogicalPoint logCBOffs(wm, cbOffset, cbrsSize - containerSize); aHypotheticalBox.mIStart += logCBOffs.I(wm); aHypotheticalBox.mIEnd += logCBOffs.I(wm); @@ -1425,6 +1442,74 @@ nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext, aHypotheticalBox.mIStart -= border.IStart(wm); aHypotheticalBox.mIEnd -= border.IStart(wm); aHypotheticalBox.mBStart -= border.BStart(wm); + + // At this point, we have computed aHypotheticalBox using the writing mode + // of the placeholder's containing block. + + if (cbwm.GetBlockDir() != wm.GetBlockDir()) { + // If the block direction we used in calculating aHypotheticalBox does not + // match the absolute containing block's, we need to convert here so that + // aHypotheticalBox is usable in relation to the absolute containing block. + // This requires computing or measuring the abspos frame's block-size, + // which is not otherwise required/used here (as aHypotheticalBox records + // only the block-start coordinate). + + // This is similar to the inline-size calculation for a replaced + // inline-level element or a block-level element (above), except that + // 'auto' sizing is handled differently in the block direction for non- + // replaced elements and replaced elements lacking an intrinsic size. + + // Determine the total amount of block direction + // border/padding/margin that the element would have had if it had + // been in the flow. Note that we ignore any 'auto' and 'inherit' + // values. + nscoord insideBoxSizing, outsideBoxSizing; + CalculateBorderPaddingMargin(eLogicalAxisBlock, + blockContentSize.BSize(wm), + &insideBoxSizing, &outsideBoxSizing); + + nscoord boxBSize; + nsStyleCoord styleBSize = mStylePosition->BSize(wm); + bool isAutoBSize = styleBSize.GetUnit() == eStyleUnit_Auto; + if (isAutoBSize) { + if (NS_FRAME_IS_REPLACED(mFrameType) && knowIntrinsicSize) { + // It's a replaced element with an 'auto' block size so the box + // block size is its intrinsic size plus any border/padding/margin + boxBSize = LogicalSize(wm, intrinsicSize).BSize(wm) + + outsideBoxSizing + insideBoxSizing; + } else { + // XXX Bug 1191801 + // Figure out how to get the correct boxBSize here (need to reflow the + // positioned frame?) + boxBSize = 0; + } + } else { + // We need to compute it. It's important we do this, because if it's + // percentage-based this computed value may be different from the + // computed value calculated using the absolute containing block height. + boxBSize = ComputeBSizeValue(blockContentSize.BSize(wm), + insideBoxSizing, styleBSize) + + insideBoxSizing + outsideBoxSizing; + } + + LogicalSize boxSize(wm, knowBoxISize ? boxISize : 0, boxBSize); + + LogicalPoint origin(wm, aHypotheticalBox.mIStart, + aHypotheticalBox.mBStart); + origin = origin.ConvertTo(cbwm, wm, cbrsSize - + boxSize.GetPhysicalSize(wm)); + + aHypotheticalBox.mIStart = origin.I(cbwm); + aHypotheticalBox.mIEnd = aHypotheticalBox.mIStart + + boxSize.ConvertTo(cbwm, wm).ISize(cbwm); +#ifdef DEBUG + aHypotheticalBox.mIEndIsExact = false; // it may be fake +#endif + aHypotheticalBox.mBStart = origin.B(cbwm); + aHypotheticalBox.mWritingMode = cbwm; + } else { + aHypotheticalBox.mWritingMode = wm; + } } void @@ -1520,8 +1605,6 @@ nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext, if (bStartIsAuto && bEndIsAuto) { // Treat 'top' like 'static-position' - NS_ASSERTION(hypotheticalBox.mWritingMode.GetBlockDir() == cbwm.GetBlockDir(), - "block direction mismatch"); offsets.BStart(cbwm) = hypotheticalBox.mBStart; bStartIsAuto = false; } diff --git a/layout/generic/nsHTMLReflowState.h b/layout/generic/nsHTMLReflowState.h index 9a017546bb..f3a893036f 100644 --- a/layout/generic/nsHTMLReflowState.h +++ b/layout/generic/nsHTMLReflowState.h @@ -909,13 +909,18 @@ protected: // Returns the nearest containing block or block frame (whether or not // it is a containing block) for the specified frame. Also returns - // the inline-start edge and inline size of the containing block's + // the inline-start edge and logical size of the containing block's // content area. // These are returned in the coordinate space of the containing block. nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame, nscoord& aCBIStartEdge, - nscoord& aCBISize); + mozilla::LogicalSize& aCBSize); + // Calculate a "hypothetical box" position where the placeholder frame + // (for a position:fixed/absolute element) would have been placed if it were + // positioned statically. The hypothetical box will have a writing mode with + // the same block direction as the absolute containing block (cbrs->frame), + // though it may differ in inline-bidi direction. void CalculateHypotheticalBox(nsPresContext* aPresContext, nsIFrame* aPlaceholderFrame, const nsHTMLReflowState* cbrs, @@ -932,9 +937,13 @@ protected: // data members void ComputeMinMaxValues(const mozilla::LogicalSize& aContainingBlockSize); - void CalculateInlineBorderPaddingMargin(nscoord aContainingBlockISize, - nscoord* aInsideBoxSizing, - nscoord* aOutsideBoxSizing); + // aInsideBoxSizing returns the part of the padding, border, and margin + // in the aAxis dimension that goes inside the edge given by box-sizing; + // aOutsideBoxSizing returns the rest. + void CalculateBorderPaddingMargin(mozilla::LogicalAxis aAxis, + nscoord aContainingBlockSize, + nscoord* aInsideBoxSizing, + nscoord* aOutsideBoxSizing); void CalculateBlockSideMargins(nsIAtom* aFrameType); }; diff --git a/layout/generic/nsLineBox.cpp b/layout/generic/nsLineBox.cpp index 1be636f3a8..b004e1cfbd 100644 --- a/layout/generic/nsLineBox.cpp +++ b/layout/generic/nsLineBox.cpp @@ -250,11 +250,8 @@ nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const str += nsPrintfCString("{%d,%d,%d,%d} ", bounds.x, bounds.y, bounds.width, bounds.height); if (mWritingMode.IsVertical() || !mWritingMode.IsBidiLTR()) { - str += nsPrintfCString("{%s-%s: %d,%d,%d,%d; cs=%d,%d} ", - mWritingMode.IsVertical() - ? mWritingMode.IsVerticalLR() ? "vlr" : "vrl" - : "htb", - mWritingMode.IsBidiLTR() ? "ltr" : "rtl", + str += nsPrintfCString("{%s: %d,%d,%d,%d; cs=%d,%d} ", + mWritingMode.DebugString(), IStart(), BStart(), ISize(), BSize(), mContainerSize.width, mContainerSize.height); } diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index 0bb467148c..5ef67c759c 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -1,4 +1,5 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ @@ -1581,15 +1582,6 @@ void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame) } } -static nscoord StyleToCoord(const nsStyleCoord& aCoord) -{ - if (eStyleUnit_Coord == aCoord.GetUnit()) { - return aCoord.GetCoordValue(); - } else { - return 0; - } -} - static bool HasTerminalNewline(const nsTextFrame* aFrame) { @@ -1599,6 +1591,29 @@ HasTerminalNewline(const nsTextFrame* aFrame) return frag->CharAt(aFrame->GetContentEnd() - 1) == '\n'; } +static gfxFont::Metrics +GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVerticalMetrics) +{ + if (!aFontGroup) + return gfxFont::Metrics(); + gfxFont* font = aFontGroup->GetFirstValidFont(); + return font->GetMetrics(aVerticalMetrics ? gfxFont::eVertical + : gfxFont::eHorizontal); +} + +static gfxFloat +GetSpaceWidthAppUnits(gfxTextRun* aTextRun) +{ + // Round the space width when converting to appunits the same way textruns + // do. + gfxFloat spaceWidthAppUnits = + NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(), + aTextRun->UseCenterBaseline()).spaceWidth * + aTextRun->GetAppUnitsPerDevUnit()); + + return spaceWidthAppUnits; +} + static nscoord LetterSpacing(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr) { @@ -1608,11 +1623,18 @@ LetterSpacing(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr) if (!aStyleText) { aStyleText = aFrame->StyleText(); } - return StyleToCoord(aStyleText->mLetterSpacing); + + const nsStyleCoord& coord = aStyleText->mLetterSpacing; + if (eStyleUnit_Coord == coord.GetUnit()) { + return coord.GetCoordValue(); + } + return 0; } +// This function converts non-coord values (e.g. percentages) to nscoord. static nscoord -WordSpacing(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr) +WordSpacing(nsIFrame* aFrame, gfxTextRun* aTextRun, + const nsStyleText* aStyleText = nullptr) { if (aFrame->IsSVGText()) { return 0; @@ -1620,7 +1642,39 @@ WordSpacing(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr) if (!aStyleText) { aStyleText = aFrame->StyleText(); } - return aStyleText->mWordSpacing; + + const nsStyleCoord& coord = aStyleText->mWordSpacing; + if (coord.IsCoordPercentCalcUnit()) { + nscoord pctBasis = coord.HasPercent() ? GetSpaceWidthAppUnits(aTextRun) : 0; + return nsRuleNode::ComputeCoordPercentCalc(coord, pctBasis); + } + return 0; +} + +// Returns gfxTextRunFactory::TEXT_ENABLE_SPACING if non-standard +// letter-spacing or word-spacing is present. +static uint32_t +GetSpacingFlags(nsIFrame* aFrame, const nsStyleText* aStyleText = nullptr) +{ + if (aFrame->IsSVGText()) { + return 0; + } + + const nsStyleText* styleText = aFrame->StyleText(); + const nsStyleCoord& ls = styleText->mLetterSpacing; + const nsStyleCoord& ws = styleText->mWordSpacing; + + // It's possible to have a calc() value that computes to zero but for which + // IsDefinitelyZero() is false, in which case we'll return + // TEXT_ENABLE_SPACING unnecessarily. That's ok because such cases are likely + // to be rare, and avoiding TEXT_ENABLE_SPACING is just an optimization. + bool nonStandardSpacing = + (eStyleUnit_Coord == ls.GetUnit() && ls.GetCoordValue() != 0) || + (eStyleUnit_Coord == ws.GetUnit() && ws.GetCoordValue() != 0) || + (eStyleUnit_Percent == ws.GetUnit() && ws.GetPercentValue() != 0) || + (eStyleUnit_Calc == ws.GetUnit() && !ws.GetCalcValue()->IsDefinitelyZero()); + + return nonStandardSpacing ? gfxTextRunFactory::TEXT_ENABLE_SPACING : 0; } bool @@ -1774,12 +1828,6 @@ BuildTextRunsScanner::GetNextBreakBeforeFrame(uint32_t* aIndex) return static_cast(mLineBreakBeforeFrames.ElementAt(index)); } -static uint32_t -GetSpacingFlags(nscoord spacing) -{ - return spacing ? gfxTextRunFactory::TEXT_ENABLE_SPACING : 0; -} - static gfxFontGroup* GetFontGroupForFrame(nsIFrame* aFrame, float aFontSizeInflation, nsFontMetrics** aOutFontMetrics = nullptr) @@ -1828,16 +1876,6 @@ GetHyphenTextRun(gfxTextRun* aTextRun, gfxContext* aContext, nsTextFrame* aTextF MakeHyphenTextRun(ctx, aTextRun->GetAppUnitsPerDevUnit()); } -static gfxFont::Metrics -GetFirstFontMetrics(gfxFontGroup* aFontGroup, bool aVerticalMetrics) -{ - if (!aFontGroup) - return gfxFont::Metrics(); - gfxFont* font = aFontGroup->GetFirstValidFont(); - return font->GetMetrics(aVerticalMetrics ? gfxFont::eVertical - : gfxFont::eHorizontal); -} - PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0); PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE == 1); PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NOWRAP == 2); @@ -1945,8 +1983,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) { anyTextTransformStyle = true; } - textFlags |= GetSpacingFlags(LetterSpacing(f)); - textFlags |= GetSpacingFlags(WordSpacing(f)); + textFlags |= GetSpacingFlags(f); nsTextFrameUtils::CompressionMode compression = GetCSSWhitespaceToCompressionMode(f, textStyle); if ((enabledJustification || f->ShouldSuppressLineBreak()) && @@ -2877,7 +2914,7 @@ public: mFrame(aFrame), mStart(aStart), mTempIterator(aStart), mTabWidths(nullptr), mTabWidthsAnalyzedLimit(0), mLength(aLength), - mWordSpacing(WordSpacing(aFrame, aTextStyle)), + mWordSpacing(WordSpacing(aFrame, mTextRun, aTextStyle)), mLetterSpacing(LetterSpacing(aFrame, aTextStyle)), mHyphenWidth(-1), mOffsetFromBlockOriginForTabs(aOffsetFromBlockOriginForTabs), @@ -2902,7 +2939,7 @@ public: mFrame(aFrame), mStart(aStart), mTempIterator(aStart), mTabWidths(nullptr), mTabWidthsAnalyzedLimit(0), mLength(aFrame->GetContentLength()), - mWordSpacing(WordSpacing(aFrame)), + mWordSpacing(WordSpacing(aFrame, mTextRun)), mLetterSpacing(LetterSpacing(aFrame)), mHyphenWidth(-1), mOffsetFromBlockOriginForTabs(0), @@ -3227,14 +3264,8 @@ ComputeTabWidthAppUnits(nsIFrame* aFrame, gfxTextRun* aTextRun) { // Get the number of spaces from CSS -moz-tab-size const nsStyleText* textStyle = aFrame->StyleText(); - - // Round the space width when converting to appunits the same way - // textruns do - gfxFloat spaceWidthAppUnits = - NS_round(GetFirstFontMetrics(aTextRun->GetFontGroup(), - aTextRun->UseCenterBaseline()).spaceWidth * - aTextRun->GetAppUnitsPerDevUnit()); - return textStyle->mTabSize * spaceWidthAppUnits; + + return textStyle->mTabSize * GetSpaceWidthAppUnits(aTextRun); } // aX and the result are in whole appunits. diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp index d9e6edd1df..e862a432fd 100644 --- a/layout/inspector/inDOMUtils.cpp +++ b/layout/inspector/inDOMUtils.cpp @@ -238,13 +238,17 @@ inDOMUtils::GetCSSStyleRules(nsIDOMElement *aElement, NS_NewISupportsArray(getter_AddRefs(rules)); if (!rules) return NS_ERROR_OUT_OF_MEMORY; - RefPtr cssRule; for ( ; !ruleNode->IsRoot(); ruleNode = ruleNode->GetParent()) { - cssRule = do_QueryObject(ruleNode->GetRule()); - if (cssRule) { - nsCOMPtr domRule = cssRule->GetDOMRule(); - if (domRule) - rules->InsertElementAt(domRule, 0); + RefPtr decl = do_QueryObject(ruleNode->GetRule()); + if (decl) { + RefPtr styleRule = + do_QueryObject(decl->GetOwningRule()); + if (styleRule) { + nsCOMPtr domRule = styleRule->GetDOMRule(); + if (domRule) { + rules->InsertElementAt(domRule, 0); + } + } } } diff --git a/layout/inspector/tests/test_bug877690.html b/layout/inspector/tests/test_bug877690.html index 5c102908a8..f4baacf4ba 100644 --- a/layout/inspector/tests/test_bug877690.html +++ b/layout/inspector/tests/test_bug877690.html @@ -172,8 +172,8 @@ function do_test() { // test property var prop = "float"; var values = utils.getCSSValuesForProperty(prop); - var expected = [ "initial", "inherit", "unset", "none", "left", "right" ]; - ok(testValues(values, expected), "proprety float values"); + var expected = [ "initial", "inherit", "unset", "none", "left", "right", "inline-start", "inline-end" ]; + ok(testValues(values, expected), "property float's values."); // Test property with "auto" var prop = "margin"; diff --git a/layout/mathml/crashtests/crashtests.list b/layout/mathml/crashtests/crashtests.list index 718aa1e0b2..9dd6d58bf2 100644 --- a/layout/mathml/crashtests/crashtests.list +++ b/layout/mathml/crashtests/crashtests.list @@ -58,9 +58,9 @@ load 654928-1.html load 655451-1.xhtml load 713606-1.html load 716349-1.html -load 947557-1.html -test-pref(layout.css.sticky.enabled,true) load 973322-1.xhtml load 848725-1.html load 848725-2.html +load 947557-1.html +test-pref(layout.css.sticky.enabled,true) load 973322-1.xhtml load 1028521-1.xhtml load 1061027.html diff --git a/layout/reftests/css-ruby/min-font-size-1-ref.html b/layout/reftests/css-ruby/min-font-size-1-ref.html new file mode 100644 index 0000000000..86ce0bb369 --- /dev/null +++ b/layout/reftests/css-ruby/min-font-size-1-ref.html @@ -0,0 +1,13 @@ + + + + + Bug 1165538 - Minimum font size on ruby text + + + + è¶àÕ›»ç£ç ² + ?¬ãƒ¼?«ãâ\??/rt> + + + diff --git a/layout/reftests/css-ruby/min-font-size-1.html b/layout/reftests/css-ruby/min-font-size-1.html new file mode 100644 index 0000000000..555fa5e6ce --- /dev/null +++ b/layout/reftests/css-ruby/min-font-size-1.html @@ -0,0 +1,13 @@ + + + + + Bug 1165538 - Minimum font size on ruby text + + + + è¶àÕ›»ç£ç ² + ?¬ãƒ¼?«ãâ\??/rt> + + + diff --git a/layout/reftests/css-ruby/reftest.list b/layout/reftests/css-ruby/reftest.list index 609f6f9775..9954c89226 100644 --- a/layout/reftests/css-ruby/reftest.list +++ b/layout/reftests/css-ruby/reftest.list @@ -37,6 +37,7 @@ fuzzy-if(winWidget,255,792) == lang-specific-style-1.html lang-specific-style-1- == line-height-2.html line-height-2-ref.html == line-height-3.html line-height-3-ref.html == line-height-4.html line-height-4-ref.html +test-pref(font.minimum-size.ja,16) == min-font-size-1.html min-font-size-1-ref.html load nested-ruby-1.html == no-transform.html no-transform-ref.html == relative-positioning-1.html relative-positioning-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-001-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-001-ref.html new file mode 100644 index 0000000000..9cffaf9a39 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-001-ref.html @@ -0,0 +1,22 @@ + + + + + CSS Reftest Reference + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-001.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-001.html new file mode 100644 index 0000000000..cda70052d1 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-001.html @@ -0,0 +1,71 @@ + + + + + CSS Test: 'contain: paint' with various overflowing block descendants. + + + + + + +
+
+
+ + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-002-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-002-ref.html new file mode 100644 index 0000000000..99e0354764 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-002-ref.html @@ -0,0 +1,33 @@ + + + + + CSS Reftest Reference + + + + +
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA This text should + be clipped to the box. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed + nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. + Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. + Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales + ligula in libero. +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-002.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-002.html new file mode 100644 index 0000000000..b5c9e340bf --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-002.html @@ -0,0 +1,35 @@ + + + + + CSS Test: 'contain: paint' with overflowing text contents. + + + + + + +
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA This text should + be clipped to the box. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed + nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. + Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. + Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales + ligula in libero. +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-003-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-003-ref.html new file mode 100644 index 0000000000..34e79489af --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-003-ref.html @@ -0,0 +1,34 @@ + + + + + CSS Reftest Reference + + + + +
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA This text should + be clipped to the box. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed + nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. + Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. + Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales + ligula in libero. +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-003.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-003.html new file mode 100644 index 0000000000..e02dae14f0 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-003.html @@ -0,0 +1,36 @@ + + + + + CSS Test: 'contain: paint' with overflowing text contents, and 'overflow-y: scroll'. + + + + + + +
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA This text should + be clipped to the box. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed + nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. + Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. + Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales + ligula in libero. +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-004-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-004-ref.html new file mode 100644 index 0000000000..4eda898abe --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-004-ref.html @@ -0,0 +1,34 @@ + + + + + CSS Reftest Reference + + + + +
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA This text should + be clipped to the box. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed + nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. + Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. + Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales + ligula in libero. +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-004.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-004.html new file mode 100644 index 0000000000..e3ed254761 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-004.html @@ -0,0 +1,36 @@ + + + + + CSS Test: 'contain: paint' with overflowing text contents, and 'overflow-x: scroll'. + + + + + + +
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA This text should + be clipped to the box. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed + nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. + Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. + Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad litora + torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales + ligula in libero. +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-005.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-005.html new file mode 100644 index 0000000000..dc92202640 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-clip-005.html @@ -0,0 +1,42 @@ + + + + + CSS Test: 'contain: paint' on li with overflowing text contents and + bullet, and 'overflow-y: scroll'. + + + + + + +
    +
  • + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA This text should + be clipped to the box. Lorem ipsum dolor sit amet, consectetur adipiscing + elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. + Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis + ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris + massa. Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu + ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur + sodales ligula in libero. +
  • +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-absolute-001-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-absolute-001-ref.html new file mode 100644 index 0000000000..422706802a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-absolute-001-ref.html @@ -0,0 +1,22 @@ + + + + + CSS Reftest Reference + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-absolute-001.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-absolute-001.html new file mode 100644 index 0000000000..fc04df008a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-absolute-001.html @@ -0,0 +1,37 @@ + + + + + CSS Test: 'contain: paint' element should contain absolute position elements. + + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-fixed-001-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-fixed-001-ref.html new file mode 100644 index 0000000000..422706802a --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-fixed-001-ref.html @@ -0,0 +1,22 @@ + + + + + CSS Reftest Reference + + + + +
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-fixed-001.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-fixed-001.html new file mode 100644 index 0000000000..60f180beaa --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-containing-block-fixed-001.html @@ -0,0 +1,37 @@ + + + + + CSS Test: 'contain: paint' element should contain fixed position elements. + + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-float-001-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-float-001-ref.html new file mode 100644 index 0000000000..5a70a21c17 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-float-001-ref.html @@ -0,0 +1,29 @@ + + + + + CSS Reftest Reference + + + + +
+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-float-001.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-float-001.html new file mode 100644 index 0000000000..06a59f2e58 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-float-001.html @@ -0,0 +1,40 @@ + + + + + CSS Test: 'contain: paint' should contain floats as a formatting context. + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-margin-001-ref.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-margin-001-ref.html new file mode 100644 index 0000000000..6dde7bc90e --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-margin-001-ref.html @@ -0,0 +1,33 @@ + + + + + CSS Reftest Test + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-margin-001.html b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-margin-001.html new file mode 100644 index 0000000000..fdc0bb7f15 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/contain-paint-formatting-context-margin-001.html @@ -0,0 +1,39 @@ + + + + + CSS Test: 'contain: paint' with a vertical margin child. Margin collapse should not occur. + + + + + + +
+
+
+
+ + diff --git a/layout/reftests/w3c-css/submitted/contain/reftest.list b/layout/reftests/w3c-css/submitted/contain/reftest.list new file mode 100644 index 0000000000..5326e86a58 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/contain/reftest.list @@ -0,0 +1,11 @@ +default-preferences pref(layout.css.contain.enabled,true) + +== contain-paint-clip-001.html contain-paint-clip-001-ref.html +== contain-paint-clip-002.html contain-paint-clip-002-ref.html +== contain-paint-clip-003.html contain-paint-clip-003-ref.html +== contain-paint-clip-004.html contain-paint-clip-004-ref.html +== contain-paint-clip-005.html contain-paint-clip-003-ref.html +== contain-paint-containing-block-absolute-001.html contain-paint-containing-block-absolute-001-ref.html +== contain-paint-containing-block-fixed-001.html contain-paint-containing-block-fixed-001-ref.html +== contain-paint-formatting-context-float-001.html contain-paint-formatting-context-float-001-ref.html +== contain-paint-formatting-context-margin-001.html contain-paint-formatting-context-margin-001-ref.html diff --git a/layout/reftests/w3c-css/submitted/reftest.list b/layout/reftests/w3c-css/submitted/reftest.list index d11b480e32..83bdc1167e 100644 --- a/layout/reftests/w3c-css/submitted/reftest.list +++ b/layout/reftests/w3c-css/submitted/reftest.list @@ -19,6 +19,9 @@ include css21/reftest.list # Conditional Rules Level 3 include conditional3/reftest.list +# Containment +include contain/reftest.list + # Filter Effects Module include filters/reftest.list diff --git a/layout/reftests/w3c-css/submitted/text3/reftest.list b/layout/reftests/w3c-css/submitted/text3/reftest.list index a640d8ff72..2712e4363b 100644 --- a/layout/reftests/w3c-css/submitted/text3/reftest.list +++ b/layout/reftests/w3c-css/submitted/text3/reftest.list @@ -4,3 +4,5 @@ == text-align-match-parent-04.html text-align-match-parent-ref.html == text-align-match-parent-root-ltr.html text-align-match-parent-root-ltr-ref.html == text-align-match-parent-root-rtl.html text-align-match-parent-root-rtl-ref.html + +== text-word-spacing-001.html text-word-spacing-ref.html diff --git a/layout/reftests/w3c-css/submitted/text3/support/Ahem.ttf b/layout/reftests/w3c-css/submitted/text3/support/Ahem.ttf new file mode 100644 index 0000000000..ac81cb0316 Binary files /dev/null and b/layout/reftests/w3c-css/submitted/text3/support/Ahem.ttf differ diff --git a/layout/reftests/w3c-css/submitted/text3/support/ahem.css b/layout/reftests/w3c-css/submitted/text3/support/ahem.css new file mode 100644 index 0000000000..82ee466791 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/text3/support/ahem.css @@ -0,0 +1,4 @@ +@font-face { + font-family: "Ahem"; + src: url(./Ahem.ttf); +} diff --git a/layout/reftests/w3c-css/submitted/text3/text-word-spacing-001.html b/layout/reftests/w3c-css/submitted/text3/text-word-spacing-001.html new file mode 100644 index 0000000000..0706ce1fbc --- /dev/null +++ b/layout/reftests/w3c-css/submitted/text3/text-word-spacing-001.html @@ -0,0 +1,34 @@ + +CSS Text Test: Word Spacing + + + + + + + +

Test passes if the space between the words starts at zero and increases by + one on each subsequent line.

+
A Bc Def Ghij
+
A Bc Def Ghij
+
A Bc Def Ghij
+
A     Bc     Def     Ghij
+
A Bc Def Ghij
+
A    Bc    Def    Ghij
+
A Bc Def Ghij
+ diff --git a/layout/reftests/w3c-css/submitted/text3/text-word-spacing-ref.html b/layout/reftests/w3c-css/submitted/text3/text-word-spacing-ref.html new file mode 100644 index 0000000000..e85d57babd --- /dev/null +++ b/layout/reftests/w3c-css/submitted/text3/text-word-spacing-ref.html @@ -0,0 +1,28 @@ + +CSS Text Test: Word Spacing + + + + + + + +

Test passes if the space between the words starts at zero and increases by + one on each subsequent line.

+
ABcDefGhij
+
A Bc Def Ghij
+
A  Bc  Def  Ghij
+
A   Bc   Def   Ghij
+
A    Bc    Def    Ghij
+
A     Bc     Def     Ghij
+
A     Bc     Def     Ghij     
+ diff --git a/layout/reftests/writing-mode/1216747-1-notref.html b/layout/reftests/writing-mode/1216747-1-notref.html new file mode 100644 index 0000000000..373fd99b7e --- /dev/null +++ b/layout/reftests/writing-mode/1216747-1-notref.html @@ -0,0 +1,3 @@ + + +
This should have min-content width (i.e., many line breaks).
diff --git a/layout/reftests/writing-mode/1216747-1-ref.html b/layout/reftests/writing-mode/1216747-1-ref.html new file mode 100644 index 0000000000..440456ef1f --- /dev/null +++ b/layout/reftests/writing-mode/1216747-1-ref.html @@ -0,0 +1,10 @@ + + + +
This should have min-content width (i.e., many line breaks).
diff --git a/layout/reftests/writing-mode/1216747-1.html b/layout/reftests/writing-mode/1216747-1.html new file mode 100644 index 0000000000..e982646ade --- /dev/null +++ b/layout/reftests/writing-mode/1216747-1.html @@ -0,0 +1,14 @@ + + + + +
+ +
This should have min-content width (i.e., many line breaks).
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1-ref.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1-ref.html new file mode 100644 index 0000000000..fc6e6b5afb --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1-ref.html @@ -0,0 +1,16 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1a.html new file mode 100644 index 0000000000..e3a9f414df --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1a.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1b.html new file mode 100644 index 0000000000..12e79285da --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1b.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1c.html new file mode 100644 index 0000000000..234d9b86fa --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-1c.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2-ref.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2-ref.html new file mode 100644 index 0000000000..69f635771e --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2-ref.html @@ -0,0 +1,16 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2a.html new file mode 100644 index 0000000000..26a039659f --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2a.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2b.html new file mode 100644 index 0000000000..d5960f00f2 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2b.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2c.html new file mode 100644 index 0000000000..37798c920f --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-2c.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3-ref.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3-ref.html new file mode 100644 index 0000000000..87e0c4e8fc --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3-ref.html @@ -0,0 +1,13 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3a.html new file mode 100644 index 0000000000..2231ad7bdb --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3a.html @@ -0,0 +1,14 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3b.html new file mode 100644 index 0000000000..f75b01b1ab --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3b.html @@ -0,0 +1,14 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3c.html new file mode 100644 index 0000000000..d2a1511d66 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-3c.html @@ -0,0 +1,14 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4-ref.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4-ref.html new file mode 100644 index 0000000000..4a6eda9992 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4-ref.html @@ -0,0 +1,17 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4a.html new file mode 100644 index 0000000000..5c7ecab700 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4a.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4b.html new file mode 100644 index 0000000000..1e51065b7a --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4b.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4c.html new file mode 100644 index 0000000000..edc8b870e4 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-4c.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5-ref.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5-ref.html new file mode 100644 index 0000000000..cf0b18aca1 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5-ref.html @@ -0,0 +1,17 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5a.html new file mode 100644 index 0000000000..fd7e3ceadd --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5a.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5b.html new file mode 100644 index 0000000000..2b48fcaa82 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5b.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5c.html new file mode 100644 index 0000000000..3923017a4c --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-5c.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6-ref.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6-ref.html new file mode 100644 index 0000000000..c681510040 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6-ref.html @@ -0,0 +1,13 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6a.html new file mode 100644 index 0000000000..41f6681c93 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6a.html @@ -0,0 +1,14 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6b.html new file mode 100644 index 0000000000..d768e575c6 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6b.html @@ -0,0 +1,14 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6c.html new file mode 100644 index 0000000000..111443acf2 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-6c.html @@ -0,0 +1,14 @@ + + + + + +
+
abc def

xyzzy

+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7a.html new file mode 100644 index 0000000000..906757ae95 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7a.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7b.html new file mode 100644 index 0000000000..9056e75cb4 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7b.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7c.html new file mode 100644 index 0000000000..d21dbaa1e0 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-7c.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8a.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8a.html new file mode 100644 index 0000000000..ccca0406c1 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8a.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8b.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8b.html new file mode 100644 index 0000000000..948c0ee097 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8b.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8c.html b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8c.html new file mode 100644 index 0000000000..c8b95701b4 --- /dev/null +++ b/layout/reftests/writing-mode/abspos/1183431-orthogonal-modes-8c.html @@ -0,0 +1,15 @@ + + + + + +
+
abc
+
diff --git a/layout/reftests/writing-mode/abspos/blue-32x32.png b/layout/reftests/writing-mode/abspos/blue-32x32.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/layout/reftests/writing-mode/abspos/reftest.list b/layout/reftests/writing-mode/abspos/reftest.list index 826c8ccb1a..7714d63e36 100644 --- a/layout/reftests/writing-mode/abspos/reftest.list +++ b/layout/reftests/writing-mode/abspos/reftest.list @@ -99,3 +99,28 @@ fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-092.xht s71-abs-pos-non-replaced-vrl-092-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-094.xht s71-abs-pos-non-replaced-vrl-094-ref.xht fuzzy-if(cocoaWidget,15,5) fuzzy-if(d2d,102,164) == s71-abs-pos-non-replaced-vrl-096.xht s71-abs-pos-non-replaced-vrl-096-ref.xht + +== 1183431-orthogonal-modes-1a.html 1183431-orthogonal-modes-1-ref.html +== 1183431-orthogonal-modes-1b.html 1183431-orthogonal-modes-1-ref.html +== 1183431-orthogonal-modes-1c.html 1183431-orthogonal-modes-1-ref.html +== 1183431-orthogonal-modes-2a.html 1183431-orthogonal-modes-2-ref.html +== 1183431-orthogonal-modes-2b.html 1183431-orthogonal-modes-2-ref.html +== 1183431-orthogonal-modes-2c.html 1183431-orthogonal-modes-2-ref.html +== 1183431-orthogonal-modes-3a.html 1183431-orthogonal-modes-3-ref.html +== 1183431-orthogonal-modes-3b.html 1183431-orthogonal-modes-3-ref.html +== 1183431-orthogonal-modes-3c.html 1183431-orthogonal-modes-3-ref.html +== 1183431-orthogonal-modes-4a.html 1183431-orthogonal-modes-4-ref.html +== 1183431-orthogonal-modes-4b.html 1183431-orthogonal-modes-4-ref.html +== 1183431-orthogonal-modes-4c.html 1183431-orthogonal-modes-4-ref.html +== 1183431-orthogonal-modes-5a.html 1183431-orthogonal-modes-5-ref.html +== 1183431-orthogonal-modes-5b.html 1183431-orthogonal-modes-5-ref.html +== 1183431-orthogonal-modes-5c.html 1183431-orthogonal-modes-5-ref.html +fails == 1183431-orthogonal-modes-6a.html 1183431-orthogonal-modes-6-ref.html # bug 1191801 +== 1183431-orthogonal-modes-6b.html 1183431-orthogonal-modes-6-ref.html +== 1183431-orthogonal-modes-6c.html 1183431-orthogonal-modes-6-ref.html +== 1183431-orthogonal-modes-7a.html 1183431-orthogonal-modes-7-ref.html +== 1183431-orthogonal-modes-7b.html 1183431-orthogonal-modes-7-ref.html +== 1183431-orthogonal-modes-7c.html 1183431-orthogonal-modes-7-ref.html +fails == 1183431-orthogonal-modes-8a.html 1183431-orthogonal-modes-8-ref.html # bug 1191801 +== 1183431-orthogonal-modes-8b.html 1183431-orthogonal-modes-8-ref.html +== 1183431-orthogonal-modes-8c.html 1183431-orthogonal-modes-8-ref.html diff --git a/layout/reftests/writing-mode/reftest.list b/layout/reftests/writing-mode/reftest.list index 7e362faec3..ecfdf0e024 100644 --- a/layout/reftests/writing-mode/reftest.list +++ b/layout/reftests/writing-mode/reftest.list @@ -163,6 +163,9 @@ fuzzy-if(gtkWidget||B2G,255,6) fuzzy-if(cocoaWidget,65,69) == 1193519-sideways-l == 1196887-1-computed-display-inline-block.html 1196887-1-computed-display-inline-block-ref.html == 1205787-legacy-svg-values-1.html 1205787-legacy-svg-values-1-ref.html +== 1216747-1.html 1216747-1-ref.html +!= 1216747-1.html 1216747-1-notref.html + # Suite of tests from Gérard Talbot in bug 1079151 include abspos/reftest.list diff --git a/layout/style/AnimationCommon.cpp b/layout/style/AnimationCommon.cpp index 789e58ab38..74d70476a5 100644 --- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -53,7 +53,6 @@ IsGeometricProperty(nsCSSProperty aProperty) CommonAnimationManager::CommonAnimationManager(nsPresContext *aPresContext) : mPresContext(aPresContext) - , mIsObservingRefreshDriver(false) { } @@ -74,14 +73,6 @@ CommonAnimationManager::Disconnect() void CommonAnimationManager::AddElementCollection(AnimationCollection* aCollection) { - if (!mIsObservingRefreshDriver) { - NS_ASSERTION(aCollection->mNeedsRefreshes, - "Added data which doesn't need refreshing?"); - // We need to observe the refresh driver. - mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style); - mIsObservingRefreshDriver = true; - } - mElementCollections.insertBack(aCollection); } @@ -93,41 +84,6 @@ CommonAnimationManager::RemoveAllElementCollections() } } -void -CommonAnimationManager::MaybeStartObservingRefreshDriver() -{ - if (mIsObservingRefreshDriver || !NeedsRefresh()) { - return; - } - - mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style); - mIsObservingRefreshDriver = true; -} - -void -CommonAnimationManager::MaybeStartOrStopObservingRefreshDriver() -{ - bool needsRefresh = NeedsRefresh(); - if (needsRefresh && !mIsObservingRefreshDriver) { - mPresContext->RefreshDriver()->AddRefreshObserver(this, Flush_Style); - } else if (!needsRefresh && mIsObservingRefreshDriver) { - mPresContext->RefreshDriver()->RemoveRefreshObserver(this, Flush_Style); - } - mIsObservingRefreshDriver = needsRefresh; -} - -bool -CommonAnimationManager::NeedsRefresh() const -{ - for (const AnimationCollection* collection = mElementCollections.getFirst(); - collection; collection = collection->getNext()) { - if (collection->mNeedsRefreshes) { - return true; - } - } - return false; -} - AnimationCollection* CommonAnimationManager::GetAnimationCollection(const nsIFrame* aFrame) { @@ -412,31 +368,6 @@ CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement, return collection->mStyleRule; } -/* virtual */ void -CommonAnimationManager::WillRefresh(TimeStamp aTime) -{ - MOZ_ASSERT(mPresContext, - "refresh driver should not notify additional observers " - "after pres context has been destroyed"); - if (!mPresContext->GetPresShell()) { - // Someone might be keeping mPresContext alive past the point - // where it has been torn down; don't bother doing anything in - // this case. But do get rid of all our animations so we stop - // triggering refreshes. - RemoveAllElementCollections(); - return; - } - - nsAutoAnimationMutationBatch mb(mPresContext->Document()); - - for (AnimationCollection* collection = mElementCollections.getFirst(); - collection; collection = collection->getNext()) { - collection->Tick(); - } - - MaybeStartOrStopObservingRefreshDriver(); -} - void CommonAnimationManager::ClearIsRunningOnCompositor(const nsIFrame* aFrame, nsCSSProperty aProperty) @@ -766,7 +697,7 @@ AnimationCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime) { mHasPendingAnimationRestyle = false; - if (!mNeedsRefreshes) { + if (!mStyleChanging) { mStyleRuleRefreshTime = aRefreshTime; return; } @@ -785,8 +716,8 @@ AnimationCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime) mStyleRuleRefreshTime = aRefreshTime; mStyleRule = nullptr; - // We'll set mNeedsRefreshes to true below in all cases where we need them. - mNeedsRefreshes = false; + // We'll set mStyleChanging to true below if necessary. + mStyleChanging = false; // If multiple animations specify behavior for the same property the // animation which occurs last in the value of animation-name wins. @@ -795,10 +726,8 @@ AnimationCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime) nsCSSPropertySet properties; for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) { - mAnimations[animIdx]->ComposeStyle(mStyleRule, properties, mNeedsRefreshes); + mAnimations[animIdx]->ComposeStyle(mStyleRule, properties, mStyleChanging); } - - mManager->MaybeStartObservingRefreshDriver(); } bool @@ -913,10 +842,7 @@ AnimationCollection::RequestRestyle(RestyleType aRestyleType) if (aRestyleType == RestyleType::Layer) { mStyleRuleRefreshTime = TimeStamp(); - // FIXME: We should be able to remove these two lines once we move - // ticking to animation timelines as part of bug 1151731. - mNeedsRefreshes = true; - mManager->MaybeStartObservingRefreshDriver(); + mStyleChanging = true; // Prompt layers to re-sync their animations. presContext->ClearLastStyleUpdateForAllAnimations(); diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h index f7544fc1a8..8cf4881f32 100644 --- a/layout/style/AnimationCommon.h +++ b/layout/style/AnimationCommon.h @@ -9,7 +9,6 @@ #include // For #include "nsIStyleRuleProcessor.h" #include "nsIStyleRule.h" -#include "nsRefreshDriver.h" #include "nsChangeHint.h" #include "nsCSSProperty.h" #include "nsDisplayList.h" // For nsDisplayItem::Type @@ -40,8 +39,7 @@ struct AnimationCollection; bool IsGeometricProperty(nsCSSProperty aProperty); -class CommonAnimationManager : public nsIStyleRuleProcessor, - public nsARefreshObserver { +class CommonAnimationManager : public nsIStyleRuleProcessor { public: explicit CommonAnimationManager(nsPresContext *aPresContext); @@ -64,9 +62,6 @@ public: virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_MUST_OVERRIDE override; - // nsARefreshObserver - void WillRefresh(TimeStamp aTime) override; - // NOTE: This can return null after Disconnect(). nsPresContext* PresContext() const { return mPresContext; } @@ -118,32 +113,22 @@ public: nsChangeHint mChangeHint; }; + virtual bool IsAnimationManager() { + return false; + } + protected: virtual ~CommonAnimationManager(); - // For ElementCollectionRemoved - friend struct AnimationCollection; - void AddElementCollection(AnimationCollection* aCollection); - void ElementCollectionRemoved() { MaybeStartOrStopObservingRefreshDriver(); } void RemoveAllElementCollections(); - // We should normally only call MaybeStartOrStopObservingRefreshDriver in - // situations where we will also queue events since otherwise we may stop - // getting refresh driver ticks before we queue the necessary events. - void MaybeStartObservingRefreshDriver(); - void MaybeStartOrStopObservingRefreshDriver(); bool NeedsRefresh() const; virtual nsIAtom* GetAnimationsAtom() = 0; virtual nsIAtom* GetAnimationsBeforeAtom() = 0; virtual nsIAtom* GetAnimationsAfterAtom() = 0; - virtual bool IsAnimationManager() { - return false; - } - - public: // Return an AnimationCollection* if we have an animation for // the frame aFrame that can be performed on the compositor thread (as @@ -167,7 +152,6 @@ public: protected: LinkedList mElementCollections; nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect) - bool mIsObservingRefreshDriver; }; /** @@ -229,7 +213,7 @@ struct AnimationCollection : public LinkedListElement , mManager(aManager) , mAnimationGeneration(0) , mCheckGeneration(0) - , mNeedsRefreshes(true) + , mStyleChanging(true) , mHasPendingAnimationRestyle(false) #ifdef DEBUG , mCalledPropertyDtor(false) @@ -243,7 +227,6 @@ struct AnimationCollection : public LinkedListElement "must call destructor through element property dtor"); MOZ_COUNT_DTOR(AnimationCollection); remove(); - mManager->ElementCollectionRemoved(); } void Destroy() @@ -427,7 +410,7 @@ public: // False when we know that our current style rule is valid // indefinitely into the future (because all of our animations are // either completed or paused). May be invalidated by a style change. - bool mNeedsRefreshes; + bool mStyleChanging; private: // Whether or not we have already posted for animation restyle. diff --git a/layout/style/CSSStyleSheet.cpp b/layout/style/CSSStyleSheet.cpp index b95a4c5b4f..a83f4a10d3 100644 --- a/layout/style/CSSStyleSheet.cpp +++ b/layout/style/CSSStyleSheet.cpp @@ -351,12 +351,34 @@ nsDocumentRuleResultCacheKey::Finalize() #endif } +#ifdef DEBUG +static bool +ArrayIsSorted(const nsTArray& aRules) +{ + for (size_t i = 1; i < aRules.Length(); i++) { + if (aRules[i - 1] > aRules[i]) { + return false; + } + } + return true; +} +#endif + bool nsDocumentRuleResultCacheKey::Matches( nsPresContext* aPresContext, const nsTArray& aRules) const { MOZ_ASSERT(mFinalized); + MOZ_ASSERT(ArrayIsSorted(mMatchingRules)); + MOZ_ASSERT(ArrayIsSorted(aRules)); + +#ifdef DEBUG + for (css::DocumentRule* rule : mMatchingRules) { + MOZ_ASSERT(aRules.BinaryIndexOf(rule) != aRules.NoIndex, + "aRules must contain all rules in mMatchingRules"); + } +#endif // First check that aPresContext matches all the rules listed in // mMatchingRules. @@ -382,8 +404,6 @@ nsDocumentRuleResultCacheKey::Matches( while (pr < pr_end) { while (pm < pm_end && *pm < *pr) { ++pm; - MOZ_ASSERT(pm >= pm_end || *pm == *pr, - "shouldn't find rule in mMatchingRules that is not in aRules"); } if (pm >= pm_end || *pm != *pr) { if ((*pr)->UseForPresentation(aPresContext)) { @@ -2325,8 +2345,11 @@ CSSStyleSheet::ReparseSheet(const nsAString& aInput) mInner->mFirstChild = nullptr; mInner->mNameSpaceMap = nullptr; - // allow unsafe rules if the style sheet's principal is the system principal - bool allowUnsafeRules = nsContentUtils::IsSystemPrincipal(mInner->mPrincipal); + // allow agent features if the style sheet's principal is the system principal + css::SheetParsingMode parsingMode = + nsContentUtils::IsSystemPrincipal(mInner->mPrincipal) + ? css::eAgentSheetFeatures + : css::eAuthorSheetFeatures; uint32_t lineNumber = 1; if (mOwningNode) { @@ -2339,7 +2362,7 @@ CSSStyleSheet::ReparseSheet(const nsAString& aInput) nsCSSParser parser(loader, this); nsresult rv = parser.ParseSheet(aInput, mInner->mSheetURI, mInner->mBaseURI, mInner->mPrincipal, lineNumber, - allowUnsafeRules, &reusableSheets); + parsingMode, &reusableSheets); DidDirty(); // we are always 'dirty' here since we always remove rules first NS_ENSURE_SUCCESS(rv, rv); @@ -2357,12 +2380,6 @@ CSSStyleSheet::ReparseSheet(const nsAString& aInput) return NS_OK; } -/* virtual */ nsIURI* -CSSStyleSheet::GetOriginalURI() const -{ - return mInner->mOriginalSheetURI; -} - /* virtual */ JSObject* CSSStyleSheet::WrapObject(JSContext* aCx, JS::Handle aGivenProto) diff --git a/layout/style/CSSStyleSheet.h b/layout/style/CSSStyleSheet.h index 4bccac1f2d..e2c2f6900e 100644 --- a/layout/style/CSSStyleSheet.h +++ b/layout/style/CSSStyleSheet.h @@ -228,7 +228,7 @@ public: /* Get the URI this sheet was originally loaded from, if any. Can return null */ - virtual nsIURI* GetOriginalURI() const; + nsIURI* GetOriginalURI() const { return mInner->mOriginalSheetURI; } // nsICSSLoaderObserver interface NS_IMETHOD StyleSheetLoaded(CSSStyleSheet* aSheet, bool aWasAlternate, @@ -317,6 +317,12 @@ public: } virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + // Changes to sheets should be inside of a WillDirty-DidDirty pair. + // However, the calls do not need to be matched; it's ok to call + // WillDirty and then make no change and skip the DidDirty call. + void WillDirty(); + void DidDirty(); + private: CSSStyleSheet(const CSSStyleSheet& aCopy, CSSStyleSheet* aParentToUse, @@ -332,9 +338,6 @@ protected: void ClearRuleCascades(); - void WillDirty(); - void DidDirty(); - // Return success if the subject principal subsumes the principal of our // inner, error otherwise. This will also succeed if the subject has // UniversalXPConnect or if access is allowed by CORS. In the latter case, diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 153a60ee72..a10c9828be 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -19,9 +19,35 @@ namespace mozilla { namespace css { +NS_IMPL_QUERY_INTERFACE(ImportantStyleData, nsIStyleRule) +NS_IMPL_ADDREF_USING_AGGREGATOR(ImportantStyleData, Declaration()) +NS_IMPL_RELEASE_USING_AGGREGATOR(ImportantStyleData, Declaration()) + +/* virtual */ void +ImportantStyleData::MapRuleInfoInto(nsRuleData* aRuleData) +{ + Declaration()->MapImportantRuleInfoInto(aRuleData); +} + +#ifdef DEBUG +/* virtual */ void +ImportantStyleData::List(FILE* out, int32_t aIndent) const +{ + // Indent + nsAutoCString str; + for (int32_t index = aIndent; --index >= 0; ) { + str.AppendLiteral(" "); + } + + str.AppendLiteral("! important rule\n"); + fprintf_stderr(out, "%s", str.get()); +} +#endif + Declaration::Declaration() : mImmutable(false) { + mContainer.mRaw = uintptr_t(0); } Declaration::Declaration(const Declaration& aCopy) @@ -38,12 +64,37 @@ Declaration::Declaration(const Declaration& aCopy) nullptr), mImmutable(false) { + mContainer.mRaw = uintptr_t(0); } Declaration::~Declaration() { } +NS_INTERFACE_MAP_BEGIN(Declaration) + if (aIID.Equals(NS_GET_IID(mozilla::css::Declaration))) { + *aInstancePtr = this; + NS_ADDREF_THIS(); + return NS_OK; + } + else + NS_INTERFACE_MAP_ENTRY(nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) +NS_INTERFACE_MAP_END + +NS_IMPL_ADDREF(Declaration) +NS_IMPL_RELEASE(Declaration) + +/* virtual */ void +Declaration::MapRuleInfoInto(nsRuleData* aRuleData) +{ + MOZ_ASSERT(mData, "called while expanded"); + mData->MapRuleInfoInto(aRuleData); + if (mVariables) { + mVariables->MapRuleInfoInto(aRuleData); + } +} + void Declaration::ValueAppended(nsCSSProperty aProperty) { @@ -1334,7 +1385,7 @@ Declaration::ToString(nsAString& aString) const } #ifdef DEBUG -void +/* virtual */ void Declaration::List(FILE* out, int32_t aIndent) const { nsAutoCString str; diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index d90e52d2fa..40e04e152d 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -23,22 +23,63 @@ #include "nsCSSDataBlock.h" #include "nsCSSProperty.h" #include "nsCSSProps.h" +#include "nsIStyleRule.h" #include "nsStringFwd.h" #include "nsTArray.h" #include +// feec07b8-3fe6-491e-90d5-cc93f853e048 +#define NS_CSS_DECLARATION_IMPL_CID \ +{ 0xfeec07b8, 0x3fe6, 0x491e, \ + { 0x90, 0xd5, 0xcc, 0x93, 0xf8, 0x53, 0xe0, 0x48 } } + +class nsHTMLCSSStyleSheet; + namespace mozilla { namespace css { +class Rule; +class Declaration; + +/** + * ImportantStyleData is the implementation of nsIStyleRule (a source of + * style data) representing the style data coming from !important rules; + * the !important declarations need a separate nsIStyleRule object since + * they fit at a different point in the cascade. + * + * ImportantStyleData is allocated only as part of a Declaration object. + */ +class ImportantStyleData final : public nsIStyleRule +{ +public: + + NS_DECL_ISUPPORTS + + inline ::mozilla::css::Declaration* Declaration(); + + // nsIStyleRule interface + virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif + +private: + ImportantStyleData() {} + ~ImportantStyleData() {} + + friend class ::mozilla::css::Declaration; +}; + // Declaration objects have unusual lifetime rules. Every declaration // begins life in an invalid state which ends when InitializeEmpty or // CompressFrom is called upon it. After that, it can be attached to // exactly one style rule, and will be destroyed when that style rule -// is destroyed. A declaration becomes immutable when its style rule's -// |RuleMatched| method is called; after that, it must be copied before -// it can be modified, which is taken care of by |EnsureMutable|. +// is destroyed. A declaration becomes immutable (via a SetImmutable +// call) when it is matched (put in the rule tree); after that, it must +// be copied before it can be modified, which is taken care of by +// |EnsureMutable|. -class Declaration { +class Declaration final : public nsIStyleRule { public: /** * Construct an |Declaration| that is in an invalid state (null @@ -49,12 +90,21 @@ public: Declaration(const Declaration& aCopy); - NS_INLINE_DECL_REFCOUNTING(Declaration) + NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_DECLARATION_IMPL_CID) + + NS_DECL_ISUPPORTS private: ~Declaration(); public: + + // nsIStyleRule implementation + virtual void MapRuleInfoInto(nsRuleData *aRuleData) override; +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif + /** * |ValueAppended| must be called to maintain this declaration's * |mOrder| whenever a property is parsed into an expanded data block @@ -170,17 +220,6 @@ public: aExpandedData->Expand(mData.forget(), mImportantData.forget()); } - /** - * Do what |nsIStyleRule::MapRuleInfoInto| needs to do for a style - * rule using this declaration for storage. - */ - void MapNormalRuleInfoInto(nsRuleData *aRuleData) const { - MOZ_ASSERT(mData, "called while expanded"); - mData->MapRuleInfoInto(aRuleData); - if (mVariables) { - mVariables->MapRuleInfoInto(aRuleData); - } - } void MapImportantRuleInfoInto(nsRuleData *aRuleData) const { MOZ_ASSERT(mData, "called while expanded"); MOZ_ASSERT(mImportantData || mImportantVariables, @@ -276,9 +315,43 @@ public: mVariableOrder.Clear(); } -#ifdef DEBUG - void List(FILE* out = stdout, int32_t aIndent = 0) const; -#endif + void SetOwningRule(Rule* aRule) { + MOZ_ASSERT(!mContainer.mOwningRule || !aRule, + "should never overwrite one rule with another"); + mContainer.mOwningRule = aRule; + } + + Rule* GetOwningRule() { + if (mContainer.mRaw & 0x1) { + return nullptr; + } + return mContainer.mOwningRule; + } + + void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aHTMLCSSStyleSheet) { + MOZ_ASSERT(!mContainer.mHTMLCSSStyleSheet || !aHTMLCSSStyleSheet, + "should never overwrite one sheet with another"); + mContainer.mHTMLCSSStyleSheet = aHTMLCSSStyleSheet; + if (aHTMLCSSStyleSheet) { + mContainer.mRaw |= uintptr_t(1); + } + } + + nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() { + if (!(mContainer.mRaw & 0x1)) { + return nullptr; + } + auto c = mContainer; + c.mRaw &= ~uintptr_t(1); + return c.mHTMLCSSStyleSheet; + } + + ImportantStyleData* GetImportantStyleData() { + if (HasImportantData()) { + return &mImportantStyleData; + } + return nullptr; + } private: Declaration& operator=(const Declaration& aCopy) = delete; @@ -355,11 +428,46 @@ private: // may be null nsAutoPtr mImportantVariables; - // set by style rules when |RuleMatched| is called; + union { + // We only ever have one of these since we have an + // nsHTMLCSSStyleSheet only for style attributes, and style + // attributes never have an owning rule. + + // It's an nsHTMLCSSStyleSheet if the low bit is set. + + uintptr_t mRaw; + + // The style rule that owns this declaration. May be null. + Rule* mOwningRule; + + // The nsHTMLCSSStyleSheet that is responsible for this declaration. + // Only non-null for style attributes. + nsHTMLCSSStyleSheet* mHTMLCSSStyleSheet; + } mContainer; + + friend class ImportantStyleData; + ImportantStyleData mImportantStyleData; + + // set when declaration put in the rule tree; // also by ToString (hence the 'mutable'). mutable bool mImmutable; }; +inline ::mozilla::css::Declaration* +ImportantStyleData::Declaration() +{ + union { + char* ch; /* for pointer arithmetic */ + ::mozilla::css::Declaration* declaration; + ImportantStyleData* importantData; + } u; + u.importantData = this; + u.ch -= offsetof(::mozilla::css::Declaration, mImportantStyleData); + return u.declaration; +} + +NS_DEFINE_STATIC_IID_ACCESSOR(Declaration, NS_CSS_DECLARATION_IMPL_CID) + } // namespace css } // namespace mozilla diff --git a/layout/style/FontFace.cpp b/layout/style/FontFace.cpp index fa8b0639a0..89a81a8cec 100644 --- a/layout/style/FontFace.cpp +++ b/layout/style/FontFace.cpp @@ -39,7 +39,10 @@ private: void FontFaceBufferSource::TakeBuffer(uint8_t*& aBuffer, uint32_t& aLength) { + MOZ_ASSERT(mFontFace, "only call TakeBuffer once on a given " + "FontFaceBufferSource object"); mFontFace->TakeBuffer(aBuffer, aLength); + mFontFace = nullptr; } // -- Utility functions ------------------------------------------------------ @@ -471,10 +474,11 @@ FontFace::ParseDescriptor(nsCSSFontDesc aDescID, nsCOMPtr principal = global->PrincipalOrNull(); nsCOMPtr window = do_QueryInterface(mParent); + nsCOMPtr docURI = window->GetDocumentURI(); nsCOMPtr base = window->GetDocBaseURI(); if (!parser.ParseFontFaceDescriptor(aDescID, aString, - nullptr, // aSheetURL + docURI, // aSheetURL base, principal, aResult)) { diff --git a/layout/style/GroupRule.h b/layout/style/GroupRule.h index 7de24066bf..bc39697235 100644 --- a/layout/style/GroupRule.h +++ b/layout/style/GroupRule.h @@ -42,14 +42,12 @@ public: NS_DECL_CYCLE_COLLECTION_CLASS(GroupRule) NS_DECL_CYCLE_COLLECTING_ISUPPORTS - // implement part of nsIStyleRule and Rule + // implement part of Rule DECL_STYLE_RULE_INHERIT_NO_DOMRULE - virtual void SetStyleSheet(CSSStyleSheet* aSheet) override; - - // to help implement nsIStyleRule #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif + virtual void SetStyleSheet(CSSStyleSheet* aSheet) override; public: void AppendStyleRule(Rule* aRule); diff --git a/layout/style/ImportRule.h b/layout/style/ImportRule.h index 2efbf43a09..f0803ba9d0 100644 --- a/layout/style/ImportRule.h +++ b/layout/style/ImportRule.h @@ -34,7 +34,7 @@ private: ImportRule(const ImportRule& aCopy); ~ImportRule(); public: - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportRule, nsIStyleRule) + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportRule, mozilla::css::Rule) NS_DECL_CYCLE_COLLECTING_ISUPPORTS DECL_STYLE_RULE_INHERIT @@ -43,12 +43,10 @@ public: using Rule::GetStyleSheet; // unhide since nsIDOMCSSImportRule has its own GetStyleSheet #endif - // nsIStyleRule methods + // Rule methods #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; diff --git a/layout/style/Loader.cpp b/layout/style/Loader.cpp index 2bf3a648a8..37c7f27ddd 100644 --- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -104,6 +104,12 @@ namespace css { * Data needed to properly load a stylesheet * *********************************************/ +static_assert(eAuthorSheetFeatures == 0 && + eUserSheetFeatures == 1 && + eAgentSheetFeatures == 2, + "sheet parsing mode constants won't fit " + "in SheetLoadData::mParsingMode"); + class SheetLoadData final : public nsIRunnable, public nsIUnicharStreamLoaderObserver, public nsIThreadObserver @@ -137,7 +143,7 @@ public: nsIURI* aURI, CSSStyleSheet* aSheet, bool aSyncLoad, - bool aAllowUnsafeRules, + SheetParsingMode aParsingMode, bool aUseSystemPrincipal, const nsCString& aCharset, nsICSSLoaderObserver* aObserver, @@ -215,9 +221,12 @@ public: // created. bool mWasAlternate : 1; - // mAllowUnsafeRules is true if we should allow unsafe rules to be parsed - // in the loaded sheet. - bool mAllowUnsafeRules : 1; + // mParsingMode controls access to nonstandard style constructs that + // are not safe for use on the public Web but necessary in UA sheets + // and/or useful in user sheets. The only values stored in this + // field are 0, 1, and 2; three bits are allocated to avoid issues + // should the enum type be signed. + SheetParsingMode mParsingMode : 3; // mUseSystemPrincipal is true if the system principal should be used for // this sheet, no matter what the channel principal is. Only true for sync @@ -333,7 +342,7 @@ SheetLoadData::SheetLoadData(Loader* aLoader, mIsCancelled(false), mMustNotify(false), mWasAlternate(aIsAlternate), - mAllowUnsafeRules(false), + mParsingMode(eAuthorSheetFeatures), mUseSystemPrincipal(false), mSheetAlreadyComplete(false), mOwningElement(aOwningElement), @@ -364,7 +373,7 @@ SheetLoadData::SheetLoadData(Loader* aLoader, mIsCancelled(false), mMustNotify(false), mWasAlternate(false), - mAllowUnsafeRules(false), + mParsingMode(eAuthorSheetFeatures), mUseSystemPrincipal(false), mSheetAlreadyComplete(false), mOwningElement(nullptr), @@ -376,7 +385,7 @@ SheetLoadData::SheetLoadData(Loader* aLoader, if (mParentData) { mSyncLoad = mParentData->mSyncLoad; mIsNonDocumentSheet = mParentData->mIsNonDocumentSheet; - mAllowUnsafeRules = mParentData->mAllowUnsafeRules; + mParsingMode = mParentData->mParsingMode; mUseSystemPrincipal = mParentData->mUseSystemPrincipal; ++(mParentData->mPendingChildren); } @@ -389,7 +398,7 @@ SheetLoadData::SheetLoadData(Loader* aLoader, nsIURI* aURI, CSSStyleSheet* aSheet, bool aSyncLoad, - bool aAllowUnsafeRules, + SheetParsingMode aParsingMode, bool aUseSystemPrincipal, const nsCString& aCharset, nsICSSLoaderObserver* aObserver, @@ -407,7 +416,7 @@ SheetLoadData::SheetLoadData(Loader* aLoader, mIsCancelled(false), mMustNotify(false), mWasAlternate(false), - mAllowUnsafeRules(aAllowUnsafeRules), + mParsingMode(aParsingMode), mUseSystemPrincipal(aUseSystemPrincipal), mSheetAlreadyComplete(false), mOwningElement(nullptr), @@ -417,6 +426,10 @@ SheetLoadData::SheetLoadData(Loader* aLoader, mCharsetHint(aCharset) { NS_PRECONDITION(mLoader, "Must have a loader!"); + NS_PRECONDITION(aParsingMode == eAuthorSheetFeatures || + aParsingMode == eUserSheetFeatures || + aParsingMode == eAgentSheetFeatures, + "Unrecognized sheet parsing mode"); NS_POSTCONDITION(!mUseSystemPrincipal || mSyncLoad, "Shouldn't use system principal for async loads"); @@ -1779,7 +1792,7 @@ Loader::ParseSheet(const nsAString& aInput, nsresult rv = parser.ParseSheet(aInput, sheetURI, baseURI, aLoadData->mSheet->Principal(), aLoadData->mLineNumber, - aLoadData->mAllowUnsafeRules); + aLoadData->mParsingMode); mParsingDatas.RemoveElementAt(mParsingDatas.Length() - 1); if (NS_FAILED(rv)) { @@ -2293,14 +2306,16 @@ Loader::LoadChildSheet(CSSStyleSheet* aParentSheet, } nsresult -Loader::LoadSheetSync(nsIURI* aURL, bool aAllowUnsafeRules, +Loader::LoadSheetSync(nsIURI* aURL, + SheetParsingMode aParsingMode, bool aUseSystemPrincipal, CSSStyleSheet** aSheet) { LOG(("css::Loader::LoadSheetSync")); - return InternalLoadNonDocumentSheet(aURL, false, aAllowUnsafeRules, - aUseSystemPrincipal, nullptr, - EmptyCString(), aSheet, nullptr); + return InternalLoadNonDocumentSheet(aURL, + false, aParsingMode, aUseSystemPrincipal, + nullptr, EmptyCString(), + aSheet, nullptr); } nsresult @@ -2312,7 +2327,8 @@ Loader::LoadSheet(nsIURI* aURL, { LOG(("css::Loader::LoadSheet(aURL, aObserver, aSheet) api call")); NS_PRECONDITION(aSheet, "aSheet is null"); - return InternalLoadNonDocumentSheet(aURL, false, false, false, + return InternalLoadNonDocumentSheet(aURL, + false, eAuthorSheetFeatures, false, aOriginPrincipal, aCharset, aSheet, aObserver); } @@ -2328,16 +2344,17 @@ Loader::LoadSheet(nsIURI* aURL, const nsAString& aIntegrity) { LOG(("css::Loader::LoadSheet(aURL, aObserver) api call")); - return InternalLoadNonDocumentSheet(aURL, aIsPreload, false, false, + return InternalLoadNonDocumentSheet(aURL, + aIsPreload, eAuthorSheetFeatures, false, aOriginPrincipal, aCharset, - nullptr, aObserver, aCORSMode, - aReferrerPolicy, aIntegrity); + nullptr, aObserver, + aCORSMode, aReferrerPolicy, aIntegrity); } nsresult Loader::InternalLoadNonDocumentSheet(nsIURI* aURL, bool aIsPreload, - bool aAllowUnsafeRules, + SheetParsingMode aParsingMode, bool aUseSystemPrincipal, nsIPrincipal* aOriginPrincipal, const nsCString& aCharset, @@ -2394,7 +2411,7 @@ Loader::InternalLoadNonDocumentSheet(nsIURI* aURL, } SheetLoadData* data = - new SheetLoadData(this, aURL, sheet, syncLoad, aAllowUnsafeRules, + new SheetLoadData(this, aURL, sheet, syncLoad, aParsingMode, aUseSystemPrincipal, aCharset, aObserver, aOriginPrincipal, mDocument); diff --git a/layout/style/Loader.h b/layout/style/Loader.h index 22a32f4bd2..9d1c321b98 100644 --- a/layout/style/Loader.h +++ b/layout/style/Loader.h @@ -183,6 +183,33 @@ enum StyleSheetState { eSheetComplete }; +/** + * Enum defining the mode in which a sheet is to be parsed. This is + * usually, but not always, the same as the cascade level at which the + * sheet will apply (see nsStyleSet.h). Most of the Loader APIs only + * support loading of author sheets. + * + * Author sheets are the normal case: styles embedded in or linked + * from HTML pages. They are also the most restricted. + * + * User sheets can do anything author sheets can do, and also get + * access to a few CSS extensions that are not yet suitable for + * exposure on the public Web, but are very useful for expressing + * user style overrides, such as @-moz-document rules. + * + * Agent sheets have access to all author- and user-sheet features + * plus more extensions that are necessary for internal use but, + * again, not yet suitable for exposure on the public Web. Some of + * these are outright unsafe to expose; in particular, incorrect + * styling of anonymous box pseudo-elements can violate layout + * invariants. + */ +enum SheetParsingMode { + eAuthorSheetFeatures = 0, + eUserSheetFeatures, + eAgentSheetFeatures +}; + class Loader final { typedef mozilla::net::ReferrerPolicy ReferrerPolicy; @@ -300,13 +327,8 @@ public: * method can be used to load sheets not associated with a document. * * @param aURL the URL of the sheet to load - * @param aEnableUnsafeRules whether unsafe rules are enabled for this - * sheet load - * Unsafe rules are rules that can violate key Gecko invariants if misused. - * In particular, most anonymous box pseudoelements must be very carefully - * styled or we will have severe problems. Therefore unsafe rules should - * never be enabled for stylesheets controlled by untrusted sites; preferably - * unsafe rules should only be enabled for agent sheets. + * @param aParsingMode the mode in which to parse the sheet + * (see comments at enum SheetParsingMode, above). * @param aUseSystemPrincipal if true, give the resulting sheet the system * principal no matter where it's being loaded from. * @param [out] aSheet the loaded, complete sheet. @@ -319,22 +341,25 @@ public: * whether the data could be parsed as CSS and doesn't indicate anything * about the status of child sheets of the returned sheet. */ - nsresult LoadSheetSync(nsIURI* aURL, bool aEnableUnsafeRules, + nsresult LoadSheetSync(nsIURI* aURL, + SheetParsingMode aParsingMode, bool aUseSystemPrincipal, CSSStyleSheet** aSheet); /** - * As above, but aUseSystemPrincipal and aEnableUnsafeRules are assumed false. + * As above, but defaults aParsingMode to eAuthorSheetFeatures and + * aUseSystemPrincipal to false. */ nsresult LoadSheetSync(nsIURI* aURL, CSSStyleSheet** aSheet) { - return LoadSheetSync(aURL, false, false, aSheet); + return LoadSheetSync(aURL, eAuthorSheetFeatures, false, aSheet); } /** * Asynchronously load the stylesheet at aURL. If a successful result is * returned, aObserver is guaranteed to be notified asynchronously once the * sheet is loaded and marked complete. This method can be used to load - * sheets not associated with a document. + * sheets not associated with a document. This method cannot be used to + * load user or agent sheets. * * @param aURL the URL of the sheet to load * @param aOriginPrincipal the principal to use for security checks. This @@ -495,7 +520,7 @@ private: nsresult InternalLoadNonDocumentSheet(nsIURI* aURL, bool aIsPreload, - bool aAllowUnsafeRules, + SheetParsingMode aParsingMode, bool aUseSystemPrincipal, nsIPrincipal* aOriginPrincipal, const nsCString& aCharset, diff --git a/layout/style/NameSpaceRule.h b/layout/style/NameSpaceRule.h index 9ef50d7089..92d910cf5d 100644 --- a/layout/style/NameSpaceRule.h +++ b/layout/style/NameSpaceRule.h @@ -39,14 +39,11 @@ public: NS_DECL_ISUPPORTS + // Rule methods DECL_STYLE_RULE_INHERIT - - // nsIStyleRule methods #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; diff --git a/layout/style/Rule.h b/layout/style/Rule.h index 093543c56d..99c6d01a14 100644 --- a/layout/style/Rule.h +++ b/layout/style/Rule.h @@ -10,7 +10,7 @@ #include "mozilla/CSSStyleSheet.h" #include "mozilla/MemoryReporting.h" -#include "nsIStyleRule.h" +#include "nsISupports.h" #include "nsIDOMCSSRule.h" class nsIDocument; @@ -23,21 +23,20 @@ namespace css { class GroupRule; #define DECL_STYLE_RULE_INHERIT_NO_DOMRULE \ -virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; + /* nothing */ #define DECL_STYLE_RULE_INHERIT \ DECL_STYLE_RULE_INHERIT_NO_DOMRULE \ virtual nsIDOMCSSRule* GetDOMRule() override; \ virtual nsIDOMCSSRule* GetExistingDOMRule() override; -class Rule : public nsIStyleRule { +class Rule : public nsISupports { protected: Rule(uint32_t aLineNumber, uint32_t aColumnNumber) - : mSheet(0), + : mSheet(nullptr), mParentRule(nullptr), mLineNumber(aLineNumber), - mColumnNumber(aColumnNumber), - mWasMatched(false) + mColumnNumber(aColumnNumber) { } @@ -45,8 +44,7 @@ protected: : mSheet(aCopy.mSheet), mParentRule(aCopy.mParentRule), mLineNumber(aCopy.mLineNumber), - mColumnNumber(aCopy.mColumnNumber), - mWasMatched(false) + mColumnNumber(aCopy.mColumnNumber) { } @@ -54,6 +52,10 @@ protected: public: +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const = 0; +#endif + // The constants in this list must maintain the following invariants: // If a rule of type N must appear before a rule of type M in stylesheets // then N < M @@ -78,8 +80,7 @@ public: virtual int32_t GetType() const = 0; - CSSStyleSheet* GetStyleSheet() const; - nsHTMLCSSStyleSheet* GetHTMLCSSStyleSheet() const; + CSSStyleSheet* GetStyleSheet() const { return mSheet; } // Return the document the rule lives in, if any nsIDocument* GetDocument() const @@ -89,9 +90,6 @@ public: } virtual void SetStyleSheet(CSSStyleSheet* aSheet); - // This does not need to be virtual, because GroupRule and MediaRule are not - // used for inline style. - void SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aSheet); void SetParentRule(GroupRule* aRule) { // We don't reference count this up reference. The group rule @@ -131,15 +129,15 @@ public: void* aData); protected: - // This is either a CSSStyleSheet* or an nsHTMLStyleSheet*. The former - // if the low bit is 0, the latter if the low bit is 1. - uintptr_t mSheet; - GroupRule* mParentRule; + // This is sometimes null (e.g., for style attributes). + CSSStyleSheet* mSheet; + // When the parent GroupRule is destroyed, it will call SetParentRule(nullptr) + // on this object. (Through SetParentRuleReference); + GroupRule* MOZ_NON_OWNING_REF mParentRule; // Keep the same type so that MSVC packs them. uint32_t mLineNumber; - uint32_t mColumnNumber : 31; - uint32_t mWasMatched : 1; + uint32_t mColumnNumber; }; } // namespace css diff --git a/layout/style/StyleAnimationValue.cpp b/layout/style/StyleAnimationValue.cpp index dcf04abd3d..a52f0968c1 100644 --- a/layout/style/StyleAnimationValue.cpp +++ b/layout/style/StyleAnimationValue.cpp @@ -2631,8 +2631,9 @@ StyleAnimationValue::ComputeValues( nsCOMArray ruleArray; ruleArray.AppendObject(styleSet->InitialStyleRule()); - ruleArray.AppendObject(aStyleRule); - aStyleRule->RuleMatched(); + css::Declaration* declaration = aStyleRule->GetDeclaration(); + ruleArray.AppendObject(declaration); + declaration->SetImmutable(); tmpStyleContext = styleSet->ResolveStyleByAddingRules(styleContext, ruleArray); if (!tmpStyleContext) { @@ -2658,8 +2659,9 @@ StyleAnimationValue::ComputeValues( // value may have been biased by the 'initial' values supplied. if (!aIsContextSensitive || *aIsContextSensitive) { nsCOMArray ruleArray; - ruleArray.AppendObject(aStyleRule); - aStyleRule->RuleMatched(); + css::Declaration* declaration = aStyleRule->GetDeclaration(); + ruleArray.AppendObject(declaration); + declaration->SetImmutable(); tmpStyleContext = styleSet->ResolveStyleByAddingRules(styleContext, ruleArray); if (!tmpStyleContext) { diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index 973ceba64e..82526ccdd2 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -1028,46 +1028,6 @@ nsCSSSelectorList::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) cons return n; } -// -- ImportantRule ---------------------------------- - -namespace mozilla { -namespace css { - -ImportantRule::ImportantRule(Declaration* aDeclaration) - : mDeclaration(aDeclaration) -{ -} - -ImportantRule::~ImportantRule() -{ -} - -NS_IMPL_ISUPPORTS(ImportantRule, nsIStyleRule) - -/* virtual */ void -ImportantRule::MapRuleInfoInto(nsRuleData* aRuleData) -{ - mDeclaration->MapImportantRuleInfoInto(aRuleData); -} - -#ifdef DEBUG -/* virtual */ void -ImportantRule::List(FILE* out, int32_t aIndent) const -{ - // Indent - nsAutoCString str; - for (int32_t index = aIndent; --index >= 0; ) { - str.AppendLiteral(" "); - } - - str.AppendLiteral("! important rule\n"); - fprintf_stderr(out, "%s", str.get()); -} -#endif - -} // namespace css -} // namespace mozilla - // -------------------------------------------------------- namespace mozilla { @@ -1194,6 +1154,12 @@ css::Declaration* DOMCSSDeclarationImpl::GetCSSDeclaration(Operation aOperation) { if (mRule) { + if (aOperation != eOperation_Read) { + RefPtr sheet = mRule->GetStyleSheet(); + if (sheet) { + sheet->WillDirty(); + } + } return mRule->GetDeclaration(); } else { return nullptr; @@ -1227,26 +1193,21 @@ DOMCSSDeclarationImpl::SetCSSDeclaration(css::Declaration* aDecl) "can only be called when |GetCSSDeclaration| returned a declaration"); nsCOMPtr owningDoc; - nsCOMPtr sheet = mRule->GetStyleSheet(); + RefPtr sheet = mRule->GetStyleSheet(); if (sheet) { owningDoc = sheet->GetOwningDocument(); } mozAutoDocUpdate updateBatch(owningDoc, UPDATE_STYLE, true); - RefPtr oldRule = mRule; - mRule = oldRule->DeclarationChanged(aDecl, true).take(); - if (!mRule) - return NS_ERROR_OUT_OF_MEMORY; - nsrefcnt cnt = mRule->Release(); - if (cnt == 0) { - NS_NOTREACHED("container didn't take ownership"); - mRule = nullptr; - return NS_ERROR_UNEXPECTED; + mRule->SetDeclaration(aDecl); + + if (sheet) { + sheet->DidDirty(); } if (owningDoc) { - owningDoc->StyleRuleChanged(sheet, oldRule, mRule); + owningDoc->StyleRuleChanged(sheet, mRule, mRule); } return NS_OK; } @@ -1410,6 +1371,8 @@ StyleRule::StyleRule(nsCSSSelectorList* aSelector, mDeclaration(aDeclaration) { NS_PRECONDITION(aDeclaration, "must have a declaration"); + + mDeclaration->SetOwningRule(this); } // for |Clone| @@ -1418,39 +1381,20 @@ StyleRule::StyleRule(const StyleRule& aCopy) mSelector(aCopy.mSelector ? aCopy.mSelector->Clone() : nullptr), mDeclaration(new Declaration(*aCopy.mDeclaration)) { + mDeclaration->SetOwningRule(this); // rest is constructed lazily on existing data } -// for |SetCSSDeclaration| -StyleRule::StyleRule(StyleRule& aCopy, - Declaration* aDeclaration) - : Rule(aCopy), - mSelector(aCopy.mSelector), - mDeclaration(aDeclaration), - mDOMRule(aCopy.mDOMRule.forget()) -{ - // The DOM rule is replacing |aCopy| with |this|, so transfer - // the reverse pointer as well (and transfer ownership). - - // Similarly for the selector. - aCopy.mSelector = nullptr; - - // We are probably replacing the old declaration with |aDeclaration| - // instead of taking ownership of the old declaration; only null out - // aCopy.mDeclaration if we are taking ownership. - if (mDeclaration == aCopy.mDeclaration) { - // This should only ever happen if the declaration was modifiable. - mDeclaration->AssertMutable(); - aCopy.mDeclaration = nullptr; - } -} - StyleRule::~StyleRule() { delete mSelector; if (mDOMRule) { mDOMRule->DOMDeclaration()->DropReference(); } + + if (mDeclaration) { + mDeclaration->SetOwningRule(nullptr); + } } // QueryInterface implementation for StyleRule @@ -1461,27 +1405,12 @@ NS_INTERFACE_MAP_BEGIN(StyleRule) return NS_OK; } else - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(StyleRule) NS_IMPL_RELEASE(StyleRule) -void -StyleRule::RuleMatched() -{ - if (!mWasMatched) { - MOZ_ASSERT(!mImportantRule, "should not have important rule yet"); - - mWasMatched = true; - mDeclaration->SetImmutable(); - if (mDeclaration->HasImportantData()) { - mImportantRule = new ImportantRule(mDeclaration); - } - } -} - /* virtual */ int32_t StyleRule::GetType() const { @@ -1516,34 +1445,15 @@ StyleRule::GetExistingDOMRule() return mDOMRule; } -/* virtual */ already_AddRefed -StyleRule::DeclarationChanged(Declaration* aDecl, - bool aHandleContainer) +void +StyleRule::SetDeclaration(Declaration* aDecl) { - RefPtr clone = new StyleRule(*this, aDecl); - - if (aHandleContainer) { - CSSStyleSheet* sheet = GetStyleSheet(); - if (mParentRule) { - if (sheet) { - sheet->ReplaceRuleInGroup(mParentRule, this, clone); - } else { - mParentRule->ReplaceStyleRule(this, clone); - } - } else if (sheet) { - sheet->ReplaceStyleRule(this, clone); - } + if (aDecl == mDeclaration) { + return; } - - return clone.forget(); -} - -/* virtual */ void -StyleRule::MapRuleInfoInto(nsRuleData* aRuleData) -{ - MOZ_ASSERT(mWasMatched, - "somebody forgot to call css::StyleRule::RuleMatched"); - mDeclaration->MapNormalRuleInfoInto(aRuleData); + mDeclaration->SetOwningRule(nullptr); + mDeclaration = aDecl; + mDeclaration->SetOwningRule(this); } #ifdef DEBUG @@ -1642,7 +1552,6 @@ StyleRule::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const // Measurement of the following members may be added later if DMD finds it is // worthwhile: - // - mImportantRule; // - mDOMRule; return n; diff --git a/layout/style/StyleRule.h b/layout/style/StyleRule.h index 5ba5c9a201..2217f732bf 100644 --- a/layout/style/StyleRule.h +++ b/layout/style/StyleRule.h @@ -19,6 +19,7 @@ #include "nsCOMPtr.h" #include "nsCSSPseudoElements.h" #include "nsCSSPseudoClasses.h" +#include "nsIStyleRule.h" class nsIAtom; struct nsCSSSelectorList; @@ -290,28 +291,6 @@ namespace css { class Declaration; class DOMCSSStyleRule; -class StyleRule; - -class ImportantRule final : public nsIStyleRule { -public: - explicit ImportantRule(Declaration *aDeclaration); - - NS_DECL_ISUPPORTS - - // nsIStyleRule interface - virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; -#ifdef DEBUG - virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; -#endif - -protected: - virtual ~ImportantRule(); - - RefPtr mDeclaration; - - friend class StyleRule; -}; - class StyleRule final : public Rule { public: @@ -321,9 +300,6 @@ class StyleRule final : public Rule private: // for |Clone| StyleRule(const StyleRule& aCopy); - // for |DeclarationChanged| - StyleRule(StyleRule& aCopy, - Declaration *aDeclaration); public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_CSS_STYLE_RULE_IMPL_CID) @@ -334,25 +310,7 @@ public: Declaration* GetDeclaration() const { return mDeclaration; } - /** - * Return a new |nsIStyleRule| instance that replaces the current - * one, with |aDecl| replacing the previous declaration. Due to the - * |nsIStyleRule| contract of immutability, this must be called if - * the declaration is modified. - * - * |DeclarationChanged| handles replacing the object in the container - * sheet or group rule if |aHandleContainer| is true. - */ - already_AddRefed - DeclarationChanged(Declaration* aDecl, bool aHandleContainer); - - nsIStyleRule* GetImportantRule() const { return mImportantRule; } - - /** - * The rule processor must call this method before calling - * nsRuleWalker::Forward on this rule during rule matching. - */ - void RuleMatched(); + void SetDeclaration(Declaration* aDecl); // hooks for DOM rule void GetCssText(nsAString& aCssText); @@ -368,9 +326,6 @@ public: virtual nsIDOMCSSRule* GetExistingDOMRule() override; - // The new mapping function. - virtual void MapRuleInfoInto(nsRuleData* aRuleData) override; - #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif @@ -383,7 +338,6 @@ private: private: nsCSSSelectorList* mSelector; // null for style attribute RefPtr mDeclaration; - RefPtr mImportantRule; // initialized by RuleMatched RefPtr mDOMRule; private: diff --git a/layout/style/crashtests/1146101-1.html b/layout/style/crashtests/1146101-1.html new file mode 100644 index 0000000000..e3f8f2aa3f --- /dev/null +++ b/layout/style/crashtests/1146101-1.html @@ -0,0 +1,10 @@ + + + +
diff --git a/layout/style/crashtests/1161366-1.html b/layout/style/crashtests/1161366-1.html new file mode 100644 index 0000000000..d4eacccdc9 --- /dev/null +++ b/layout/style/crashtests/1161366-1.html @@ -0,0 +1,7 @@ + diff --git a/layout/style/crashtests/1164813-1.html b/layout/style/crashtests/1164813-1.html new file mode 100644 index 0000000000..ee5a60ffd6 --- /dev/null +++ b/layout/style/crashtests/1164813-1.html @@ -0,0 +1,33 @@ + + + +
+
Searching
+
+ + diff --git a/layout/style/crashtests/1186768-1.xhtml b/layout/style/crashtests/1186768-1.xhtml new file mode 100644 index 0000000000..22608557df --- /dev/null +++ b/layout/style/crashtests/1186768-1.xhtml @@ -0,0 +1,10 @@ + + + + +
+
+ +
+ + diff --git a/layout/style/crashtests/1206105-1.html b/layout/style/crashtests/1206105-1.html new file mode 100644 index 0000000000..88af39e532 --- /dev/null +++ b/layout/style/crashtests/1206105-1.html @@ -0,0 +1,6 @@ + +crashtest, bug 1206105 + + diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list index 0e729eda87..7456c67879 100644 --- a/layout/style/crashtests/crashtests.list +++ b/layout/style/crashtests/crashtests.list @@ -2,8 +2,8 @@ load 105619-1.html load 147777-1.html load 187671-1.html load 192408-1.html -load 286707-1.html load 285727-1.html +load 286707-1.html load 317561-1.html load 330998-1.html load 363950.html @@ -46,10 +46,6 @@ load 473720-1.html load 473892-1.html load 473914-1.html load 478321-1.xhtml -load 512851-1.xhtml -load 539613-1.xhtml -load 588627-1.html -skip load long-url-list-stack-overflow.html # skipped due to being slow (bug 477490) load 495269-1.html load 495269-2.html load 498036-1.html @@ -62,17 +58,20 @@ load 536789-1.html load 565248-1.html load 558943-1.xhtml load 559491.html +load 565248-1.html load 571105-1.xhtml load 573127-1.html +load 575464-1.html load 580685.html load 585185-1.html +load 588627-1.html load 592698-1.html load 601437-1.html load 601439-1.html load 605689-1.html +load 611922-1.html load 637242.xhtml load 645142.html -load 611922-1.html == 645951-1.html 645951-1-ref.html load 665209-1.html load 671799-1.html @@ -87,8 +86,8 @@ skip-if(browserIsRemote) load 786108-2.html # Bug 795534 load 788836.html load 806310-1.html load 812824.html -load 822842.html load 822766-1.html +load 822842.html load 827591-1.html load 829817.html load 840898.html @@ -96,8 +95,8 @@ load 842134.html load 861489-1.html load 862113.html load 867487.html -load 880862.html load 873222.html +load 880862.html load 915440.html load 927734-1.html load 930270-1.html @@ -111,9 +110,17 @@ load 1028514-1.html load 1066089-1.html load 1074651-1.html pref(dom.webcomponents.enabled,true) load 1089463-1.html -load 1136010-1.html +pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html +pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html load 1153693-1.html +load 1161366-1.html +load 1163446-1.html +load 1164813-1.html load 1167782-1.html +load 1186768-1.xhtml load 1200568-1.html -load large_border_image_width.html +load 1206105-1.html load border-image-visited-link.html +load font-face-truncated-src.html +load large_border_image_width.html +skip load long-url-list-stack-overflow.html # skipped due to being slow (bug 477490) diff --git a/layout/style/html.css b/layout/style/html.css index 931ba6a6eb..f41bf9e6cd 100644 --- a/layout/style/html.css +++ b/layout/style/html.css @@ -830,6 +830,7 @@ marquee[direction="up"], marquee[direction="down"] { rtc, rt { white-space: nowrap; font-size: 50%; + -moz-min-font-size-ratio: 50%; line-height: 1; %ifndef XP_WIN /* The widely-used Windows font Meiryo doesn't work fine with this @@ -854,6 +855,7 @@ marquee[direction="up"], marquee[direction="down"] { } rtc:lang(zh-TW), rt:lang(zh-TW) { font-size: 30%; /* bopomofo */ + -moz-min-font-size-ratio: 30%; } rtc > rt { font-size: inherit; diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index d76e5d3b83..235050fa6e 100644 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -198,7 +198,7 @@ CSSAnimation::QueueEvents() ComputedTiming computedTiming = mEffect->GetComputedTiming(); - if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Null) { + if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Null) { return; // do nothing } @@ -212,23 +212,23 @@ CSSAnimation::QueueEvents() bool wasActive = mPreviousPhaseOrIteration != PREVIOUS_PHASE_BEFORE && mPreviousPhaseOrIteration != PREVIOUS_PHASE_AFTER; bool isActive = - computedTiming.mPhase == ComputedTiming::AnimationPhase_Active; + computedTiming.mPhase == ComputedTiming::AnimationPhase::Active; bool isSameIteration = computedTiming.mCurrentIteration == mPreviousPhaseOrIteration; bool skippedActivePhase = (mPreviousPhaseOrIteration == PREVIOUS_PHASE_BEFORE && - computedTiming.mPhase == ComputedTiming::AnimationPhase_After) || + computedTiming.mPhase == ComputedTiming::AnimationPhase::After) || (mPreviousPhaseOrIteration == PREVIOUS_PHASE_AFTER && - computedTiming.mPhase == ComputedTiming::AnimationPhase_Before); + computedTiming.mPhase == ComputedTiming::AnimationPhase::Before); MOZ_ASSERT(!skippedActivePhase || (!isActive && !wasActive), "skippedActivePhase only makes sense if we were & are inactive"); - if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Before) { + if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Before) { mPreviousPhaseOrIteration = PREVIOUS_PHASE_BEFORE; - } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_Active) { + } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::Active) { mPreviousPhaseOrIteration = computedTiming.mCurrentIteration; - } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase_After) { + } else if (computedTiming.mPhase == ComputedTiming::AnimationPhase::After) { mPreviousPhaseOrIteration = PREVIOUS_PHASE_AFTER; } @@ -275,21 +275,6 @@ CSSAnimation::QueueEvents() this)); } -bool -CSSAnimation::HasEndEventToQueue() const -{ - if (!mEffect) { - return false; - } - - bool wasActive = mPreviousPhaseOrIteration != PREVIOUS_PHASE_BEFORE && - mPreviousPhaseOrIteration != PREVIOUS_PHASE_AFTER; - bool isActive = mEffect->GetComputedTiming().mPhase == - ComputedTiming::AnimationPhase_Active; - - return wasActive && !isActive; -} - CommonAnimationManager* CSSAnimation::GetAnimationManager() const { @@ -357,13 +342,25 @@ nsAnimationManager::MaybeUpdateCascadeResults(AnimationCollection* aCollection) if (anim->IsInEffect() != anim->mInEffectForCascadeResults) { // Update our own cascade results. mozilla::dom::Element* element = aCollection->GetElementToRestyle(); + bool updatedCascadeResults = false; if (element) { nsIFrame* frame = element->GetPrimaryFrame(); if (frame) { UpdateCascadeResults(frame->StyleContext(), aCollection); + updatedCascadeResults = true; } } + if (!updatedCascadeResults) { + // If we don't have a style context we can't do the work of updating + // cascading results but we need to make sure to update + // mInEffectForCascadeResults or else we'll keep running this + // code every time (potentially leading to infinite recursion due + // to the fact that this method both calls and is (indirectly) called + // by nsTransitionManager). + anim->mInEffectForCascadeResults = anim->IsInEffect(); + } + // Notify the transition manager, whose results might depend on ours. mPresContext->TransitionManager()-> UpdateCascadeResultsWithAnimations(aCollection); @@ -549,7 +546,7 @@ nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext, } } collection->mAnimations.SwapElements(newAnimations); - collection->mNeedsRefreshes = true; + collection->mStyleChanging = true; // Cancel removed animations for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) { @@ -607,16 +604,16 @@ public: ResolvedStyleCache() : mCache() {} nsStyleContext* Get(nsPresContext *aPresContext, nsStyleContext *aParentStyleContext, - nsCSSKeyframeRule *aKeyframe); + Declaration* aKeyframeDeclaration); private: - nsRefPtrHashtable, nsStyleContext> mCache; + nsRefPtrHashtable, nsStyleContext> mCache; }; nsStyleContext* ResolvedStyleCache::Get(nsPresContext *aPresContext, nsStyleContext *aParentStyleContext, - nsCSSKeyframeRule *aKeyframe) + Declaration* aKeyframeDeclaration) { // FIXME (spec): The css3-animations spec isn't very clear about how // properties are resolved when they have values that depend on other @@ -626,13 +623,18 @@ ResolvedStyleCache::Get(nsPresContext *aPresContext, // that they're not, since that would prevent us from caching a lot of // data that we'd really like to cache (in particular, the // StyleAnimationValue values in AnimationPropertySegment). - nsStyleContext *result = mCache.GetWeak(aKeyframe); + nsStyleContext *result = mCache.GetWeak(aKeyframeDeclaration); if (!result) { + aKeyframeDeclaration->SetImmutable(); + // The spec says that !important declarations should just be ignored + MOZ_ASSERT(!aKeyframeDeclaration->HasImportantData(), + "Keyframe rule has !important data"); + nsCOMArray rules; - rules.AppendObject(aKeyframe); + rules.AppendObject(aKeyframeDeclaration); RefPtr resultStrong = aPresContext->StyleSet()-> ResolveStyleByAddingRules(aParentStyleContext, rules); - mCache.Put(aKeyframe, resultStrong); + mCache.Put(aKeyframeDeclaration, resultStrong); result = resultStrong; } return result; @@ -795,7 +797,8 @@ nsAnimationManager::BuildAnimations(nsStyleContext* aStyleContext, KeyframeData &toKeyframe = sortedKeyframes[kfIdx]; RefPtr toContext = - resolvedStyles.Get(mPresContext, aStyleContext, toKeyframe.mRule); + resolvedStyles.Get(mPresContext, aStyleContext, + toKeyframe.mRule->Declaration()); if (fromKeyframe) { interpolated = interpolated && diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h index 131d405394..2ffc09a056 100644 --- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -126,7 +126,6 @@ public: void Tick() override; void QueueEvents(); - bool HasEndEventToQueue() const override; bool IsStylePaused() const { return mIsStylePaused; } @@ -339,6 +338,10 @@ public: void StopAnimationsForElement(mozilla::dom::Element* aElement, nsCSSPseudoElements::Type aPseudoType); + bool IsAnimationManager() override { + return true; + } + protected: virtual ~nsAnimationManager() {} @@ -351,9 +354,6 @@ protected: virtual nsIAtom* GetAnimationsAfterAtom() override { return nsGkAtoms::animationsOfAfterProperty; } - virtual bool IsAnimationManager() override { - return true; - } mozilla::DelayedEventDispatcher mEventDispatcher; diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index cc4921eca2..01c8578913 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -306,8 +306,10 @@ CSS_KEY(initial, initial) CSS_KEY(inline, inline) CSS_KEY(inline-axis, inline_axis) CSS_KEY(inline-block, inline_block) +CSS_KEY(inline-end, inline_end) CSS_KEY(inline-flex, inline_flex) CSS_KEY(inline-grid, inline_grid) +CSS_KEY(inline-start, inline_start) CSS_KEY(inline-table, inline_table) CSS_KEY(inset, inset) CSS_KEY(inside, inside) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index b89f3b9787..ef89128055 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -107,6 +107,12 @@ struct CSSParserInputState { bool mHavePushBack; }; +static_assert(eAuthorSheetFeatures == 0 && + eUserSheetFeatures == 1 && + eAgentSheetFeatures == 2, + "sheet parsing mode constants won't fit " + "in CSSParserImpl::mParsingMode"); + // Your basic top-down recursive descent style parser // The exposed methods and members of this class are precisely those // needed by nsCSSParser, far below. @@ -131,14 +137,14 @@ public: nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules, + SheetParsingMode aParsingMode, LoaderReusableStyleSheets* aReusableSheets); - nsresult ParseStyleAttribute(const nsAString& aAttributeValue, + already_AddRefed + ParseStyleAttribute(const nsAString& aAttributeValue, nsIURI* aDocURL, nsIURI* aBaseURL, - nsIPrincipal* aNodePrincipal, - css::StyleRule** aResult); + nsIPrincipal* aNodePrincipal); nsresult ParseDeclarations(const nsAString& aBuffer, nsIURI* aSheetURL, @@ -322,12 +328,20 @@ public: uint32_t aLineNumber, uint32_t aLineOffset); + bool AgentRulesEnabled() const { + return mParsingMode == eAgentSheetFeatures; + } + bool UserRulesEnabled() const { + return mParsingMode == eAgentSheetFeatures || + mParsingMode == eUserSheetFeatures; + } + nsCSSProps::EnabledState PropertyEnabledState() const { static_assert(nsCSSProps::eEnabledForAllContent == 0, "nsCSSProps::eEnabledForAllContent should be zero for " "this bitfield to work"); nsCSSProps::EnabledState enabledState = nsCSSProps::eEnabledForAllContent; - if (mUnsafeRulesEnabled) { + if (AgentRulesEnabled()) { enabledState |= nsCSSProps::eEnabledInUASheets; } if (mIsChrome) { @@ -1302,8 +1316,12 @@ protected: // True when the unitless length quirk applies. bool mUnitlessLengthQuirk : 1; - // True if unsafe rules should be allowed - bool mUnsafeRulesEnabled : 1; + // Controls access to nonstandard style constructs that are not safe + // for use on the public Web but necessary in UA sheets and/or + // useful in user sheets. The only values stored in this field are + // 0, 1, and 2; three bits are allocated to avoid issues should the + // enum type be signed. + SheetParsingMode mParsingMode : 3; // True if we are in parsing rules for the chrome. bool mIsChrome : 1; @@ -1430,7 +1448,7 @@ CSSParserImpl::CSSParserImpl() mNavQuirkMode(false), mHashlessColorQuirk(false), mUnitlessLengthQuirk(false), - mUnsafeRulesEnabled(false), + mParsingMode(eAuthorSheetFeatures), mIsChrome(false), mViewportUnitsEnabled(true), mHTMLMediaMode(false), @@ -1537,7 +1555,7 @@ CSSParserImpl::ParseSheet(const nsAString& aInput, nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules, + SheetParsingMode aParsingMode, LoaderReusableStyleSheets* aReusableSheets) { NS_PRECONDITION(aSheetPrincipal, "Must have principal here!"); @@ -1583,7 +1601,7 @@ CSSParserImpl::ParseSheet(const nsAString& aInput, mSection = eCSSSection_Charset; // sheet is empty, any rules are fair } - mUnsafeRulesEnabled = aAllowUnsafeRules; + mParsingMode = aParsingMode; mIsChrome = nsContentUtils::IsSystemPrincipal(aSheetPrincipal); mReusableSheets = aReusableSheets; @@ -1608,11 +1626,10 @@ CSSParserImpl::ParseSheet(const nsAString& aInput, } ReleaseScanner(); - mUnsafeRulesEnabled = false; + mParsingMode = eAuthorSheetFeatures; mIsChrome = false; mReusableSheets = nullptr; - // XXX check for low level errors return NS_OK; } @@ -1629,12 +1646,11 @@ NonMozillaVendorIdentifier(const nsAString& ident) } -nsresult +already_AddRefed CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue, nsIURI* aDocURI, nsIURI* aBaseURI, - nsIPrincipal* aNodePrincipal, - css::StyleRule** aResult) + nsIPrincipal* aNodePrincipal) { NS_PRECONDITION(aNodePrincipal, "Must have principal here!"); NS_PRECONDITION(aBaseURI, "need base URI"); @@ -1649,17 +1665,10 @@ CSSParserImpl::ParseStyleAttribute(const nsAString& aAttributeValue, uint32_t parseFlags = eParseDeclaration_AllowImportant; RefPtr declaration = ParseDeclarationBlock(parseFlags); - if (declaration) { - // Create a style rule for the declaration - NS_ADDREF(*aResult = new css::StyleRule(nullptr, declaration, 0, 0)); - } else { - *aResult = nullptr; - } ReleaseScanner(); - // XXX check for low level errors - return NS_OK; + return declaration.forget(); } nsresult @@ -5715,7 +5724,7 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, bool pseudoClassIsUserAction = nsCSSPseudoClasses::IsUserActionPseudoClass(pseudoClassType); - if (!mUnsafeRulesEnabled && + if (!AgentRulesEnabled() && ((pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount && nsCSSPseudoElements::PseudoElementIsUASheetOnly(pseudoElementType)) || (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass && @@ -5751,10 +5760,10 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask, bool isPseudoElement = (pseudoElementType < nsCSSPseudoElements::ePseudo_PseudoElementCount); // anonymous boxes are only allowed if they're the tree boxes or we have - // enabled unsafe rules + // enabled agent rules bool isAnonBox = isTreePseudo || (pseudoElementType == nsCSSPseudoElements::ePseudo_AnonBox && - mUnsafeRulesEnabled); + AgentRulesEnabled()); bool isPseudoClass = (pseudoClassType != nsCSSPseudoClasses::ePseudoClass_NotPseudoClass); @@ -16263,24 +16272,22 @@ nsCSSParser::ParseSheet(const nsAString& aInput, nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules, + SheetParsingMode aParsingMode, LoaderReusableStyleSheets* aReusableSheets) { return static_cast(mImpl)-> ParseSheet(aInput, aSheetURI, aBaseURI, aSheetPrincipal, aLineNumber, - aAllowUnsafeRules, aReusableSheets); + aParsingMode, aReusableSheets); } -nsresult +already_AddRefed nsCSSParser::ParseStyleAttribute(const nsAString& aAttributeValue, nsIURI* aDocURI, nsIURI* aBaseURI, - nsIPrincipal* aNodePrincipal, - css::StyleRule** aResult) + nsIPrincipal* aNodePrincipal) { return static_cast(mImpl)-> - ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI, - aNodePrincipal, aResult); + ParseStyleAttribute(aAttributeValue, aDocURI, aBaseURI, aNodePrincipal); } nsresult diff --git a/layout/style/nsCSSParser.h b/layout/style/nsCSSParser.h index 34ad931a1e..ba538d1585 100644 --- a/layout/style/nsCSSParser.h +++ b/layout/style/nsCSSParser.h @@ -9,6 +9,7 @@ #define nsCSSParser_h___ #include "mozilla/Attributes.h" +#include "mozilla/css/Loader.h" #include "nsCSSProperty.h" #include "nsCSSScanner.h" @@ -32,8 +33,6 @@ class CSSVariableValues; namespace css { class Rule; class Declaration; -class Loader; -class LoaderReusableStyleSheets; class StyleRule; } // namespace css } // namespace mozilla @@ -78,8 +77,7 @@ public: * @param aSheetPrincipal the principal of the stylesheet. This must match * the principal of the sheet passed to SetStyleSheet. * @param aLineNumber the line number of the first line of the sheet. - * @param aAllowUnsafeRules see aEnableUnsafeRules in - * mozilla::css::Loader::LoadSheetSync + * @param aParsingMode see SheetParsingMode in css/Loader.h * @param aReusableSheets style sheets that can be reused by an @import. * This can be nullptr. */ @@ -88,18 +86,18 @@ public: nsIURI* aBaseURI, nsIPrincipal* aSheetPrincipal, uint32_t aLineNumber, - bool aAllowUnsafeRules, + mozilla::css::SheetParsingMode aParsingMode, 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 // the declaration. - nsresult ParseStyleAttribute(const nsAString& aAttributeValue, + already_AddRefed + ParseStyleAttribute(const nsAString& aAttributeValue, nsIURI* aDocURL, nsIURI* aBaseURL, - nsIPrincipal* aNodePrincipal, - mozilla::css::StyleRule** aResult); + nsIPrincipal* aNodePrincipal); // Parse the body of a declaration block. Very similar to // ParseStyleAttribute, but used under different circumstances. diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 1a321cbf91..2e7995184d 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -2493,6 +2493,20 @@ CSS_PROP_POSITION( kWidthKTable, offsetof(nsStylePosition, mMaxWidth), eStyleAnimType_Coord) +#ifndef CSS_PROP_LIST_EXCLUDE_INTERNAL +CSS_PROP_FONT( + -moz-min-font-size-ratio, + _moz_min_font_size_ratio, + CSS_PROP_DOMPROP_PREFIXED(MinFontSizeRatio), + CSS_PROPERTY_INTERNAL | + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_ENABLED_IN_UA_SHEETS, + "", + VARIANT_INHERIT | VARIANT_PERCENT, + nullptr, + offsetof(nsStyleFont, mMinFontSizeRatio), + eStyleAnimType_None) +#endif CSS_PROP_POSITION( min-height, min_height, @@ -3656,12 +3670,13 @@ CSS_PROP_TEXT( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | CSS_PROPERTY_APPLIES_TO_PLACEHOLDER | - CSS_PROPERTY_UNITLESS_LENGTH_QUIRK, + CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | + CSS_PROPERTY_STORES_CALC, "", - VARIANT_HL | VARIANT_NORMAL | VARIANT_CALC, + VARIANT_HLP | VARIANT_NORMAL | VARIANT_CALC, nullptr, offsetof(nsStyleText, mWordSpacing), - eStyleAnimType_nscoord) + eStyleAnimType_Coord) CSS_PROP_TEXT( word-wrap, word_wrap, diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 7739e70157..f365a35912 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -994,10 +994,12 @@ const KTableValue nsCSSProps::kCaptionSideKTable[] = { }; const KTableValue nsCSSProps::kClearKTable[] = { - eCSSKeyword_none, NS_STYLE_CLEAR_NONE, - eCSSKeyword_left, NS_STYLE_CLEAR_LEFT, - eCSSKeyword_right, NS_STYLE_CLEAR_RIGHT, - eCSSKeyword_both, NS_STYLE_CLEAR_BOTH, + eCSSKeyword_none, NS_STYLE_CLEAR_NONE, + eCSSKeyword_left, NS_STYLE_CLEAR_LEFT, + eCSSKeyword_right, NS_STYLE_CLEAR_RIGHT, + eCSSKeyword_inline_start, NS_STYLE_CLEAR_INLINE_START, + eCSSKeyword_inline_end, NS_STYLE_CLEAR_INLINE_END, + eCSSKeyword_both, NS_STYLE_CLEAR_BOTH, eCSSKeyword_UNKNOWN,-1 }; @@ -1356,9 +1358,11 @@ const KTableValue nsCSSProps::kJustifyContentKTable[] = { }; const KTableValue nsCSSProps::kFloatKTable[] = { - eCSSKeyword_none, NS_STYLE_FLOAT_NONE, - eCSSKeyword_left, NS_STYLE_FLOAT_LEFT, - eCSSKeyword_right, NS_STYLE_FLOAT_RIGHT, + eCSSKeyword_none, NS_STYLE_FLOAT_NONE, + eCSSKeyword_left, NS_STYLE_FLOAT_LEFT, + eCSSKeyword_right, NS_STYLE_FLOAT_RIGHT, + eCSSKeyword_inline_start, NS_STYLE_FLOAT_INLINE_START, + eCSSKeyword_inline_end, NS_STYLE_FLOAT_INLINE_END, eCSSKeyword_UNKNOWN,-1 }; diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 19c4470453..0bc497eac2 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1561,8 +1561,11 @@ nthChildGenericMatches(Element* aElement, // Integer division in C does truncation (towards 0). So // check that the result is nonnegative, and that there was no // truncation. - const int32_t n = (index - b) / a; - return n >= 0 && (a * n == index - b); + const CheckedInt indexMinusB = CheckedInt(index) - b; + const CheckedInt n = indexMinusB / a; + return n.isValid() && + n.value() >= 0 && + a * n == indexMinusB; } static inline bool @@ -2389,10 +2392,20 @@ nsCSSRuleProcessor::RestrictedSelectorMatches( #define NS_IS_GREEDY_OPERATOR(ch) \ ((ch) == char16_t(' ') || (ch) == char16_t('~')) -static bool SelectorMatchesTree(Element* aPrevElement, - nsCSSSelector* aSelector, - TreeMatchContext& aTreeMatchContext, - bool aLookForRelevantLink) +/** + * Flags for SelectorMatchesTree. + */ +enum SelectorMatchesTreeFlags { + // Whether we still have not found the closest ancestor link element and + // thus have to check the current element for it. + eLookForRelevantLink = 0x1, +}; + +static bool +SelectorMatchesTree(Element* aPrevElement, + nsCSSSelector* aSelector, + TreeMatchContext& aTreeMatchContext, + SelectorMatchesTreeFlags aFlags) { MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement()); nsCSSSelector* selector = aSelector; @@ -2415,7 +2428,7 @@ static bool SelectorMatchesTree(Element* aPrevElement, if (char16_t('+') == selector->mOperator || char16_t('~') == selector->mOperator) { // The relevant link must be an ancestor of the node being matched. - aLookForRelevantLink = false; + aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink); nsIContent* parent = prevElement->GetParent(); if (parent) { if (aTreeMatchContext.mForStyling) @@ -2447,7 +2460,7 @@ static bool SelectorMatchesTree(Element* aPrevElement, if (selector->mOperator == '>' && element->IsActiveChildrenElement()) { Element* styleScope = aTreeMatchContext.mCurrentStyleScope; if (SelectorMatchesTree(element, selector, aTreeMatchContext, - aLookForRelevantLink)) { + aFlags)) { // It matched, don't try matching on the element at // all. return true; @@ -2463,10 +2476,10 @@ static bool SelectorMatchesTree(Element* aPrevElement, if (!element) { return false; } - NodeMatchContext nodeContext(EventStates(), - aLookForRelevantLink && - nsCSSRuleProcessor::IsLink(element)); - if (nodeContext.mIsRelevantLink) { + const bool isRelevantLink = (aFlags & eLookForRelevantLink) && + nsCSSRuleProcessor::IsLink(element); + NodeMatchContext nodeContext(EventStates(), isRelevantLink); + if (isRelevantLink) { // If we find an ancestor of the matched node that is a link // during the matching process, then it's the relevant link (see // constructor call above). @@ -2474,7 +2487,7 @@ static bool SelectorMatchesTree(Element* aPrevElement, // :visited (they'll just fail), we will always find such a node // during the selector matching process if there is a relevant // link that can influence selector matching. - aLookForRelevantLink = false; + aFlags = SelectorMatchesTreeFlags(aFlags & ~eLookForRelevantLink); aTreeMatchContext.SetHaveRelevantLink(); } if (SelectorMatches(element, selector, nodeContext, aTreeMatchContext, @@ -2498,8 +2511,7 @@ static bool SelectorMatchesTree(Element* aPrevElement, // doesn't matter much for performance since most selectors // don't match. (If most did, it might be faster...) Element* styleScope = aTreeMatchContext.mCurrentStyleScope; - if (SelectorMatchesTree(element, selector, aTreeMatchContext, - aLookForRelevantLink)) { + if (SelectorMatchesTree(element, selector, aTreeMatchContext, aFlags)) { return true; } // We want to reset mCurrentStyleScope on aTreeMatchContext @@ -2574,12 +2586,15 @@ void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector, if (SelectorMatches(data->mElement, selector, nodeContext, data->mTreeMatchContext, selectorFlags)) { nsCSSSelector *next = selector->mNext; - if (!next || SelectorMatchesTree(data->mElement, next, - data->mTreeMatchContext, - !nodeContext.mIsRelevantLink)) { - css::StyleRule *rule = value.mRule; - rule->RuleMatched(); - data->mRuleWalker->Forward(rule); + if (!next || + SelectorMatchesTree(data->mElement, next, + data->mTreeMatchContext, + nodeContext.mIsRelevantLink ? + SelectorMatchesTreeFlags(0) : + eLookForRelevantLink)) { + css::Declaration* declaration = value.mRule->GetDeclaration(); + declaration->SetImmutable(); + data->mRuleWalker->Forward(declaration); // nsStyleSet will deal with the !important rule } } @@ -2624,8 +2639,9 @@ nsCSSRuleProcessor::RulesMatching(AnonBoxRuleProcessorData* aData) nsTArray& rules = entry->mRules; for (RuleValue *value = rules.Elements(), *end = value + rules.Length(); value != end; ++value) { - value->mRule->RuleMatched(); - aData->mRuleWalker->Forward(value->mRule); + css::Declaration* declaration = value->mRule->GetDeclaration(); + declaration->SetImmutable(); + aData->mRuleWalker->Forward(declaration); } } } @@ -2750,7 +2766,7 @@ nsCSSRuleProcessor::HasStateDependentStyle(ElementDependentRuleProcessorData* aD aData->mTreeMatchContext, selectorFlags) && SelectorMatchesTree(aData->mElement, selector->mNext, aData->mTreeMatchContext, - false)) + SelectorMatchesTreeFlags(0))) { hint = nsRestyleHint(hint | possibleChange); } @@ -2786,14 +2802,77 @@ nsCSSRuleProcessor::HasDocumentStateDependentStyle(StateRuleProcessorData* aData } struct AttributeEnumData { - explicit AttributeEnumData(AttributeRuleProcessorData *aData) - : data(aData), change(nsRestyleHint(0)) {} + AttributeEnumData(AttributeRuleProcessorData *aData, + RestyleHintData& aRestyleHintData) + : data(aData), change(nsRestyleHint(0)), hintData(aRestyleHintData) {} AttributeRuleProcessorData *data; nsRestyleHint change; + RestyleHintData& hintData; }; +static inline nsRestyleHint +RestyleHintForSelectorWithAttributeChange(nsRestyleHint aCurrentHint, + nsCSSSelector* aSelector, + nsCSSSelector* aRightmostSelector) +{ + MOZ_ASSERT(aSelector); + + char16_t oper = aSelector->mOperator; + + if (oper == char16_t('+') || oper == char16_t('~')) { + return eRestyle_LaterSiblings; + } + + if (oper == char16_t(':')) { + return eRestyle_Subtree; + } + + if (oper != char16_t(0)) { + // Check whether the selector is in a form that supports + // eRestyle_SomeDescendants. If it isn't, return eRestyle_Subtree. + + if (aCurrentHint & eRestyle_Subtree) { + // No point checking, since we'll end up restyling the whole + // subtree anyway. + return eRestyle_Subtree; + } + + if (!aRightmostSelector) { + // aSelector wasn't a top-level selector, which means we were inside + // a :not() or :-moz-any(). We don't support that. + return eRestyle_Subtree; + } + + MOZ_ASSERT(aSelector != aRightmostSelector, + "if aSelector == aRightmostSelector then we should have " + "no operator"); + + // Check that aRightmostSelector can be passed to RestrictedSelectorMatches. + if (!aRightmostSelector->IsRestrictedSelector()) { + return eRestyle_Subtree; + } + + // We also don't support pseudo-elements on any of the selectors + // between aRightmostSelector and aSelector. + // XXX Can we lift this restriction, so that we don't have to loop + // over all the selectors? + for (nsCSSSelector* sel = aRightmostSelector->mNext; + sel != aSelector; + sel = sel->mNext) { + MOZ_ASSERT(sel, "aSelector must be reachable from aRightmostSelector"); + if (sel->PseudoType() != nsCSSPseudoElements::ePseudo_NotPseudoElement) { + return eRestyle_Subtree; + } + } + + return eRestyle_SomeDescendants; + } + + return eRestyle_Self; +} + static void AttributeEnumFunc(nsCSSSelector* aSelector, nsCSSSelector* aRightmostSelector, @@ -2808,18 +2887,27 @@ AttributeEnumFunc(nsCSSSelector* aSelector, return; } - nsRestyleHint possibleChange = RestyleHintForOp(aSelector->mOperator); + nsRestyleHint possibleChange = + RestyleHintForSelectorWithAttributeChange(aData->change, + aSelector, aRightmostSelector); - // If enumData->change already includes all the bits of possibleChange, don't - // bother calling SelectorMatches, since even if it returns false - // enumData->change won't change. + // If, ignoring eRestyle_SomeDescendants, enumData->change already includes + // all the bits of possibleChange, don't bother calling SelectorMatches, since + // even if it returns false enumData->change won't change. If possibleChange + // has eRestyle_SomeDescendants, we need to call SelectorMatches(Tree) + // regardless as it might give us new selectors to append to + // mSelectorsForDescendants. NodeMatchContext nodeContext(EventStates(), false); - if ((possibleChange & ~(aData->change)) && + if (((possibleChange & (~(aData->change) | eRestyle_SomeDescendants))) && SelectorMatches(data->mElement, aSelector, nodeContext, data->mTreeMatchContext, SelectorMatchesFlags::UNKNOWN) && SelectorMatchesTree(data->mElement, aSelector->mNext, - data->mTreeMatchContext, false)) { + data->mTreeMatchContext, + SelectorMatchesTreeFlags(0))) { aData->change = nsRestyleHint(aData->change | possibleChange); + if (possibleChange & eRestyle_SomeDescendants) { + aData->hintData.mSelectorsForDescendants.AppendElement(aRightmostSelector); + } } } @@ -2851,7 +2939,7 @@ nsCSSRuleProcessor::HasAttributeDependentStyle( // We could try making use of aData->mModType, but :not rules make it a bit // of a pain to do so... So just ignore it for now. - AttributeEnumData data(aData); + AttributeEnumData data(aData, aRestyleHintDataResult); // Don't do our special handling of certain attributes if the attr // hasn't changed yet. @@ -3843,7 +3931,8 @@ nsCSSRuleProcessor::SelectorListMatches(Element* aElement, SelectorMatchesFlags::NONE)) { nsCSSSelector* next = sel->mNext; if (!next || - SelectorMatchesTree(aElement, next, aTreeMatchContext, false)) { + SelectorMatchesTree(aElement, next, aTreeMatchContext, + SelectorMatchesTreeFlags(0))) { return true; } } diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index 022c57a7eb..38b4094939 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -19,6 +19,7 @@ #include "mozilla/SheetType.h" #include "mozilla/UniquePtr.h" #include "nsAutoPtr.h" +#include "nsCSSPseudoElements.h" #include "nsExpirationTracker.h" #include "nsIMediaList.h" #include "nsIStyleRuleProcessor.h" diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index edc412bde6..dad92b21e7 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -46,56 +46,22 @@ using namespace mozilla::dom; { return this; } \ /* virtual */ nsIDOMCSSRule* class_::GetExistingDOMRule() \ { return this; } -#define IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(class_, super_) \ -/* virtual */ void class_::MapRuleInfoInto(nsRuleData* aRuleData) \ - { MOZ_ASSERT(false, "should not be called"); } #define IMPL_STYLE_RULE_INHERIT(class_, super_) \ -IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_) \ -IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(class_, super_) +IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(class_, super_) // base class for all rule types in a CSS style sheet namespace mozilla { namespace css { -CSSStyleSheet* -Rule::GetStyleSheet() const -{ - if (!(mSheet & 0x1)) { - return reinterpret_cast(mSheet); - } - - return nullptr; -} - -nsHTMLCSSStyleSheet* -Rule::GetHTMLCSSStyleSheet() const -{ - if (mSheet & 0x1) { - return reinterpret_cast(mSheet & ~uintptr_t(0x1)); - } - - return nullptr; -} - /* virtual */ void Rule::SetStyleSheet(CSSStyleSheet* aSheet) { // We don't reference count this up reference. The style sheet // will tell us when it's going away or when we're detached from // it. - mSheet = reinterpret_cast(aSheet); -} - -void -Rule::SetHTMLCSSStyleSheet(nsHTMLCSSStyleSheet* aSheet) -{ - // We don't reference count this up reference. The style sheet - // will tell us when it's going away or when we're detached from - // it. - mSheet = reinterpret_cast(aSheet); - mSheet |= 0x1; + mSheet = aSheet; } nsresult @@ -248,10 +214,9 @@ NS_IMPL_CYCLE_COLLECTION(ImportRule, mMedia, mChildSheet) // QueryInterface implementation for ImportRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ImportRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSImportRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSImportRule) NS_INTERFACE_MAP_END @@ -429,8 +394,6 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(GroupRule) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupRule) NS_INTERFACE_MAP_END -IMPL_STYLE_RULE_INHERIT_MAP_RULE_INFO_INTO(GroupRule, Rule) - static bool SetStyleSheetReference(Rule* aRule, void* aSheet) { @@ -662,12 +625,11 @@ NS_IMPL_RELEASE_INHERITED(MediaRule, GroupRule) // QueryInterface implementation for MediaRule NS_INTERFACE_MAP_BEGIN(MediaRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSMediaRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSMediaRule) NS_INTERFACE_MAP_END_INHERITING(GroupRule) @@ -880,12 +842,11 @@ NS_IMPL_RELEASE_INHERITED(DocumentRule, GroupRule) // QueryInterface implementation for DocumentRule NS_INTERFACE_MAP_BEGIN(DocumentRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSMozDocumentRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSMozDocumentRule) NS_INTERFACE_MAP_END_INHERITING(GroupRule) @@ -1149,9 +1110,8 @@ NS_INTERFACE_MAP_BEGIN(NameSpaceRule) return NS_OK; } else - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSNameSpaceRule) NS_INTERFACE_MAP_END @@ -1591,10 +1551,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END // QueryInterface implementation for nsCSSFontFaceRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSFontFaceRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSFontFaceRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFaceRule) NS_INTERFACE_MAP_END @@ -1736,10 +1695,9 @@ NS_IMPL_RELEASE(nsCSSFontFeatureValuesRule) // QueryInterface implementation for nsCSSFontFeatureValuesRule NS_INTERFACE_MAP_BEGIN(nsCSSFontFeatureValuesRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFeatureValuesRule) NS_INTERFACE_MAP_END @@ -2038,10 +1996,12 @@ nsCSSKeyframeRule::nsCSSKeyframeRule(const nsCSSKeyframeRule& aCopy) , mKeys(aCopy.mKeys) , mDeclaration(new css::Declaration(*aCopy.mDeclaration)) { + mDeclaration->SetOwningRule(this); } nsCSSKeyframeRule::~nsCSSKeyframeRule() { + mDeclaration->SetOwningRule(nullptr); if (mDOMDeclaration) { mDOMDeclaration->DropReference(); } @@ -2071,29 +2031,14 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END // QueryInterface implementation for nsCSSKeyframeRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSKeyframeRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMMozCSSKeyframeRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCSSKeyframeRule) NS_INTERFACE_MAP_END IMPL_STYLE_RULE_INHERIT_GET_DOM_RULE_WEAK(nsCSSKeyframeRule, Rule) -/* virtual */ void -nsCSSKeyframeRule::MapRuleInfoInto(nsRuleData* aRuleData) -{ - // We need to implement MapRuleInfoInto because the animation manager - // constructs a rule node pointing to us in order to compute the - // styles it needs to animate. - - // The spec says that !important declarations should just be ignored - NS_ASSERTION(!mDeclaration->HasImportantData(), - "Keyframe rules has !important data"); - - mDeclaration->MapNormalRuleInfoInto(aRuleData); -} - #ifdef DEBUG void nsCSSKeyframeRule::List(FILE* out, int32_t aIndent) const @@ -2236,7 +2181,9 @@ nsCSSKeyframeRule::ChangeDeclaration(css::Declaration* aDeclaration) MOZ_AUTO_DOC_UPDATE(doc, UPDATE_STYLE, true); if (aDeclaration != mDeclaration) { + mDeclaration->SetOwningRule(nullptr); mDeclaration = aDeclaration; + mDeclaration->SetOwningRule(this); } CSSStyleSheet* sheet = GetStyleSheet(); @@ -2291,10 +2238,9 @@ NS_IMPL_RELEASE_INHERITED(nsCSSKeyframesRule, css::GroupRule) // QueryInterface implementation for nsCSSKeyframesRule NS_INTERFACE_MAP_BEGIN(nsCSSKeyframesRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) NS_INTERFACE_MAP_ENTRY(nsIDOMMozCSSKeyframesRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozCSSKeyframesRule) NS_INTERFACE_MAP_END_INHERITING(GroupRule) @@ -2596,10 +2542,12 @@ nsCSSPageRule::nsCSSPageRule(const nsCSSPageRule& aCopy) : Rule(aCopy) , mDeclaration(new css::Declaration(*aCopy.mDeclaration)) { + mDeclaration->SetOwningRule(this); } nsCSSPageRule::~nsCSSPageRule() { + mDeclaration->SetOwningRule(nullptr); if (mDOMDeclaration) { mDOMDeclaration->DropReference(); } @@ -2629,10 +2577,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END // QueryInterface implementation for nsCSSPageRule NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsCSSPageRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSPageRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSPageRule) NS_INTERFACE_MAP_END @@ -2705,24 +2652,6 @@ nsCSSPageRule::GetCSSRule() return Rule::GetCSSRule(); } -css::ImportantRule* -nsCSSPageRule::GetImportantRule() -{ - if (!mDeclaration->HasImportantData()) { - return nullptr; - } - if (!mImportantRule) { - mImportantRule = new css::ImportantRule(mDeclaration); - } - return mImportantRule; -} - -/* virtual */ void -nsCSSPageRule::MapRuleInfoInto(nsRuleData* aRuleData) -{ - mDeclaration->MapNormalRuleInfoInto(aRuleData); -} - NS_IMETHODIMP nsCSSPageRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle) { @@ -2736,9 +2665,10 @@ nsCSSPageRule::GetStyle(nsIDOMCSSStyleDeclaration** aStyle) void nsCSSPageRule::ChangeDeclaration(css::Declaration* aDeclaration) { - mImportantRule = nullptr; if (aDeclaration != mDeclaration) { + mDeclaration->SetOwningRule(nullptr); mDeclaration = aDeclaration; + mDeclaration->SetOwningRule(this); } CSSStyleSheet* sheet = GetStyleSheet(); @@ -2818,12 +2748,11 @@ NS_IMPL_RELEASE_INHERITED(CSSSupportsRule, css::GroupRule) // QueryInterface implementation for CSSSupportsRule NS_INTERFACE_MAP_BEGIN(CSSSupportsRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSGroupingRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSConditionRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSSupportsRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSSupportsRule) NS_INTERFACE_MAP_END_INHERITING(GroupRule) @@ -2949,10 +2878,9 @@ NS_IMPL_RELEASE(nsCSSCounterStyleRule) // QueryInterface implementation for nsCSSCounterStyleRule NS_INTERFACE_MAP_BEGIN(nsCSSCounterStyleRule) - NS_INTERFACE_MAP_ENTRY(nsIStyleRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) NS_INTERFACE_MAP_ENTRY(nsIDOMCSSCounterStyleRule) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozilla::css::Rule) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSCounterStyleRule) NS_INTERFACE_MAP_END diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index 3efc67f922..0da583c911 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -55,12 +55,10 @@ public: NS_DECL_ISUPPORTS_INHERITED - // nsIStyleRule methods + // Rule methods #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods virtual void SetStyleSheet(mozilla::CSSStyleSheet* aSheet) override; //override GroupRule virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; @@ -113,12 +111,10 @@ public: NS_DECL_ISUPPORTS_INHERITED - // nsIStyleRule methods + // Rule methods #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; virtual nsIDOMCSSRule* GetDOMRule() override @@ -251,14 +247,11 @@ public: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsCSSFontFaceRule, mozilla::css::Rule) - // nsIStyleRule methods + // Rule methods + DECL_STYLE_RULE_INHERIT #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods - DECL_STYLE_RULE_INHERIT - virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; @@ -319,14 +312,11 @@ public: NS_DECL_ISUPPORTS - // nsIStyleRule methods + // Rule methods + DECL_STYLE_RULE_INHERIT #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods - DECL_STYLE_RULE_INHERIT - virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; @@ -396,21 +386,20 @@ public: , mDeclaration(aDeclaration) { mKeys.SwapElements(aKeys); + mDeclaration->SetOwningRule(this); } private: nsCSSKeyframeRule(const nsCSSKeyframeRule& aCopy); ~nsCSSKeyframeRule(); public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCSSKeyframeRule, nsIStyleRule) - - // nsIStyleRule methods -#ifdef DEBUG - virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; -#endif + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCSSKeyframeRule, mozilla::css::Rule) // Rule methods DECL_STYLE_RULE_INHERIT +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; +#endif virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; @@ -452,12 +441,10 @@ private: public: NS_DECL_ISUPPORTS_INHERITED - // nsIStyleRule methods + // Rule methods #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; virtual nsIDOMCSSRule* GetDOMRule() override @@ -525,8 +512,8 @@ public: uint32_t aLineNumber, uint32_t aColumnNumber) : mozilla::css::Rule(aLineNumber, aColumnNumber) , mDeclaration(aDeclaration) - , mImportantRule(nullptr) { + mDeclaration->SetOwningRule(this); } private: nsCSSPageRule(const nsCSSPageRule& aCopy); @@ -535,13 +522,11 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsCSSPageRule, nsIDOMCSSPageRule) - // nsIStyleRule methods + // Rule methods + DECL_STYLE_RULE_INHERIT #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods - DECL_STYLE_RULE_INHERIT virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; @@ -555,14 +540,11 @@ public: void ChangeDeclaration(mozilla::css::Declaration* aDeclaration); - mozilla::css::ImportantRule* GetImportantRule(); - virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const override; private: RefPtr mDeclaration; // lazily created when needed: RefPtr mDOMDeclaration; - RefPtr mImportantRule; }; namespace mozilla { @@ -575,12 +557,10 @@ public: uint32_t aLineNumber, uint32_t aColumnNumber); CSSSupportsRule(const CSSSupportsRule& aCopy); - // nsIStyleRule methods + // Rule methods #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; virtual bool UseForPresentation(nsPresContext* aPresContext, @@ -638,13 +618,11 @@ private: public: NS_DECL_ISUPPORTS - // nsIStyleRule methods + // Rule methods + DECL_STYLE_RULE_INHERIT #ifdef DEBUG virtual void List(FILE* out = stdout, int32_t aIndent = 0) const override; #endif - - // Rule methods - DECL_STYLE_RULE_INHERIT virtual int32_t GetType() const override; virtual already_AddRefed Clone() const override; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 8ada59c0a2..6656f69760 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -3531,7 +3531,7 @@ CSSValue* nsComputedDOMStyle::DoGetWordSpacing() { nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue; - val->SetAppUnits(StyleText()->mWordSpacing); + SetValueToCoord(val, StyleText()->mWordSpacing, false); return val; } diff --git a/layout/style/nsDOMCSSAttrDeclaration.cpp b/layout/style/nsDOMCSSAttrDeclaration.cpp index cfbbddb6c5..1f8ae7a953 100644 --- a/layout/style/nsDOMCSSAttrDeclaration.cpp +++ b/layout/style/nsDOMCSSAttrDeclaration.cpp @@ -72,20 +72,9 @@ nsresult nsDOMCSSAttributeDeclaration::SetCSSDeclaration(css::Declaration* aDecl) { NS_ASSERTION(mElement, "Must have Element to set the declaration!"); - css::StyleRule* oldRule = - mIsSMILOverride ? mElement->GetSMILOverrideStyleRule() : - mElement->GetInlineStyleRule(); - NS_ASSERTION(oldRule, "Element must have rule"); - - RefPtr newRule = - oldRule->DeclarationChanged(aDecl, false); - if (!newRule) { - return NS_ERROR_OUT_OF_MEMORY; - } - return - mIsSMILOverride ? mElement->SetSMILOverrideStyleRule(newRule, true) : - mElement->SetInlineStyleRule(newRule, nullptr, true); + mIsSMILOverride ? mElement->SetSMILOverrideStyleDeclaration(aDecl, true) : + mElement->SetInlineStyleDeclaration(aDecl, nullptr, true); } nsIDocument* @@ -102,11 +91,11 @@ nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) if (!mElement) return nullptr; - css::StyleRule* cssRule; + css::Declaration* declaration; if (mIsSMILOverride) - cssRule = mElement->GetSMILOverrideStyleRule(); + declaration = mElement->GetSMILOverrideStyleDeclaration(); else - cssRule = mElement->GetInlineStyleRule(); + declaration = mElement->GetInlineStyleDeclaration(); // Notify observers that our style="" attribute is going to change // unless: @@ -122,15 +111,15 @@ nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) // AttributeWillChange if this is inline style. if (!mIsSMILOverride && ((aOperation == eOperation_Modify) || - (aOperation == eOperation_RemoveProperty && cssRule))) { + (aOperation == eOperation_RemoveProperty && declaration))) { nsNodeUtils::AttributeWillChange(mElement, kNameSpaceID_None, nsGkAtoms::style, nsIDOMMutationEvent::MODIFICATION, nullptr); } - if (cssRule) { - return cssRule->GetDeclaration(); + if (declaration) { + return declaration; } if (aOperation != eOperation_Modify) { @@ -140,14 +129,13 @@ nsDOMCSSAttributeDeclaration::GetCSSDeclaration(Operation aOperation) // cannot fail RefPtr decl = new css::Declaration(); decl->InitializeEmpty(); - RefPtr newRule = new css::StyleRule(nullptr, decl, 0, 0); // this *can* fail (inside SetAttrAndNotify, at least). nsresult rv; if (mIsSMILOverride) - rv = mElement->SetSMILOverrideStyleRule(newRule, false); + rv = mElement->SetSMILOverrideStyleDeclaration(decl, false); else - rv = mElement->SetInlineStyleRule(newRule, nullptr, false); + rv = mElement->SetInlineStyleDeclaration(decl, nullptr, false); if (NS_FAILED(rv)) { return nullptr; // the decl will be destroyed along with the style rule diff --git a/layout/style/nsDOMCSSDeclaration.cpp b/layout/style/nsDOMCSSDeclaration.cpp index 4aa08914ae..56ce8319d0 100644 --- a/layout/style/nsDOMCSSDeclaration.cpp +++ b/layout/style/nsDOMCSSDeclaration.cpp @@ -107,7 +107,7 @@ nsDOMCSSDeclaration::SetCssText(const nsAString& aCssText) // to ensure that it exists, or else SetCSSDeclaration may crash. css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify); if (!olddecl) { - return NS_ERROR_FAILURE; + return NS_ERROR_NOT_AVAILABLE; } CSSParsingEnvironment env; @@ -313,7 +313,7 @@ nsDOMCSSDeclaration::ParsePropertyValue(const nsCSSProperty aPropID, { css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify); if (!olddecl) { - return NS_ERROR_FAILURE; + return NS_ERROR_NOT_AVAILABLE; } CSSParsingEnvironment env; @@ -351,7 +351,7 @@ nsDOMCSSDeclaration::ParseCustomPropertyValue(const nsAString& aPropertyName, css::Declaration* olddecl = GetCSSDeclaration(eOperation_Modify); if (!olddecl) { - return NS_ERROR_FAILURE; + return NS_ERROR_NOT_AVAILABLE; } CSSParsingEnvironment env; diff --git a/layout/style/nsHTMLCSSStyleSheet.cpp b/layout/style/nsHTMLCSSStyleSheet.cpp index a8669a6075..101a5cf61d 100644 --- a/layout/style/nsHTMLCSSStyleSheet.cpp +++ b/layout/style/nsHTMLCSSStyleSheet.cpp @@ -29,9 +29,10 @@ ClearAttrCache(const nsAString& aKey, MiscContainer*& aValue, void*) { // Ideally we'd just call MiscContainer::Evict, but we can't do that since // we're iterating the hashtable. - MOZ_ASSERT(aValue->mType == nsAttrValue::eCSSStyleRule); + MOZ_ASSERT(aValue->mType == nsAttrValue::eCSSDeclaration); - aValue->mValue.mCSSStyleRule->SetHTMLCSSStyleSheet(nullptr); + css::Declaration* declaration = aValue->mValue.mCSSDeclaration; + declaration->SetHTMLCSSStyleSheet(nullptr); aValue->mValue.mCached = 0; return PL_DHASH_REMOVE; @@ -65,20 +66,20 @@ nsHTMLCSSStyleSheet::ElementRulesMatching(nsPresContext* aPresContext, nsRuleWalker* aRuleWalker) { // just get the one and only style rule from the content's STYLE attribute - css::StyleRule* rule = aElement->GetInlineStyleRule(); - if (rule) { - rule->RuleMatched(); - aRuleWalker->Forward(rule); + css::Declaration* declaration = aElement->GetInlineStyleDeclaration(); + if (declaration) { + declaration->SetImmutable(); + aRuleWalker->Forward(declaration); } - rule = aElement->GetSMILOverrideStyleRule(); - if (rule) { + declaration = aElement->GetSMILOverrideStyleDeclaration(); + if (declaration) { RestyleManager* restyleManager = aPresContext->RestyleManager(); if (!restyleManager->SkipAnimationRules()) { // Animation restyle (or non-restyle traversal of rules) // Now we can walk SMIL overrride style, without triggering transitions. - rule->RuleMatched(); - aRuleWalker->Forward(rule); + declaration->SetImmutable(); + aRuleWalker->Forward(declaration); } } } @@ -94,10 +95,10 @@ nsHTMLCSSStyleSheet::PseudoElementRulesMatching(Element* aPseudoElement, MOZ_ASSERT(aPseudoElement); // just get the one and only style rule from the content's STYLE attribute - css::StyleRule* rule = aPseudoElement->GetInlineStyleRule(); - if (rule) { - rule->RuleMatched(); - aRuleWalker->Forward(rule); + css::Declaration* declaration = aPseudoElement->GetInlineStyleDeclaration(); + if (declaration) { + declaration->SetImmutable(); + aRuleWalker->Forward(declaration); } } diff --git a/layout/style/nsHTMLCSSStyleSheet.h b/layout/style/nsHTMLCSSStyleSheet.h index 234f3e24cd..b8fdc4c813 100644 --- a/layout/style/nsHTMLCSSStyleSheet.h +++ b/layout/style/nsHTMLCSSStyleSheet.h @@ -13,6 +13,7 @@ #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" +#include "nsCSSPseudoElements.h" #include "nsDataHashtable.h" #include "nsIStyleRuleProcessor.h" diff --git a/layout/style/nsLayoutStylesheetCache.cpp b/layout/style/nsLayoutStylesheetCache.cpp index 82254fe6c7..a3beca5f80 100644 --- a/layout/style/nsLayoutStylesheetCache.cpp +++ b/layout/style/nsLayoutStylesheetCache.cpp @@ -18,7 +18,19 @@ #include "nsIXULRuntime.h" #include "nsPrintfCString.h" +// Includes for the crash report annotation in ErrorLoadingBuiltinSheet. +#ifdef MOZ_CRASHREPORTER +#include "mozilla/Omnijar.h" +#include "nsDirectoryService.h" +#include "nsDirectoryServiceDefs.h" +#include "nsExceptionHandler.h" +#include "nsIChromeRegistry.h" +#include "nsISimpleEnumerator.h" +#include "nsISubstitutingProtocolHandler.h" +#endif + using namespace mozilla; +using namespace mozilla::css; static bool sNumberControlEnabled; @@ -59,7 +71,7 @@ nsLayoutStylesheetCache::ScrollbarsSheet() if (!gStyleCache->mScrollbarsSheet) { // Scrollbars don't need access to unsafe rules LoadSheetURL("chrome://global/skin/scrollbars.css", - gStyleCache->mScrollbarsSheet, false); + gStyleCache->mScrollbarsSheet, eAuthorSheetFeatures); } return gStyleCache->mScrollbarsSheet; @@ -73,7 +85,7 @@ nsLayoutStylesheetCache::FormsSheet() if (!gStyleCache->mFormsSheet) { // forms.css needs access to unsafe rules LoadSheetURL("resource://gre-resources/forms.css", - gStyleCache->mFormsSheet, true); + gStyleCache->mFormsSheet, eAgentSheetFeatures); } return gStyleCache->mFormsSheet; @@ -90,7 +102,7 @@ nsLayoutStylesheetCache::NumberControlSheet() if (!gStyleCache->mNumberControlSheet) { LoadSheetURL("resource://gre-resources/number-control.css", - gStyleCache->mNumberControlSheet, true); + gStyleCache->mNumberControlSheet, eAgentSheetFeatures); } return gStyleCache->mNumberControlSheet; @@ -117,7 +129,7 @@ nsLayoutStylesheetCache::UASheet() if (!gStyleCache->mUASheet) { LoadSheetURL("resource://gre-resources/ua.css", - gStyleCache->mUASheet, true); + gStyleCache->mUASheet, eAgentSheetFeatures); } return gStyleCache->mUASheet; @@ -130,7 +142,7 @@ nsLayoutStylesheetCache::HTMLSheet() if (!gStyleCache->mHTMLSheet) { LoadSheetURL("resource://gre-resources/html.css", - gStyleCache->mHTMLSheet, true); + gStyleCache->mHTMLSheet, eAgentSheetFeatures); } return gStyleCache->mHTMLSheet; @@ -171,7 +183,7 @@ nsLayoutStylesheetCache::MathMLSheet() if (!gStyleCache->mMathMLSheet) { LoadSheetURL("resource://gre-resources/mathml.css", - gStyleCache->mMathMLSheet, true); + gStyleCache->mMathMLSheet, eAgentSheetFeatures); } return gStyleCache->mMathMLSheet; @@ -191,8 +203,14 @@ nsLayoutStylesheetCache::NoScriptSheet() EnsureGlobal(); if (!gStyleCache->mNoScriptSheet) { - LoadSheetURL("resource://gre-resources/noscript.css", - gStyleCache->mNoScriptSheet, true); + // If you update the data: URL, also update noscript.css (See bug 1194856.) + LoadSheetURL( +#ifdef RELEASE_BUILD + "data:text/css,noscript { display%3A none !important%3B }", +#else + "resource://gre-resources/noscript.css", +#endif + gStyleCache->mNoScriptSheet, eAgentSheetFeatures); } return gStyleCache->mNoScriptSheet; @@ -204,8 +222,15 @@ nsLayoutStylesheetCache::NoFramesSheet() EnsureGlobal(); if (!gStyleCache->mNoFramesSheet) { - LoadSheetURL("resource://gre-resources/noframes.css", - gStyleCache->mNoFramesSheet, true); + // If you update the data: URL, also update noframes.css (See bug 1194856.) + LoadSheetURL( +#ifdef RELEASE_BUILD + "data:text/css,noframes { display%3A block%3B } " + "frame%2C frameset%2C iframe { display%3A none !important%3B }", +#else + "resource://gre-resources/noframes.css", +#endif + gStyleCache->mNoFramesSheet, eAgentSheetFeatures); } return gStyleCache->mNoFramesSheet; @@ -244,7 +269,7 @@ nsLayoutStylesheetCache::ContentEditableSheet() if (!gStyleCache->mContentEditableSheet) { LoadSheetURL("resource://gre/res/contenteditable.css", - gStyleCache->mContentEditableSheet, true); + gStyleCache->mContentEditableSheet, eAgentSheetFeatures); } return gStyleCache->mContentEditableSheet; @@ -257,7 +282,7 @@ nsLayoutStylesheetCache::DesignModeSheet() if (!gStyleCache->mDesignModeSheet) { LoadSheetURL("resource://gre/res/designmode.css", - gStyleCache->mDesignModeSheet, true); + gStyleCache->mDesignModeSheet, eAgentSheetFeatures); } return gStyleCache->mDesignModeSheet; @@ -335,15 +360,15 @@ nsLayoutStylesheetCache::nsLayoutStylesheetCache() // And make sure that we load our UA sheets. No need to do this // per-profile, since they're profile-invariant. LoadSheetURL("resource://gre-resources/counterstyles.css", - mCounterStylesSheet, true); + mCounterStylesSheet, eAgentSheetFeatures); LoadSheetURL("chrome://global/content/minimal-xul.css", - mMinimalXULSheet, true); + mMinimalXULSheet, eAgentSheetFeatures); LoadSheetURL("resource://gre-resources/quirk.css", - mQuirkSheet, true); + mQuirkSheet, eAgentSheetFeatures); LoadSheetURL("resource://gre/res/svg.css", - mSVGSheet, true); + mSVGSheet, eAgentSheetFeatures); LoadSheetURL("chrome://global/content/xul.css", - mXULSheet, true); + mXULSheet, eAgentSheetFeatures); // The remaining sheets are created on-demand do to their use being rarer // (which helps save memory for Firefox OS apps) or because they need to @@ -410,25 +435,27 @@ nsLayoutStylesheetCache::InitFromProfile() contentFile->Append(NS_LITERAL_STRING("userContent.css")); chromeFile->Append(NS_LITERAL_STRING("userChrome.css")); - LoadSheetFile(contentFile, mUserContentSheet); - LoadSheetFile(chromeFile, mUserChromeSheet); + LoadSheetFile(contentFile, mUserContentSheet, eUserSheetFeatures); + LoadSheetFile(chromeFile, mUserChromeSheet, eUserSheetFeatures); } /* static */ void nsLayoutStylesheetCache::LoadSheetURL(const char* aURL, RefPtr& aSheet, - bool aEnableUnsafeRules) + SheetParsingMode aParsingMode) { nsCOMPtr uri; NS_NewURI(getter_AddRefs(uri), aURL); - LoadSheet(uri, aSheet, aEnableUnsafeRules); + LoadSheet(uri, aSheet, aParsingMode); if (!aSheet) { NS_ERROR(nsPrintfCString("Could not load %s", aURL).get()); } } void -nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile, RefPtr& aSheet) +nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile, + RefPtr& aSheet, + SheetParsingMode aParsingMode) { bool exists = false; aFile->Exists(&exists); @@ -438,12 +465,252 @@ nsLayoutStylesheetCache::LoadSheetFile(nsIFile* aFile, RefPtr& aS nsCOMPtr uri; NS_NewFileURI(getter_AddRefs(uri), aFile); - LoadSheet(uri, aSheet, false); + LoadSheet(uri, aSheet, aParsingMode); } +#ifdef MOZ_CRASHREPORTER +static void +ListInterestingFiles(nsString& aAnnotation, nsIFile* aFile, + const nsTArray& aInterestingFilenames) +{ + nsString filename; + aFile->GetLeafName(filename); + for (const nsString& interestingFilename : aInterestingFilenames) { + if (interestingFilename == filename) { + nsString path; + aFile->GetPath(path); + aAnnotation.AppendLiteral(" "); + aAnnotation.Append(path); + aAnnotation.AppendLiteral(" ("); + int64_t size; + if (NS_SUCCEEDED(aFile->GetFileSize(&size))) { + aAnnotation.AppendPrintf("%ld", size); + } else { + aAnnotation.AppendLiteral("???"); + } + aAnnotation.AppendLiteral(" bytes)\n"); + return; + } + } + + bool isDir = false; + aFile->IsDirectory(&isDir); + + if (!isDir) { + return; + } + + nsCOMPtr entries; + if (NS_FAILED(aFile->GetDirectoryEntries(getter_AddRefs(entries)))) { + aAnnotation.AppendLiteral(" (failed to enumerated directory)\n"); + return; + } + + for (;;) { + bool hasMore = false; + if (NS_FAILED(entries->HasMoreElements(&hasMore))) { + aAnnotation.AppendLiteral(" (failed during directory enumeration)\n"); + return; + } + if (!hasMore) { + break; + } + + nsCOMPtr entry; + if (NS_FAILED(entries->GetNext(getter_AddRefs(entry)))) { + aAnnotation.AppendLiteral(" (failed during directory enumeration)\n"); + return; + } + + nsCOMPtr file = do_QueryInterface(entry); + if (file) { + ListInterestingFiles(aAnnotation, file, aInterestingFilenames); + } + } +} + +// Generate a crash report annotation to help debug issues with style +// sheets failing to load (bug 1194856). +static void +AnnotateCrashReport(nsIURI* aURI) +{ + nsAutoCString spec; + nsAutoCString scheme; + nsDependentCSubstring filename; + if (aURI) { + aURI->GetSpec(spec); + aURI->GetScheme(scheme); + int32_t i = spec.RFindChar('/'); + if (i != -1) { + filename.Rebind(spec, i + 1); + } + } + + nsString annotation; + + // The URL of the sheet that failed to load. + annotation.AppendLiteral("Error loading sheet: "); + annotation.Append(NS_ConvertUTF8toUTF16(spec).get()); + annotation.Append('\n'); + + // The jar: or file: URL that the sheet's resource: or chrome: URL + // resolves to. + if (scheme.EqualsLiteral("resource")) { + annotation.AppendLiteral("Real location: "); + nsCOMPtr handler; + nsCOMPtr io(do_GetIOService()); + if (io) { + nsCOMPtr ph; + io->GetProtocolHandler(scheme.get(), getter_AddRefs(ph)); + if (ph) { + handler = do_QueryInterface(ph); + } + } + if (!handler) { + annotation.AppendLiteral("(ResolveURI failed)\n"); + } else { + nsAutoCString resolvedSpec; + handler->ResolveURI(aURI, resolvedSpec); + annotation.Append(NS_ConvertUTF8toUTF16(resolvedSpec)); + annotation.Append('\n'); + } + } else if (scheme.EqualsLiteral("chrome")) { + annotation.AppendLiteral("Real location: "); + nsCOMPtr reg = + mozilla::services::GetChromeRegistryService(); + if (!reg) { + annotation.AppendLiteral("(no chrome registry)\n"); + } else { + nsCOMPtr resolvedURI; + reg->ConvertChromeURL(aURI, getter_AddRefs(resolvedURI)); + if (!resolvedURI) { + annotation.AppendLiteral("(ConvertChromeURL failed)\n"); + } else { + nsAutoCString resolvedSpec; + resolvedURI->GetSpec(resolvedSpec); + annotation.Append(NS_ConvertUTF8toUTF16(resolvedSpec)); + annotation.Append('\n'); + } + } + } + + nsTArray interestingFiles; + interestingFiles.AppendElement(NS_LITERAL_STRING("chrome.manifest")); + interestingFiles.AppendElement(NS_LITERAL_STRING("omni.ja")); + interestingFiles.AppendElement(NS_ConvertUTF8toUTF16(filename)); + + annotation.AppendLiteral("GRE directory: "); + nsCOMPtr file; + nsDirectoryService::gService->Get(NS_GRE_DIR, NS_GET_IID(nsIFile), + getter_AddRefs(file)); + if (file) { + // The Firefox installation directory. + nsString path; + file->GetPath(path); + annotation.Append(path); + annotation.Append('\n'); + + // List interesting files -- any chrome.manifest or omni.ja file or any file + // whose name is the sheet's filename -- under the Firefox installation + // directory. + annotation.AppendLiteral("Interesting files in the GRE directory:\n"); + ListInterestingFiles(annotation, file, interestingFiles); + + // If the Firefox installation directory has a chrome.manifest file, let's + // see what's in it. + file->Append(NS_LITERAL_STRING("chrome.manifest")); + bool exists = false; + file->Exists(&exists); + if (exists) { + annotation.AppendLiteral("Contents of chrome.manifest:\n[[[\n"); + PRFileDesc* fd; + if (NS_SUCCEEDED(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd))) { + nsCString contents; + char buf[512]; + int32_t n; + while ((n = PR_Read(fd, buf, sizeof(buf))) > 0) { + contents.Append(buf, n); + } + if (n < 0) { + annotation.AppendLiteral(" (error while reading)\n"); + } else { + annotation.Append(NS_ConvertUTF8toUTF16(contents)); + } + PR_Close(fd); + } + annotation.AppendLiteral("]]]\n"); + } + } else { + annotation.AppendLiteral("(none)\n"); + } + + // The jar: or file: URL prefix that chrome: and resource: URLs get translated + // to. + annotation.AppendLiteral("GRE omnijar URI string: "); + nsCString uri; + nsresult rv = Omnijar::GetURIString(Omnijar::GRE, uri); + if (NS_FAILED(rv)) { + annotation.AppendLiteral("(failed)\n"); + } else { + annotation.Append(NS_ConvertUTF8toUTF16(uri)); + annotation.Append('\n'); + } + + RefPtr zip = Omnijar::GetReader(Omnijar::GRE); + if (zip) { + // List interesting files in the GRE omnijar. + annotation.AppendLiteral("Interesting files in the GRE omnijar:\n"); + nsZipFind* find; + rv = zip->FindInit(nullptr, &find); + if (NS_FAILED(rv)) { + annotation.AppendPrintf(" (FindInit failed with 0x%08x)\n", rv); + } else if (!find) { + annotation.AppendLiteral(" (FindInit returned null)\n"); + } else { + const char* result; + uint16_t len; + while (NS_SUCCEEDED(find->FindNext(&result, &len))) { + nsCString itemPathname; + nsString itemFilename; + itemPathname.Append(result, len); + int32_t i = itemPathname.RFindChar('/'); + if (i != -1) { + itemFilename = NS_ConvertUTF8toUTF16(Substring(itemPathname, i + 1)); + } + for (const nsString& interestingFile : interestingFiles) { + if (interestingFile == itemFilename) { + annotation.AppendLiteral(" "); + annotation.Append(NS_ConvertUTF8toUTF16(itemPathname)); + nsZipItem* item = zip->GetItem(itemPathname.get()); + if (!item) { + annotation.AppendLiteral(" (GetItem failed)\n"); + } else { + annotation.AppendPrintf(" (%d bytes, crc32 = 0x%08x)\n", + item->RealSize(), + item->CRC32()); + } + break; + } + } + } + delete find; + } + } else { + annotation.AppendLiteral("No GRE omnijar\n"); + } + + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SheetLoadFailure"), + NS_ConvertUTF16toUTF8(annotation)); +} +#endif + static void ErrorLoadingBuiltinSheet(nsIURI* aURI, const char* aMsg) { +#ifdef MOZ_CRASHREPORTER + AnnotateCrashReport(aURI); +#endif + nsAutoCString spec; if (aURI) { aURI->GetSpec(spec); @@ -455,7 +722,7 @@ ErrorLoadingBuiltinSheet(nsIURI* aURI, const char* aMsg) void nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI, RefPtr& aSheet, - bool aEnableUnsafeRules) + SheetParsingMode aParsingMode) { if (!aURI) { ErrorLoadingBuiltinSheet(aURI, "null URI"); @@ -472,7 +739,7 @@ nsLayoutStylesheetCache::LoadSheet(nsIURI* aURI, } - nsresult rv = gCSSLoader->LoadSheetSync(aURI, aEnableUnsafeRules, true, + nsresult rv = gCSSLoader->LoadSheetSync(aURI, aParsingMode, true, getter_AddRefs(aSheet)); if (NS_FAILED(rv)) { ErrorLoadingBuiltinSheet(aURI, diff --git a/layout/style/nsLayoutStylesheetCache.h b/layout/style/nsLayoutStylesheetCache.h index 9aebc35468..86c50b5f3d 100644 --- a/layout/style/nsLayoutStylesheetCache.h +++ b/layout/style/nsLayoutStylesheetCache.h @@ -13,15 +13,13 @@ #include "mozilla/Attributes.h" #include "mozilla/MemoryReporting.h" #include "mozilla/StaticPtr.h" +#include "mozilla/css/Loader.h" class nsIFile; class nsIURI; namespace mozilla { class CSSStyleSheet; -namespace css { -class Loader; -} // namespace css } // namespace mozilla class nsLayoutStylesheetCache final @@ -69,11 +67,12 @@ private: void InitMemoryReporter(); static void LoadSheetURL(const char* aURL, RefPtr& aSheet, - bool aEnableUnsafeRules); + mozilla::css::SheetParsingMode aParsingMode); static void LoadSheetFile(nsIFile* aFile, - RefPtr& aSheet); + RefPtr& aSheet, + mozilla::css::SheetParsingMode aParsingMode); static void LoadSheet(nsIURI* aURI, RefPtr& aSheet, - bool aEnableUnsafeRules); + mozilla::css::SheetParsingMode aParsingMode); static void InvalidateSheet(RefPtr& aSheet); static void DependentPrefChanged(const char* aPref, void* aData); void BuildPreferenceSheet(RefPtr& aSheet, diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 6130c8b2ff..4f9a4990f5 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -17,6 +17,8 @@ #include "mozilla/Likely.h" #include "mozilla/LookAndFeel.h" +#include "mozilla/css/Declaration.h" + #include "nsAlgorithm.h" // for clamped() #include "nsRuleNode.h" #include "nscore.h" @@ -50,6 +52,7 @@ #include "nsCSSPropertySet.h" #include "mozilla/RuleNodeCacheConditions.h" #include "nsDeviceContext.h" +#include "nsQueryObject.h" #if defined(_MSC_VER) || defined(__MINGW32__) #include @@ -1553,6 +1556,14 @@ nsRuleNode* nsRuleNode::Transition(nsIStyleRule* aRule, SheetType aLevel, bool aIsImportantRule) { +#ifdef DEBUG + { + RefPtr declaration(do_QueryObject(aRule)); + MOZ_ASSERT(!declaration || !declaration->IsMutable(), + "caller must call Declaration::SetImmutable first"); + } +#endif + nsRuleNode* next = nullptr; nsRuleNode::Key key(aRule, aLevel, aIsImportantRule); @@ -2629,9 +2640,9 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex nsStyle##type_* data_ = nullptr; \ mozilla::Maybe maybeFakeParentData; \ const nsStyle##type_* parentdata_ = nullptr; \ - RuleNodeCacheConditions conditions = aConditions; \ + RuleNodeCacheConditions conditions = aConditions; \ \ - /* If |conditions.Cacheable()| might be true by the time we're done, we */ \ + /* If |conditions.Cacheable()| might be true by the time we're done, we */ \ /* can't call parentContext->Style##type_() since it could recur into */ \ /* setting the same struct on the same rule node, causing a leak. */ \ if (aRuleDetail != eRuleFullReset && \ @@ -2658,7 +2669,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex if (aRuleDetail != eRuleFullMixed && aRuleDetail != eRuleFullReset) { \ /* No question. We will have to inherit. Go ahead and init */ \ /* with inherited vals from parent. */ \ - conditions.SetUncacheable(); \ + conditions.SetUncacheable(); \ if (parentdata_) \ data_ = new (mPresContext) nsStyle##type_(*parentdata_); \ else \ @@ -2702,7 +2713,7 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex else \ data_ = new (mPresContext) nsStyle##type_ ctorargs_; \ \ - /* If |conditions.Cacheable()| might be true by the time we're done, we */ \ + /* If |conditions.Cacheable()| might be true by the time we're done, we */ \ /* can't call parentContext->Style##type_() since it could recur into */ \ /* setting the same struct on the same rule node, causing a leak. */ \ mozilla::Maybe maybeFakeParentData; \ @@ -2726,13 +2737,13 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_INHERITED(type_, data_) \ - NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ + NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ aRuleDetail == eRuleFullReset || \ (aStartStruct && aRuleDetail == eRulePartialReset), \ - "conditions.CacheableWithoutDependencies() must be false " \ + "conditions.CacheableWithoutDependencies() must be false " \ "for inherited structs unless all properties have been " \ "specified with values other than inherit"); \ - if (conditions.CacheableWithoutDependencies()) { \ + if (conditions.CacheableWithoutDependencies()) { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ if (!aHighestNode->mStyleData.mInheritedData) { \ @@ -2761,14 +2772,14 @@ nsRuleNode::SetDefaultOnRoot(const nsStyleStructID aSID, nsStyleContext* aContex * @param data_ Variable holding the result of this function. */ #define COMPUTE_END_RESET(type_, data_) \ - NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ + NS_POSTCONDITION(!conditions.CacheableWithoutDependencies() || \ aRuleDetail == eRuleNone || \ aRuleDetail == eRulePartialReset || \ aRuleDetail == eRuleFullReset, \ - "conditions.CacheableWithoutDependencies() must be false " \ + "conditions.CacheableWithoutDependencies() must be false " \ "for reset structs if any properties were specified as " \ "inherit"); \ - if (conditions.CacheableWithoutDependencies()) { \ + if (conditions.CacheableWithoutDependencies()) { \ /* We were fully specified and can therefore be cached right on the */ \ /* rule node. */ \ if (!aHighestNode->mStyleData.mResetData) { \ @@ -3798,6 +3809,36 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext, languageOverrideValue->GetStringValue(aFont->mFont.languageOverride); } + // -moz-min-font-size-ratio: percent, inherit + const nsCSSValue* minFontSizeRatio = aRuleData->ValueForMinFontSizeRatio(); + switch (minFontSizeRatio->GetUnit()) { + case eCSSUnit_Null: + break; + case eCSSUnit_Unset: + case eCSSUnit_Inherit: + aFont->mMinFontSizeRatio = aParentFont->mMinFontSizeRatio; + aConditions.SetUncacheable(); + break; + case eCSSUnit_Initial: + aFont->mMinFontSizeRatio = 100; // 100% + break; + case eCSSUnit_Percent: { + // While percentages are parsed as floating point numbers, we + // only store an integer in the range [0, 255] since that's all + // we need for now. + float percent = minFontSizeRatio->GetPercentValue() * 100; + if (percent < 0) { + percent = 0; + } else if (percent > 255) { + percent = 255; + } + aFont->mMinFontSizeRatio = uint8_t(percent); + break; + } + default: + MOZ_ASSERT_UNREACHABLE("Unknown unit for -moz-min-font-size-ratio"); + } + // font-size: enum, length, percent, inherit nscoord scriptLevelAdjustedParentSize = aParentFont->mSize; nscoord scriptLevelAdjustedUnconstrainedParentSize; @@ -3806,24 +3847,50 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext, &scriptLevelAdjustedUnconstrainedParentSize); NS_ASSERTION(!aUsedStartStruct || aFont->mScriptUnconstrainedSize == aFont->mSize, "If we have a start struct, we should have reset everything coming in here"); + + // Compute whether we're affected by scriptMinSize *before* calling + // SetFontSize, since aParentFont might be the same as aFont. If it + // is, calling SetFontSize might throw off our calculation. + bool affectedByScriptMinSize = + aParentFont->mSize != aParentFont->mScriptUnconstrainedSize || + scriptLevelAdjustedParentSize != + scriptLevelAdjustedUnconstrainedParentSize; + SetFontSize(aPresContext, aRuleData, aFont, aParentFont, &aFont->mSize, systemFont, aParentFont->mSize, scriptLevelAdjustedParentSize, aUsedStartStruct, atRoot, aConditions); - if (aParentFont->mSize == aParentFont->mScriptUnconstrainedSize && - scriptLevelAdjustedParentSize == scriptLevelAdjustedUnconstrainedParentSize) { + if (!aPresContext->Document()->GetMathMLEnabled()) { + MOZ_ASSERT(!affectedByScriptMinSize); + // If MathML is not enabled, we don't need to mark this node as + // uncacheable. If it becomes enabled, code in + // nsMathMLElementFactory will rebuild the rule tree and style data + // when MathML is first enabled (see nsMathMLElement::BindToTree). + aFont->mScriptUnconstrainedSize = aFont->mSize; + } else if (!affectedByScriptMinSize) { // Fast path: we have not been affected by scriptminsize so we don't // need to call SetFontSize again to compute the // scriptminsize-unconstrained size. This is OK even if we have a // start struct, because if we have a start struct then 'font-size' // was specified and so scriptminsize has no effect. aFont->mScriptUnconstrainedSize = aFont->mSize; + // It's possible we could, in the future, have a different parent, + // which would lead to a different affectedByScriptMinSize. + aConditions.SetUncacheable(); } else { + // see previous else-if + aConditions.SetUncacheable(); + + // Use a separate conditions object because it might get a + // *different* font-size dependency. We can ignore it because we've + // already called SetUncacheable. + RuleNodeCacheConditions unconstrainedConditions; + SetFontSize(aPresContext, aRuleData, aFont, aParentFont, &aFont->mScriptUnconstrainedSize, systemFont, aParentFont->mScriptUnconstrainedSize, scriptLevelAdjustedUnconstrainedParentSize, - aUsedStartStruct, atRoot, aConditions); + aUsedStartStruct, atRoot, unconstrainedConditions); } NS_ASSERTION(aFont->mScriptUnconstrainedSize <= aFont->mSize, "scriptminsize should never be making things bigger"); @@ -3836,6 +3903,8 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext, nscoord minFontSize = aPresContext->MinFontSize(aFont->mLanguage); if (minFontSize < 0) { minFontSize = 0; + } else { + minFontSize = (minFontSize * aFont->mMinFontSizeRatio) / 100; } if (fontSize < minFontSize && !aPresContext->IsChrome()) { // override the minimum font-size constraint @@ -4341,25 +4410,17 @@ nsRuleNode::ComputeTextData(void* aStartStruct, parentText->mWordBreak, NS_STYLE_WORDBREAK_NORMAL, 0, 0, 0, 0); - // word-spacing: normal, length, inherit - nsStyleCoord tempCoord; + // word-spacing: normal, length, percent, inherit const nsCSSValue* wordSpacingValue = aRuleData->ValueForWordSpacing(); - if (SetCoord(*wordSpacingValue, tempCoord, - nsStyleCoord(parentText->mWordSpacing, - nsStyleCoord::CoordConstructor), - SETCOORD_LH | SETCOORD_NORMAL | SETCOORD_INITIAL_NORMAL | - SETCOORD_CALC_LENGTH_ONLY | SETCOORD_UNSET_INHERIT, - aContext, mPresContext, conditions)) { - if (tempCoord.GetUnit() == eStyleUnit_Coord) { - text->mWordSpacing = tempCoord.GetCoordValue(); - } else if (tempCoord.GetUnit() == eStyleUnit_Normal) { - text->mWordSpacing = 0; - } else { - NS_NOTREACHED("unexpected unit"); - } + if (wordSpacingValue->GetUnit() == eCSSUnit_Normal) { + // Do this so that "normal" computes to 0px, as the CSS 2.1 spec requires. + text->mWordSpacing.SetCoordValue(0); } else { - NS_ASSERTION(wordSpacingValue->GetUnit() == eCSSUnit_Null, - "unexpected unit"); + SetCoord(*aRuleData->ValueForWordSpacing(), + text->mWordSpacing, parentText->mWordSpacing, + SETCOORD_LPH | SETCOORD_INITIAL_ZERO | + SETCOORD_STORE_CALC | SETCOORD_UNSET_INHERIT, + aContext, mPresContext, conditions); } // word-wrap: enum, inherit, initial @@ -5646,6 +5707,21 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, display->mOverflowY = NS_STYLE_OVERFLOW_AUTO; } + // When 'contain: paint', update overflow from 'visible' to 'clip'. + if (display->IsContainPaint()) { + // XXX This actually sets overflow-[x|y] to -moz-hidden-unscrollable. + if (display->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE) { + // This uncacheability (and the one below) could be fixed by adding + // mOriginalOverflowX and mOriginalOverflowY fields, if necessary. + display->mOverflowX = NS_STYLE_OVERFLOW_CLIP; + conditions.SetUncacheable(); + } + if (display->mOverflowY == NS_STYLE_OVERFLOW_VISIBLE) { + display->mOverflowY = NS_STYLE_OVERFLOW_CLIP; + conditions.SetUncacheable(); + } + } + SetDiscrete(*aRuleData->ValueForOverflowClipBox(), display->mOverflowClipBox, conditions, SETDSC_ENUMERATED | SETDSC_UNSET_INITIAL, @@ -5787,6 +5863,18 @@ nsRuleNode::ComputeDisplayData(void* aStartStruct, // mOriginalDisplay, which we have carefully not changed. } + if (display->IsContainPaint()) { + // An element with contain:paint or contain:layout needs to "be a + // formatting context". For the purposes of the "display" property, that + // just means we need to promote "display:inline" to "inline-block". + // XXX We may also need to promote ruby display vals; see bug 1179349. + + // It's okay to cache this change in the rule tree for the same + // reasons as floats in the previous condition. + if (display->mDisplay == NS_STYLE_DISPLAY_INLINE) { + display->mDisplay = NS_STYLE_DISPLAY_INLINE_BLOCK; + } + } } /* Convert the nsCSSValueList into an nsTArray. */ @@ -7702,23 +7790,13 @@ nsRuleNode::ComputePositionData(void* aStartStruct, // block-axis properties, we turn them into unset if we find them in // that case. - bool vertical; - switch (aContext->StyleVisibility()->mWritingMode) { - default: - MOZ_ASSERT(false, "unexpected writing-mode value"); - // fall through - case NS_STYLE_WRITING_MODE_HORIZONTAL_TB: - vertical = false; - break; - case NS_STYLE_WRITING_MODE_VERTICAL_RL: - case NS_STYLE_WRITING_MODE_VERTICAL_LR: - case NS_STYLE_WRITING_MODE_SIDEWAYS_RL: - case NS_STYLE_WRITING_MODE_SIDEWAYS_LR: - vertical = true; - break; - } + WritingMode wm(aContext); + bool vertical = wm.IsVertical(); const nsCSSValue* width = aRuleData->ValueForWidth(); + if (width->GetUnit() == eCSSUnit_Enumerated) { + conditions.SetWritingModeDependency(wm.GetBits()); + } SetCoord(width->GetUnit() == eCSSUnit_Enumerated && vertical ? nsCSSValue(eCSSUnit_Unset) : *width, pos->mWidth, parentPos->mWidth, @@ -7727,6 +7805,9 @@ nsRuleNode::ComputePositionData(void* aStartStruct, aContext, mPresContext, conditions); const nsCSSValue* minWidth = aRuleData->ValueForMinWidth(); + if (minWidth->GetUnit() == eCSSUnit_Enumerated) { + conditions.SetWritingModeDependency(wm.GetBits()); + } SetCoord(minWidth->GetUnit() == eCSSUnit_Enumerated && vertical ? nsCSSValue(eCSSUnit_Unset) : *minWidth, pos->mMinWidth, parentPos->mMinWidth, @@ -7735,6 +7816,9 @@ nsRuleNode::ComputePositionData(void* aStartStruct, aContext, mPresContext, conditions); const nsCSSValue* maxWidth = aRuleData->ValueForMaxWidth(); + if (maxWidth->GetUnit() == eCSSUnit_Enumerated) { + conditions.SetWritingModeDependency(wm.GetBits()); + } SetCoord(maxWidth->GetUnit() == eCSSUnit_Enumerated && vertical ? nsCSSValue(eCSSUnit_Unset) : *maxWidth, pos->mMaxWidth, parentPos->mMaxWidth, @@ -7743,6 +7827,9 @@ nsRuleNode::ComputePositionData(void* aStartStruct, aContext, mPresContext, conditions); const nsCSSValue* height = aRuleData->ValueForHeight(); + if (height->GetUnit() == eCSSUnit_Enumerated) { + conditions.SetWritingModeDependency(wm.GetBits()); + } SetCoord(height->GetUnit() == eCSSUnit_Enumerated && !vertical ? nsCSSValue(eCSSUnit_Unset) : *height, pos->mHeight, parentPos->mHeight, @@ -7751,6 +7838,9 @@ nsRuleNode::ComputePositionData(void* aStartStruct, aContext, mPresContext, conditions); const nsCSSValue* minHeight = aRuleData->ValueForMinHeight(); + if (minHeight->GetUnit() == eCSSUnit_Enumerated) { + conditions.SetWritingModeDependency(wm.GetBits()); + } SetCoord(minHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ? nsCSSValue(eCSSUnit_Unset) : *minHeight, pos->mMinHeight, parentPos->mMinHeight, @@ -7759,6 +7849,9 @@ nsRuleNode::ComputePositionData(void* aStartStruct, aContext, mPresContext, conditions); const nsCSSValue* maxHeight = aRuleData->ValueForMaxHeight(); + if (maxHeight->GetUnit() == eCSSUnit_Enumerated) { + conditions.SetWritingModeDependency(wm.GetBits()); + } SetCoord(maxHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ? nsCSSValue(eCSSUnit_Unset) : *maxHeight, pos->mMaxHeight, parentPos->mMaxHeight, diff --git a/layout/style/nsRuleProcessorData.h b/layout/style/nsRuleProcessorData.h index b8516776cc..0e84d96b73 100644 --- a/layout/style/nsRuleProcessorData.h +++ b/layout/style/nsRuleProcessorData.h @@ -22,6 +22,7 @@ #include "mozilla/BloomFilter.h" #include "mozilla/EventStates.h" #include "mozilla/GuardObjects.h" +#include "mozilla/dom/Element.h" class nsIAtom; class nsIContent; diff --git a/layout/style/nsRuleWalker.h b/layout/style/nsRuleWalker.h index 51e2228a7e..ea07837225 100644 --- a/layout/style/nsRuleWalker.h +++ b/layout/style/nsRuleWalker.h @@ -13,7 +13,7 @@ #include "nsRuleNode.h" #include "nsIStyleRule.h" -#include "StyleRule.h" +#include "Declaration.h" #include "nsQueryObject.h" class nsRuleWalker { @@ -34,14 +34,14 @@ protected: public: void Forward(nsIStyleRule* aRule) { - NS_PRECONDITION(!RefPtr(do_QueryObject(aRule)), + NS_PRECONDITION(!RefPtr(do_QueryObject(aRule)), "Calling the wrong Forward() overload"); DoForward(aRule); } - void Forward(mozilla::css::StyleRule* aRule) { + void Forward(mozilla::css::Declaration* aRule) { DoForward(aRule); mCheckForImportantRules = - mCheckForImportantRules && !aRule->GetImportantRule(); + mCheckForImportantRules && !aRule->HasImportantData(); } // ForwardOnPossiblyCSSRule should only be used by callers that have // an explicit list of rules they need to walk, with the list diff --git a/layout/style/nsStyleConsts.h b/layout/style/nsStyleConsts.h index c586b22e62..32a5f24863 100644 --- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -346,8 +346,10 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_CLEAR_NONE 0 #define NS_STYLE_CLEAR_LEFT 1 #define NS_STYLE_CLEAR_RIGHT 2 -#define NS_STYLE_CLEAR_BOTH 3 -#define NS_STYLE_CLEAR_LINE 4 +#define NS_STYLE_CLEAR_INLINE_START 3 +#define NS_STYLE_CLEAR_INLINE_END 4 +#define NS_STYLE_CLEAR_BOTH 5 +#define NS_STYLE_CLEAR_LINE 8 // @note NS_STYLE_CLEAR_LINE can be added to one of the other values in layout // so it needs to use a bit value that none of the other values can have. #define NS_STYLE_CLEAR_MAX (NS_STYLE_CLEAR_LINE | NS_STYLE_CLEAR_BOTH) @@ -572,6 +574,8 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_FLOAT_NONE 0 #define NS_STYLE_FLOAT_LEFT 1 #define NS_STYLE_FLOAT_RIGHT 2 +#define NS_STYLE_FLOAT_INLINE_START 3 +#define NS_STYLE_FLOAT_INLINE_END 4 // See nsStyleClipPath #define NS_STYLE_CLIP_PATH_NONE 0 diff --git a/layout/style/nsStyleCoord.h b/layout/style/nsStyleCoord.h index 5b646798a1..8241fbe7c5 100644 --- a/layout/style/nsStyleCoord.h +++ b/layout/style/nsStyleCoord.h @@ -88,6 +88,10 @@ public: bool operator!=(const CalcValue& aOther) const { return !(*this == aOther); } + + // If this returns true the value is definitely zero. It it returns false + // it might be zero. So it's best used for conservative optimization. + bool IsDefinitelyZero() const { return mLength == 0 && mPercent == 0; } }; // Reference counted calc() value. This is the type that is used to store diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 62b3909009..4247ed71fa 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -1039,11 +1039,11 @@ nsStyleSet::AddImportantRules(nsRuleNode* aCurrLevelNode, node = node->GetParent()) { // We guarantee that we never walk the root node here, so no need // to null-check GetRule(). Furthermore, it must be a CSS rule. - NS_ASSERTION(RefPtr(do_QueryObject(node->GetRule())), + NS_ASSERTION(RefPtr(do_QueryObject(node->GetRule())), "Unexpected non-CSS rule"); nsIStyleRule* impRule = - static_cast(node->GetRule())->GetImportantRule(); + static_cast(node->GetRule())->GetImportantStyleData(); if (impRule) importantRules.AppendElement(impRule); } @@ -1066,10 +1066,11 @@ nsStyleSet::AssertNoImportantRules(nsRuleNode* aCurrLevelNode, for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode; node = node->GetParent()) { - RefPtr rule(do_QueryObject(node->GetRule())); - NS_ASSERTION(rule, "Unexpected non-CSS rule"); + RefPtr declaration(do_QueryObject(node->GetRule())); + NS_ASSERTION(declaration, "Unexpected non-CSS rule"); - NS_ASSERTION(!rule->GetImportantRule(), "Unexpected important rule"); + NS_ASSERTION(!declaration->GetImportantStyleData(), + "Unexpected important style source"); } } @@ -1083,8 +1084,13 @@ nsStyleSet::AssertNoCSSRules(nsRuleNode* aCurrLevelNode, for (nsRuleNode *node = aCurrLevelNode; node != aLastPrevLevelNode; node = node->GetParent()) { nsIStyleRule *rule = node->GetRule(); - RefPtr cssRule(do_QueryObject(rule)); - NS_ASSERTION(!cssRule || !cssRule->Selector(), "Unexpected CSS rule"); + RefPtr declaration(do_QueryObject(rule)); + if (declaration) { + RefPtr cssRule = + do_QueryObject(declaration->GetOwningRule()); + NS_ASSERTION(!cssRule || !cssRule->Selector(), + "Unexpected CSS rule"); + } } } #endif @@ -1960,11 +1966,14 @@ nsStyleSet::ResolveAnonymousBoxStyle(nsIAtom* aPseudoTag, if (aPseudoTag == nsCSSAnonBoxes::pageContent) { // Add any @page rules that are specified. nsTArray rules; - nsTArray importantRules; + nsTArray importantRules; PresContext()->StyleSet()->AppendPageRules(rules); for (uint32_t i = 0, i_end = rules.Length(); i != i_end; ++i) { - ruleWalker.Forward(rules[i]); - css::ImportantRule* importantRule = rules[i]->GetImportantRule(); + css::Declaration* declaration = rules[i]->Declaration(); + declaration->SetImmutable(); + ruleWalker.Forward(declaration); + css::ImportantStyleData* importantRule = + declaration->GetImportantStyleData(); if (importantRule) { importantRules.AppendElement(importantRule); } diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index bcb7772804..b99c606f61 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -98,11 +98,22 @@ static nsChangeHint CalcShadowDifference(nsCSSShadowArray* lhs, // nsStyleFont::nsStyleFont(const nsFont& aFont, nsPresContext *aPresContext) : mFont(aFont) + , mSize(nsStyleFont::ZoomText(aPresContext, mFont.size)) , mGenericID(kGenericFont_NONE) + , mScriptLevel(0) + , mMathVariant(NS_MATHML_MATHVARIANT_NONE) + , mMathDisplay(NS_MATHML_DISPLAYSTYLE_INLINE) + , mMinFontSizeRatio(100) // 100% , mExplicitLanguage(false) + , mAllowZoom(true) + , mScriptUnconstrainedSize(mSize) + , mScriptMinSize(aPresContext->CSSTwipsToAppUnits( + NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT))) + , mScriptSizeMultiplier(NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER) + , mLanguage(GetLanguage(aPresContext)) { MOZ_COUNT_CTOR(nsStyleFont); - Init(aPresContext); + mFont.size = mSize; } nsStyleFont::nsStyleFont(const nsStyleFont& aSrc) @@ -112,6 +123,7 @@ nsStyleFont::nsStyleFont(const nsStyleFont& aSrc) , mScriptLevel(aSrc.mScriptLevel) , mMathVariant(aSrc.mMathVariant) , mMathDisplay(aSrc.mMathDisplay) + , mMinFontSizeRatio(aSrc.mMinFontSizeRatio) , mExplicitLanguage(aSrc.mExplicitLanguage) , mAllowZoom(aSrc.mAllowZoom) , mScriptUnconstrainedSize(aSrc.mScriptUnconstrainedSize) @@ -123,45 +135,9 @@ nsStyleFont::nsStyleFont(const nsStyleFont& aSrc) } nsStyleFont::nsStyleFont(nsPresContext* aPresContext) - // passing nullptr to GetDefaultFont make it use the doc language - : mFont(*(aPresContext->GetDefaultFont(kPresContext_DefaultVariableFont_ID, - nullptr))) - , mGenericID(kGenericFont_NONE) - , mExplicitLanguage(false) + : nsStyleFont(*(aPresContext->GetDefaultFont( + kPresContext_DefaultVariableFont_ID, nullptr)), aPresContext) { - MOZ_COUNT_CTOR(nsStyleFont); - Init(aPresContext); -} - -void -nsStyleFont::Init(nsPresContext* aPresContext) -{ - mSize = mFont.size = nsStyleFont::ZoomText(aPresContext, mFont.size); - mScriptUnconstrainedSize = mSize; - mScriptMinSize = aPresContext->CSSTwipsToAppUnits( - NS_POINTS_TO_TWIPS(NS_MATHML_DEFAULT_SCRIPT_MIN_SIZE_PT)); - mScriptLevel = 0; - mScriptSizeMultiplier = NS_MATHML_DEFAULT_SCRIPT_SIZE_MULTIPLIER; - mMathVariant = NS_MATHML_MATHVARIANT_NONE; - mMathDisplay = NS_MATHML_DISPLAYSTYLE_INLINE; - mAllowZoom = true; - - nsAutoString language; - aPresContext->Document()->GetContentLanguage(language); - language.StripWhitespace(); - - // Content-Language may be a comma-separated list of language codes, - // in which case the HTML5 spec says to treat it as unknown - if (!language.IsEmpty() && - !language.Contains(char16_t(','))) { - mLanguage = do_GetAtom(language); - // NOTE: This does *not* count as an explicit language; in other - // words, it doesn't trigger language-specific hyphenation. - } else { - // we didn't find a (usable) Content-Language, so we fall back - // to whatever the presContext guessed from the charset - mLanguage = aPresContext->GetLanguageFromCharset(); - } } void @@ -199,7 +175,8 @@ nsChangeHint nsStyleFont::CalcDifference(const nsStyleFont& aOther) const mLanguage != aOther.mLanguage || mExplicitLanguage != aOther.mExplicitLanguage || mMathVariant != aOther.mMathVariant || - mMathDisplay != aOther.mMathDisplay) { + mMathDisplay != aOther.mMathDisplay || + mMinFontSizeRatio != aOther.mMinFontSizeRatio) { return NS_STYLE_HINT_REFLOW; } @@ -232,6 +209,27 @@ nsStyleFont::UnZoomText(nsPresContext *aPresContext, nscoord aSize) return nscoord(float(aSize) / aPresContext->TextZoom()); } +/* static */ already_AddRefed +nsStyleFont::GetLanguage(nsPresContext* aPresContext) +{ + nsAutoString language; + aPresContext->Document()->GetContentLanguage(language); + language.StripWhitespace(); + + // Content-Language may be a comma-separated list of language codes, + // in which case the HTML5 spec says to treat it as unknown + if (!language.IsEmpty() && + !language.Contains(char16_t(','))) { + return do_GetAtom(language); + // NOTE: This does *not* count as an explicit language; in other + // words, it doesn't trigger language-specific hyphenation. + } else { + // we didn't find a (usable) Content-Language, so we fall back + // to whatever the presContext guessed from the charset + return do_AddRef(aPresContext->GetLanguageFromCharset()); + } +} + nsChangeHint nsStyleFont::CalcFontDifference(const nsFont& aFont1, const nsFont& aFont2) { if ((aFont1.size == aFont2.size) && @@ -3468,10 +3466,10 @@ nsStyleText::nsStyleText(void) mTextCombineUpright = NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE; mControlCharacterVisibility = nsCSSParser::ControlCharVisibilityDefault(); + mWordSpacing.SetCoordValue(0); mLetterSpacing.SetNormalValue(); mLineHeight.SetNormalValue(); mTextIndent.SetCoordValue(0); - mWordSpacing = 0; mTextShadow = nullptr; mTabSize = NS_STYLE_TABSIZE_INITIAL; diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 20be200e84..d912a6741e 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -101,9 +101,6 @@ struct nsStyleFont { nsStyleFont(const nsFont& aFont, nsPresContext *aPresContext); nsStyleFont(const nsStyleFont& aStyleFont); explicit nsStyleFont(nsPresContext *aPresContext); -private: - void Init(nsPresContext *aPresContext); -public: ~nsStyleFont(void) { MOZ_COUNT_DTOR(nsStyleFont); } @@ -124,6 +121,7 @@ public: static nscoord ZoomText(nsPresContext* aPresContext, nscoord aSize); static nscoord UnZoomText(nsPresContext* aPresContext, nscoord aSize); + static already_AddRefed GetLanguage(nsPresContext* aPresContext); void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->PresShell()-> @@ -150,6 +148,9 @@ public: // MathML displaystyle support uint8_t mMathDisplay; // [inherited] + // allow different min font-size for certain cases + uint8_t mMinFontSizeRatio; // [inherited] percent * 100 + // was mLanguage set based on a lang attribute in the document? bool mExplicitLanguage; // [inherited] @@ -1535,10 +1536,10 @@ struct nsStyleText { uint8_t mControlCharacterVisibility; // [inherited] see nsStyleConsts.h int32_t mTabSize; // [inherited] see nsStyleConsts.h - nscoord mWordSpacing; // [inherited] - nsStyleCoord mLetterSpacing; // [inherited] coord, normal - nsStyleCoord mLineHeight; // [inherited] coord, factor, normal - nsStyleCoord mTextIndent; // [inherited] coord, percent, calc + nsStyleCoord mWordSpacing; // [inherited] coord, percent, calc + nsStyleCoord mLetterSpacing; // [inherited] coord, normal + nsStyleCoord mLineHeight; // [inherited] coord, factor, normal + nsStyleCoord mTextIndent; // [inherited] coord, percent, calc RefPtr mTextShadow; // [inherited] nullptr in case of a zero-length @@ -2173,6 +2174,10 @@ struct nsStyleDisplay { mOverflowX != NS_STYLE_OVERFLOW_CLIP; } + bool IsContainPaint() const { + return NS_STYLE_CONTAIN_PAINT & mContain; + } + /* Returns whether the element has the -moz-transform property * or a related property. */ bool HasTransformStyle() const { @@ -2220,6 +2225,12 @@ struct nsStyleDisplay { * aContextFrame is the frame for which this is the nsStylePosition. */ inline bool IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const; + + // Return the 'float' and 'clear' properties, with inline-{start,end} values + // resolved to {left,right} according to the given writing mode. These are + // defined in WritingModes.h. + inline uint8_t PhysicalFloats(mozilla::WritingMode aWM) const; + inline uint8_t PhysicalBreakType(mozilla::WritingMode aWM) const; }; struct nsStyleTable { diff --git a/layout/style/nsStyleStructInlines.h b/layout/style/nsStyleStructInlines.h index e2da9c5d58..21ac16b05f 100644 --- a/layout/style/nsStyleStructInlines.h +++ b/layout/style/nsStyleStructInlines.h @@ -140,7 +140,8 @@ nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const { NS_ASSERTION(aContextFrame->StyleDisplay() == this, "unexpected aContextFrame"); - return (HasTransform(aContextFrame) || HasPerspectiveStyle() || + return (IsContainPaint() || HasTransform(aContextFrame) || + HasPerspectiveStyle() || aContextFrame->StyleSVGReset()->HasFilters()) && !aContextFrame->IsSVGText(); } diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index 9a50e2b408..c12456204a 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -57,14 +57,14 @@ ElementPropertyTransition::CurrentValuePortion() const timingToUse.mFillMode = NS_STYLE_ANIMATION_FILL_MODE_BOTH; ComputedTiming computedTiming = GetComputedTiming(&timingToUse); - MOZ_ASSERT(computedTiming.mProgress != ComputedTiming::kNullProgress, + MOZ_ASSERT(!computedTiming.mProgress.IsNull(), "Got a null progress for a fill mode of 'both'"); MOZ_ASSERT(mProperties.Length() == 1, "Should have one animation property for a transition"); MOZ_ASSERT(mProperties[0].mSegments.Length() == 1, "Animation property should have one segment for a transition"); return mProperties[0].mSegments[0].mTimingFunction - .GetValue(computedTiming.mProgress); + .GetValue(computedTiming.mProgress.Value()); } ////////////////////////// CSSTransition //////////////////////////// @@ -155,17 +155,6 @@ CSSTransition::QueueEvents() this)); } -bool -CSSTransition::HasEndEventToQueue() const -{ - if (!mEffect) { - return false; - } - - return !mWasFinishedOnLastTick && - PlayState() == AnimationPlayState::Finished; -} - void CSSTransition::Tick() { @@ -487,7 +476,7 @@ nsTransitionManager::StyleContextChanged(dom::Element *aElement, // creates a new style rule if we started *or* stopped transitions. collection->mStyleRuleRefreshTime = TimeStamp(); collection->UpdateCheckGeneration(mPresContext); - collection->mNeedsRefreshes = true; + collection->mStyleChanging = true; TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh(); collection->EnsureStyleRuleFor(now); } diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h index 592369e7dc..1f26b5acdb 100644 --- a/layout/style/nsTransitionManager.h +++ b/layout/style/nsTransitionManager.h @@ -164,7 +164,6 @@ protected: SyncNotifyFlag aSyncNotifyFlag) override; void QueueEvents(); - bool HasEndEventToQueue() const override; // The (pseudo-)element whose computed transition-property refers to this // transition (if any). diff --git a/layout/style/test/ListCSSProperties.cpp b/layout/style/test/ListCSSProperties.cpp index 64d587c182..60d2b82c50 100644 --- a/layout/style/test/ListCSSProperties.cpp +++ b/layout/style/test/ListCSSProperties.cpp @@ -115,6 +115,7 @@ const char *gInaccessibleProperties[] = { "-moz-math-variant", "-moz-math-display", // parsed by UA sheets only "-moz-top-layer", // parsed by UA sheets only + "-moz-min-font-size-ratio", // parsed by UA sheets only "-moz-window-dragging", // chrome-only internal properties "-moz-window-shadow" // chrome-only internal properties }; diff --git a/layout/style/test/mochitest.ini b/layout/style/test/mochitest.ini index 8ea3e306cd..cd97c27e04 100644 --- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -138,6 +138,7 @@ skip-if = toolkit == 'android' [test_computed_style_prefs.html] [test_condition_text.html] [test_condition_text_assignment.html] +[test_contain_formatting_context.html] [test_counter_descriptor_storage.html] [test_counter_style.html] [test_css_cross_domain.html] diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 290588704a..a51a470d7c 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -2473,7 +2473,7 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "none" ], - other_values: [ "left", "right", "both" ], + other_values: [ "left", "right", "both", "inline-start", "inline-end" ], invalid_values: [] }, "clip": { @@ -2543,7 +2543,7 @@ var gCSSProperties = { type: CSS_TYPE_LONGHAND, initial_values: [ "inline" ], /* XXX none will really mess with other properties */ - prerequisites: { "float": "none", "position": "static" }, + prerequisites: { "float": "none", "position": "static", "contain": "none" }, other_values: [ "block", "flex", @@ -2577,7 +2577,7 @@ var gCSSProperties = { inherited: false, type: CSS_TYPE_LONGHAND, initial_values: [ "none" ], - other_values: [ "left", "right" ], + other_values: [ "left", "right", "inline-start", "inline-end" ], invalid_values: [] }, "font": { @@ -3221,7 +3221,7 @@ var gCSSProperties = { domProp: "overflow", inherited: false, type: CSS_TYPE_SHORTHAND_AND_LONGHAND, - prerequisites: { "display": "block" }, + prerequisites: { "display": "block", "contain": "none" }, subproperties: [ "overflow-x", "overflow-y" ], initial_values: [ "visible" ], other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable", "-moz-scrollbars-none" ], @@ -3231,7 +3231,7 @@ var gCSSProperties = { domProp: "overflowX", inherited: false, type: CSS_TYPE_LONGHAND, - prerequisites: { "display": "block", "overflow-y": "visible" }, + prerequisites: { "display": "block", "overflow-y": "visible", "contain": "none" }, initial_values: [ "visible" ], other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable" ], invalid_values: [] @@ -3240,7 +3240,7 @@ var gCSSProperties = { domProp: "overflowY", inherited: false, type: CSS_TYPE_LONGHAND, - prerequisites: { "display": "block", "overflow-x": "visible" }, + prerequisites: { "display": "block", "overflow-x": "visible", "contain": "none" }, initial_values: [ "visible" ], other_values: [ "auto", "scroll", "hidden", "-moz-hidden-unscrollable" ], invalid_values: [] @@ -3764,10 +3764,11 @@ var gCSSProperties = { initial_values: [ "normal", "0", "0px", "-0em", "calc(-0px)", "calc(0em)" ], - other_values: [ "1em", "2px", "-3px", + other_values: [ "1em", "2px", "-3px", "0%", "50%", "-120%", "calc(1em)", "calc(1em + 3px)", "calc(15px / 2)", "calc(15px/2)", - "calc(-2em)" + "calc(-2em)", "calc(0% + 0px)", + "calc(-10%/2 - 1em)" ], invalid_values: [], quirks_values: { "5": "5px" }, diff --git a/layout/style/test/test_animations_dynamic_changes.html b/layout/style/test/test_animations_dynamic_changes.html index 9ef72ac362..a68d734dcc 100644 --- a/layout/style/test/test_animations_dynamic_changes.html +++ b/layout/style/test/test_animations_dynamic_changes.html @@ -46,14 +46,14 @@ function test_bug978833() { p.classList.add("alwaysa"); kf.style.marginLeft = "100px"; - todo_is(cs.marginLeft, "100px", "p margin-left should be 100px after change"); + is(cs.marginLeft, "100px", "p margin-left should be 100px after change"); // Try the same thing a second time, just to make sure it works again. p.classList.remove("alwaysa"); is(cs.marginLeft, "0px", "p margin-left should be 0px without animation"); p.classList.add("alwaysa"); kf.style.marginLeft = "150px"; - todo_is(cs.marginLeft, "150px", "p margin-left should be 150px after second change"); + is(cs.marginLeft, "150px", "p margin-left should be 150px after second change"); p.style.animation = ""; } diff --git a/layout/style/test/test_contain_formatting_context.html b/layout/style/test/test_contain_formatting_context.html new file mode 100644 index 0000000000..928cc35f50 --- /dev/null +++ b/layout/style/test/test_contain_formatting_context.html @@ -0,0 +1,40 @@ + + + + + + Test that 'contain: paint' updates 'display' correctly + + + + + +
+ + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 48bfa27ad8..48ff72a36f 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -83,8 +83,8 @@ load 466585-1.svg load 467323-1.svg load 467498-1.svg load 470124-1.svg -load 474700-1.svg load 472782-1.svg +load 474700-1.svg load 475181-1.svg load 475193-1.html load 475302-1.svg @@ -99,7 +99,6 @@ load 515288-1.html load 522394-1.svg load 522394-2.svg load 522394-3.svg -load extref-test-1.xhtml load 566216-1.svg skip-if(Android&&!browserIsRemote) load 587336-1.html load 590291-1.svg @@ -135,21 +134,21 @@ load 740627-1.svg load 740627-2.svg load 757704-1.svg load 757718-1.svg -load 767056-1.svg -load 768351.svg -load 780963-1.html load 757751-1.svg +load 767056-1.svg load 767535-1.xhtml load 768087-1.html +load 768351.svg load 778492-1.svg load 779971-1.svg load 780764-1.svg +load 780963-1.html load 782141-1.svg load 784061-1.svg load 788831-1.svg +load 789390-1.html load 790072.svg load 791826-1.svg -load 789390-1.html load 808318-1.svg load 813420-1.svg load 841163-1.svg @@ -181,8 +180,8 @@ load 913990.html load 919371-1.xhtml load 952270-1.svg load 963086-1.svg -load 975773-1.svg load 974746-1.svg +load 975773-1.svg load 979407-1.svg load 979407-2.svg load 993443.svg diff --git a/layout/tables/crashtests/crashtests.list b/layout/tables/crashtests/crashtests.list index ea4fdae620..7911686e68 100644 --- a/layout/tables/crashtests/crashtests.list +++ b/layout/tables/crashtests/crashtests.list @@ -122,7 +122,7 @@ load 488388-1.html load 509562-1.xhtml load 512749-1.html load 513732-1.html -asserts(0-1) load 533380-1.xhtml # Bug 614457 +load 533380-1.xhtml load 534716-1.html load 563009-1.html load 563009-2.html diff --git a/layout/xul/crashtests/crashtests.list b/layout/xul/crashtests/crashtests.list index 8491287352..489d26e740 100644 --- a/layout/xul/crashtests/crashtests.list +++ b/layout/xul/crashtests/crashtests.list @@ -14,9 +14,9 @@ load 291702-3.xul load 294371-1.xul load 311457-1.html load 321056-1.xhtml -load 326834-1.html load 322786-1.xul load 325377.xul +load 326834-1.html load 326879-1.xul load 327776-1.xul load 328135-1.xul diff --git a/layout/xul/tree/crashtests/crashtests.list b/layout/xul/tree/crashtests/crashtests.list index 7e305f692d..d462e95505 100644 --- a/layout/xul/tree/crashtests/crashtests.list +++ b/layout/xul/tree/crashtests/crashtests.list @@ -2,7 +2,7 @@ load 307298-1.xul load 309732-1.xul load 309732-2.xul load 366583-1.xul -asserts-if(winWidget,0-4) load 380217-1.xul # bug 616710 +load 380217-1.xul load 382444-1.html load 391178-1.xhtml load 391178-2.xul diff --git a/parser/htmlparser/tests/crashtests/crashtests.list b/parser/htmlparser/tests/crashtests/crashtests.list index 93f818288d..63d5ed6fff 100644 --- a/parser/htmlparser/tests/crashtests/crashtests.list +++ b/parser/htmlparser/tests/crashtests/crashtests.list @@ -30,7 +30,7 @@ load 269095-1.html load 286733-1.html load 286733-2.html load 299036-1.html -skip-if((OSX==1007||OSX==1008)&&browserIsRemote) load 328751-1.html # Bug 849747 +skip-if(cocoaWidget&&browserIsRemote) load 328751-1.html # Bug 849747 load 408939-1.html load 423373-1.html load 445171-1.html @@ -40,8 +40,8 @@ load 502869.html load 515278-1.html load 515533-1.html load 515816-1.html -load 525229-1.html load 522326-1.html +load 525229-1.html load 536097-1.html load 555462.html load 563514-1.html diff --git a/testing/crashtest/crashtests.list b/testing/crashtest/crashtests.list index 7d9ee5faab..8482e87f17 100644 --- a/testing/crashtest/crashtests.list +++ b/testing/crashtest/crashtests.list @@ -20,7 +20,7 @@ include ../../dom/offline/crashtests/crashtests.list include ../../dom/plugins/test/crashtests/crashtests.list include ../../dom/smil/crashtests/crashtests.list include ../../dom/svg/crashtests/crashtests.list -include ../../dom/workers/test/crashtests.list +include ../../dom/workers/test/crashtests/crashtests.list include ../../dom/xbl/crashtests/crashtests.list include ../../dom/xml/crashtests/crashtests.list include ../../dom/xslt/crashtests/crashtests.list @@ -30,7 +30,9 @@ include ../../dom/xul/templates/crashtests/crashtests.list # Bug 811873 - mozRTCPeerConnection doesn't support remote browser yet skip-if(browserIsRemote||!webrtc) include ../../dom/media/tests/crashtests/crashtests.list -include ../../editor/crashtests.list +include ../../editor/composer/crashtests/crashtests.list +include ../../editor/libeditor/crashtests/crashtests.list +include ../../editor/txmgr/tests/crashtests/crashtests.list include ../../gfx/tests/crashtests/crashtests.list diff --git a/toolkit/commonjs/moz.build b/toolkit/commonjs/moz.build index fcd452dbda..077d2e6dae 100644 --- a/toolkit/commonjs/moz.build +++ b/toolkit/commonjs/moz.build @@ -227,16 +227,25 @@ EXTRA_JS_MODULES.commonjs.sdk.console += [ EXTRA_JS_MODULES.commonjs.sdk.content += [ 'sdk/content/content-worker.js', 'sdk/content/content.js', + 'sdk/content/context-menu.js', 'sdk/content/events.js', + 'sdk/content/l10n-html.js', 'sdk/content/loader.js', 'sdk/content/mod.js', + 'sdk/content/page-mod.js', + 'sdk/content/page-worker.js', 'sdk/content/sandbox.js', + 'sdk/content/tab-events.js', 'sdk/content/thumbnail.js', 'sdk/content/utils.js', 'sdk/content/worker-child.js', 'sdk/content/worker.js', ] +EXTRA_JS_MODULES.commonjs.sdk.content.sandbox += [ + 'sdk/content/sandbox/events.js', +] + EXTRA_JS_MODULES.commonjs.sdk.core += [ 'sdk/core/disposable.js', 'sdk/core/heritage.js', @@ -255,6 +264,7 @@ EXTRA_JS_MODULES.commonjs.sdk.deprecated.traits += [ ] EXTRA_JS_MODULES.commonjs.sdk.dom += [ + 'sdk/dom/events-shimmed.js', 'sdk/dom/events.js', ] @@ -376,6 +386,7 @@ EXTRA_JS_MODULES.commonjs.sdk.stylesheet += [ EXTRA_JS_MODULES.commonjs.sdk.system += [ 'sdk/system/child_process.js', 'sdk/system/environment.js', + 'sdk/system/events-shimmed.js', 'sdk/system/events.js', 'sdk/system/globals.js', 'sdk/system/process.js', diff --git a/toolkit/commonjs/sdk/content/content-worker.js b/toolkit/commonjs/sdk/content/content-worker.js index 3be3477a69..0a82257331 100644 --- a/toolkit/commonjs/sdk/content/content-worker.js +++ b/toolkit/commonjs/sdk/content/content-worker.js @@ -110,7 +110,7 @@ Object.freeze({ injectTimers: function injectTimers(exports, chromeAPI, pipe, console) { // wrapped functions from `'timer'` module. // Wrapper adds `try catch` blocks to the callbacks in order to - // emit `error` event on a symbiont if exception is thrown in + // emit `error` event if exception is thrown in // the Worker global scope. // @see http://www.w3.org/TR/workers/#workerutils diff --git a/toolkit/commonjs/sdk/content/content.js b/toolkit/commonjs/sdk/content/content.js index 5bd98145c8..9655223a3a 100644 --- a/toolkit/commonjs/sdk/content/content.js +++ b/toolkit/commonjs/sdk/content/content.js @@ -9,22 +9,7 @@ module.metadata = { const { deprecateUsage } = require('../util/deprecate'); -Object.defineProperty(exports, "Loader", { - get: function() { - deprecateUsage('`sdk/content/content` is deprecated. Please use `sdk/content/loader` directly.'); - return require('./loader').Loader; - } -}); - -Object.defineProperty(exports, "Symbiont", { - get: function() { - deprecateUsage('Both `sdk/content/content` and `sdk/deprecated/symbiont` are deprecated. ' + - '`sdk/core/heritage` supersedes Symbiont for inheritance.'); - return require('../deprecated/symbiont').Symbiont; - } -}); - -Object.defineProperty(exports, "Worker", { +Object.defineProperty(exports, "Worker", { get: function() { deprecateUsage('`sdk/content/content` is deprecated. Please use `sdk/content/worker` directly.'); return require('./worker').Worker; diff --git a/toolkit/commonjs/sdk/content/context-menu.js b/toolkit/commonjs/sdk/content/context-menu.js new file mode 100644 index 0000000000..4495638731 --- /dev/null +++ b/toolkit/commonjs/sdk/content/context-menu.js @@ -0,0 +1,408 @@ +/* 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/. */ +"use strict"; + +const { Class } = require("../core/heritage"); +const self = require("../self"); +const { WorkerChild } = require("./worker-child"); +const { getInnerId } = require("../window/utils"); +const { Ci } = require("chrome"); +const { Services } = require("resource://gre/modules/Services.jsm"); +const system = require('../system/events'); +const { process } = require('../remote/child'); + +// These functions are roughly copied from sdk/selection which doesn't work +// in the content process +function getElementWithSelection(window) { + let element = Services.focus.getFocusedElementForWindow(window, false, {}); + if (!element) + return null; + + try { + // Accessing selectionStart and selectionEnd on e.g. a button + // results in an exception thrown as per the HTML5 spec. See + // http://www.whatwg.org/specs/web-apps/current-work/multipage/association-of-controls-and-forms.html#textFieldSelection + + let { value, selectionStart, selectionEnd } = element; + + let hasSelection = typeof value === "string" && + !isNaN(selectionStart) && + !isNaN(selectionEnd) && + selectionStart !== selectionEnd; + + return hasSelection ? element : null; + } + catch (err) { + console.exception(err); + return null; + } +} + +function safeGetRange(selection, rangeNumber) { + try { + let { rangeCount } = selection; + let range = null; + + for (let rangeNumber = 0; rangeNumber < rangeCount; rangeNumber++ ) { + range = selection.getRangeAt(rangeNumber); + + if (range && range.toString()) + break; + + range = null; + } + + return range; + } + catch (e) { + return null; + } +} + +function getSelection(window) { + let selection = window.getSelection(); + let range = safeGetRange(selection); + if (range) + return range.toString(); + + let node = getElementWithSelection(window); + if (!node) + return null; + + return node.value.substring(node.selectionStart, node.selectionEnd); +} + +//These are used by PageContext.isCurrent below. If the popupNode or any of +//its ancestors is one of these, Firefox uses a tailored context menu, and so +//the page context doesn't apply. +const NON_PAGE_CONTEXT_ELTS = [ + Ci.nsIDOMHTMLAnchorElement, + Ci.nsIDOMHTMLAppletElement, + Ci.nsIDOMHTMLAreaElement, + Ci.nsIDOMHTMLButtonElement, + Ci.nsIDOMHTMLCanvasElement, + Ci.nsIDOMHTMLEmbedElement, + Ci.nsIDOMHTMLImageElement, + Ci.nsIDOMHTMLInputElement, + Ci.nsIDOMHTMLMapElement, + Ci.nsIDOMHTMLMediaElement, + Ci.nsIDOMHTMLMenuElement, + Ci.nsIDOMHTMLObjectElement, + Ci.nsIDOMHTMLOptionElement, + Ci.nsIDOMHTMLSelectElement, + Ci.nsIDOMHTMLTextAreaElement, +]; + +// List all editable types of inputs. Or is it better to have a list +// of non-editable inputs? +var editableInputs = { + email: true, + number: true, + password: true, + search: true, + tel: true, + text: true, + textarea: true, + url: true +}; + +var CONTEXTS = {}; + +var Context = Class({ + initialize: function(id) { + this.id = id; + }, + + adjustPopupNode: function adjustPopupNode(popupNode) { + return popupNode; + }, + + // Gets state to pass through to the parent process for the node the user + // clicked on + getState: function(popupNode) { + return false; + } +}); + +// Matches when the context-clicked node doesn't have any of +// NON_PAGE_CONTEXT_ELTS in its ancestors +CONTEXTS.PageContext = Class({ + extends: Context, + + getState: function(popupNode) { + // If there is a selection in the window then this context does not match + if (!popupNode.ownerDocument.defaultView.getSelection().isCollapsed) + return false; + + // If the clicked node or any of its ancestors is one of the blocked + // NON_PAGE_CONTEXT_ELTS then this context does not match + while (!(popupNode instanceof Ci.nsIDOMDocument)) { + if (NON_PAGE_CONTEXT_ELTS.some(type => popupNode instanceof type)) + return false; + + popupNode = popupNode.parentNode; + } + + return true; + } +}); + +// Matches when there is an active selection in the window +CONTEXTS.SelectionContext = Class({ + extends: Context, + + getState: function(popupNode) { + if (!popupNode.ownerDocument.defaultView.getSelection().isCollapsed) + return true; + + try { + // The node may be a text box which has selectionStart and selectionEnd + // properties. If not this will throw. + let { selectionStart, selectionEnd } = popupNode; + return !isNaN(selectionStart) && !isNaN(selectionEnd) && + selectionStart !== selectionEnd; + } + catch (e) { + return false; + } + } +}); + +// Matches when the context-clicked node or any of its ancestors matches the +// selector given +CONTEXTS.SelectorContext = Class({ + extends: Context, + + initialize: function initialize(id, selector) { + Context.prototype.initialize.call(this, id); + this.selector = selector; + }, + + adjustPopupNode: function adjustPopupNode(popupNode) { + let selector = this.selector; + + while (!(popupNode instanceof Ci.nsIDOMDocument)) { + if (popupNode.mozMatchesSelector(selector)) + return popupNode; + + popupNode = popupNode.parentNode; + } + + return null; + }, + + getState: function(popupNode) { + return !!this.adjustPopupNode(popupNode); + } +}); + +// Matches when the page url matches any of the patterns given +CONTEXTS.URLContext = Class({ + extends: Context, + + getState: function(popupNode) { + return popupNode.ownerDocument.URL; + } +}); + +// Matches when the user-supplied predicate returns true +CONTEXTS.PredicateContext = Class({ + extends: Context, + + getState: function(node) { + let window = node.ownerDocument.defaultView; + let data = {}; + + data.documentType = node.ownerDocument.contentType; + + data.documentURL = node.ownerDocument.location.href; + data.targetName = node.nodeName.toLowerCase(); + data.targetID = node.id || null ; + + if ((data.targetName === 'input' && editableInputs[node.type]) || + data.targetName === 'textarea') { + data.isEditable = !node.readOnly && !node.disabled; + } + else { + data.isEditable = node.isContentEditable; + } + + data.selectionText = getSelection(window, "TEXT"); + + data.srcURL = node.src || null; + data.value = node.value || null; + + while (!data.linkURL && node) { + data.linkURL = node.href || null; + node = node.parentNode; + } + + return data; + }, +}); + +function instantiateContext({ id, type, args }) { + if (!(type in CONTEXTS)) { + console.error("Attempt to use unknown context " + type); + return; + } + return new CONTEXTS[type](id, ...args); +} + +var ContextWorker = Class({ + implements: [ WorkerChild ], + + // Calls the context workers context listeners and returns the first result + // that is either a string or a value that evaluates to true. If all of the + // listeners returned false then returns false. If there are no listeners, + // returns true (show the menu item by default). + getMatchedContext: function getCurrentContexts(popupNode) { + let results = this.sandbox.emitSync("context", popupNode); + if (!results.length) + return true; + return results.reduce((val, result) => val || result); + }, + + // Emits a click event in the worker's port. popupNode is the node that was + // context-clicked, and clickedItemData is the data of the item that was + // clicked. + fireClick: function fireClick(popupNode, clickedItemData) { + this.sandbox.emitSync("click", popupNode, clickedItemData); + } +}); + +// Gets the item's content script worker for a window, creating one if necessary +// Once created it will be automatically destroyed when the window unloads. +// If there is not content scripts for the item then null will be returned. +function getItemWorkerForWindow(item, window) { + if (!item.contentScript && !item.contentScriptFile) + return null; + + let id = getInnerId(window); + let worker = item.workerMap.get(id); + + if (worker) + return worker; + + worker = ContextWorker({ + id: item.id, + window, + manager: item.manager, + contentScript: item.contentScript, + contentScriptFile: item.contentScriptFile, + onDetach: function() { + item.workerMap.delete(id); + } + }); + + item.workerMap.set(id, worker); + + return worker; +} + +// A very simple remote proxy for every item. It's job is to provide data for +// the main process to use to determine visibility state and to call into +// content scripts when clicked. +var RemoteItem = Class({ + initialize: function(options, manager) { + this.id = options.id; + this.contexts = [instantiateContext(c) for (c of options.contexts)]; + this.contentScript = options.contentScript; + this.contentScriptFile = options.contentScriptFile; + + this.manager = manager; + + this.workerMap = new Map(); + keepAlive.set(this.id, this); + }, + + destroy: function() { + for (let worker of this.workerMap.values()) { + worker.destroy(); + } + keepAlive.delete(this.id); + }, + + activate: function(popupNode, data) { + let worker = getItemWorkerForWindow(this, popupNode.ownerDocument.defaultView); + if (!worker) + return; + + for (let context of this.contexts) + popupNode = context.adjustPopupNode(popupNode); + + worker.fireClick(popupNode, data); + }, + + // Fills addonInfo with state data to send through to the main process + getContextState: function(popupNode, addonInfo) { + if (!(self.id in addonInfo)) { + addonInfo[self.id] = { + processID: process.id, + items: {} + }; + } + + let worker = getItemWorkerForWindow(this, popupNode.ownerDocument.defaultView); + let contextStates = {}; + for (let context of this.contexts) + contextStates[context.id] = context.getState(popupNode); + + addonInfo[self.id].items[this.id] = { + // It isn't ideal to create a PageContext for every item but there isn't + // a good shared place to do it. + pageContext: (new CONTEXTS.PageContext()).getState(popupNode), + contextStates, + hasWorker: !!worker, + workerContext: worker ? worker.getMatchedContext(popupNode) : true + } + } +}); +exports.RemoteItem = RemoteItem; + +// Holds remote items for this frame. +var keepAlive = new Map(); + +// Called to create remote proxies for items. If they already exist we destroy +// and recreate. This can happen if the item changes in some way or in odd +// timing cases where the frame script is create around the same time as the +// item is created in the main process +process.port.on('sdk/contextmenu/createitems', (process, items) => { + for (let itemoptions of items) { + let oldItem = keepAlive.get(itemoptions.id); + if (oldItem) { + oldItem.destroy(); + } + + let item = new RemoteItem(itemoptions, this); + } +}); + +process.port.on('sdk/contextmenu/destroyitems', (process, items) => { + for (let id of items) { + let item = keepAlive.get(id); + item.destroy(); + } +}); + +var lastPopupNode = null; + +system.on('content-contextmenu', ({ subject }) => { + let { event: { target: popupNode }, addonInfo } = subject.wrappedJSObject; + lastPopupNode = popupNode; + + for (let item of keepAlive.values()) { + item.getContextState(popupNode, addonInfo); + } +}, true); + +process.port.on('sdk/contextmenu/activateitems', (process, items, data) => { + for (let id of items) { + let item = keepAlive.get(id); + if (!item) + continue; + + item.activate(lastPopupNode, data); + } +}); diff --git a/toolkit/commonjs/sdk/content/events.js b/toolkit/commonjs/sdk/content/events.js index c9b8905303..c085b61799 100644 --- a/toolkit/commonjs/sdk/content/events.js +++ b/toolkit/commonjs/sdk/content/events.js @@ -34,7 +34,7 @@ function streamEventsFrom({document}) { // Map supported event types to a streams of those events on the given // `window` for the inserted document and than merge these streams into // single form stream off all window state change events. - var stateChanges = TYPES.map(function(type) { + let stateChanges = TYPES.map(function(type) { return open(document, type, { capture: true }); }); diff --git a/toolkit/commonjs/sdk/content/l10n-html.js b/toolkit/commonjs/sdk/content/l10n-html.js new file mode 100644 index 0000000000..7927a84904 --- /dev/null +++ b/toolkit/commonjs/sdk/content/l10n-html.js @@ -0,0 +1,129 @@ +/* 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/. */ +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Ci } = require("chrome"); +const core = require("../l10n/core"); +const { loadSheet, removeSheet } = require("../stylesheet/utils"); +const { process, frames } = require("../remote/child"); +const { Services } = require("resource://gre/modules/Services.jsm"); + +const assetsURI = require('../self').data.url(); + +const hideSheetUri = "data:text/css,:root {visibility: hidden !important;}"; + +function translateElementAttributes(element) { + // Translateable attributes + const attrList = ['title', 'accesskey', 'alt', 'label', 'placeholder']; + const ariaAttrMap = { + 'ariaLabel': 'aria-label', + 'ariaValueText': 'aria-valuetext', + 'ariaMozHint': 'aria-moz-hint' + }; + const attrSeparator = '.'; + + // Try to translate each of the attributes + for (let attribute of attrList) { + const data = core.get(element.dataset.l10nId + attrSeparator + attribute); + if (data) + element.setAttribute(attribute, data); + } + + // Look for the aria attribute translations that match fxOS's aliases + for (let attrAlias in ariaAttrMap) { + const data = core.get(element.dataset.l10nId + attrSeparator + attrAlias); + if (data) + element.setAttribute(ariaAttrMap[attrAlias], data); + } +} + +// Taken from Gaia: +// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470 +function translateElement(element) { + element = element || document; + + // check all translatable children (= w/ a `data-l10n-id' attribute) + var children = element.querySelectorAll('*[data-l10n-id]'); + var elementCount = children.length; + for (var i = 0; i < elementCount; i++) { + var child = children[i]; + + // translate the child + var key = child.dataset.l10nId; + var data = core.get(key); + if (data) + child.textContent = data; + + translateElementAttributes(child); + } +} +exports.translateElement = translateElement; + +function onDocumentReady2Translate(event) { + let document = event.target; + document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate, + false); + + translateElement(document); + + try { + // Finally display document when we finished replacing all text content + if (document.defaultView) + removeSheet(document.defaultView, hideSheetUri, 'user'); + } + catch(e) { + console.exception(e); + } +} + +function onContentWindow(document) { + // Accept only HTML documents + if (!(document instanceof Ci.nsIDOMHTMLDocument)) + return; + + // Bug 769483: data:URI documents instanciated with nsIDOMParser + // have a null `location` attribute at this time + if (!document.location) + return; + + // Accept only document from this addon + if (document.location.href.indexOf(assetsURI) !== 0) + return; + + try { + // First hide content of the document in order to have content blinking + // between untranslated and translated states + loadSheet(document.defaultView, hideSheetUri, 'user'); + } + catch(e) { + console.exception(e); + } + // Wait for DOM tree to be built before applying localization + document.addEventListener("DOMContentLoaded", onDocumentReady2Translate, + false); +} + +// Listen to creation of content documents in order to translate them as soon +// as possible in their loading process +const ON_CONTENT = "document-element-inserted"; +let enabled = false; +function enable() { + if (enabled) + return; + Services.obs.addObserver(onContentWindow, ON_CONTENT, false); + enabled = true; +} +process.port.on("sdk/l10n/html/enable", enable); + +function disable() { + if (!enabled) + return; + Services.obs.removeObserver(onContentWindow, ON_CONTENT); + enabled = false; +} +process.port.on("sdk/l10n/html/disable", disable); diff --git a/toolkit/commonjs/sdk/content/loader.js b/toolkit/commonjs/sdk/content/loader.js index f4178fd0cc..d15a8c48e3 100644 --- a/toolkit/commonjs/sdk/content/loader.js +++ b/toolkit/commonjs/sdk/content/loader.js @@ -1,16 +1,13 @@ /* 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/. */ - "use strict"; module.metadata = { "stability": "unstable" }; -const { EventEmitter } = require('../deprecated/events'); const { isValidURI, isLocalURL, URL } = require('../url'); -const file = require('../io/file'); const { contract } = require('../util/contract'); const { isString, isNil, instanceOf } = require('../lang/type'); const { validateOptions, @@ -19,7 +16,8 @@ const { validateOptions, const isJSONable = (value) => { try { JSON.parse(JSON.stringify(value)); - } catch (e) { + } + catch (e) { return false; } return true; @@ -83,102 +81,4 @@ function Allow(script) { }; } -/** - * Trait is intended to be used in some composition. It provides set of core - * properties and bounded validations to them. Trait is useful for all the - * compositions providing high level APIs for interaction with content. - * Property changes emit `"propertyChange"` events on instances. - */ -const Loader = EventEmitter.compose({ - /** - * Permissions for the content, with the following keys: - * @property {Object} [allow = { script: true }] - * @property {Boolean} [allow.script = true] - * Whether or not to execute script in the content. Defaults to true. - */ - get allow() this._allow || (this._allow = Allow(true)), - set allow(value) this.allow.script = value && value.script, - _allow: null, - /** - * The content to load. Either a string of HTML or a URL. - * @type {String} - */ - get contentURL() this._contentURL, - set contentURL(value) { - value = validate(value, valid.contentURL); - if (this._contentURL != value) { - this._emit('propertyChange', { - contentURL: this._contentURL = value - }); - } - }, - _contentURL: null, - /** - * When to load the content scripts. - * Possible values are "end" (default), which loads them once all page - * contents have been loaded, "ready", which loads them once DOM nodes are - * ready (ie like DOMContentLoaded event), and "start", which loads them once - * the `window` object for the page has been created, but before any scripts - * specified by the page have been loaded. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {'start'|'ready'|'end'} - */ - get contentScriptWhen() this._contentScriptWhen, - set contentScriptWhen(value) { - value = validate(value, valid.contentScriptWhen); - if (value !== this._contentScriptWhen) { - this._emit('propertyChange', { - contentScriptWhen: this._contentScriptWhen = value - }); - } - }, - _contentScriptWhen: 'end', - /** - * Options avalaible from the content script as `self.options`. - * The value of options can be of any type (object, array, string, etc.) - * but only jsonable values will be available as frozen objects from the - * content script. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {Object} - */ - get contentScriptOptions() this._contentScriptOptions, - set contentScriptOptions(value) this._contentScriptOptions = value, - _contentScriptOptions: null, - /** - * The URLs of content scripts. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {String[]} - */ - get contentScriptFile() this._contentScriptFile, - set contentScriptFile(value) { - value = validate(value, valid.contentScriptFile); - if (value != this._contentScriptFile) { - this._emit('propertyChange', { - contentScriptFile: this._contentScriptFile = value - }); - } - }, - _contentScriptFile: null, - /** - * The texts of content script. - * Property change emits `propertyChange` event on instance with this key - * and new value. - * @type {String|undefined} - */ - get contentScript() this._contentScript, - set contentScript(value) { - value = validate(value, valid.contentScript); - if (value != this._contentScript) { - this._emit('propertyChange', { - contentScript: this._contentScript = value - }); - } - }, - _contentScript: null -}); -exports.Loader = Loader; - exports.contract = contract(valid); diff --git a/toolkit/commonjs/sdk/content/mod.js b/toolkit/commonjs/sdk/content/mod.js index 7d31272f8e..81fe9ee42e 100644 --- a/toolkit/commonjs/sdk/content/mod.js +++ b/toolkit/commonjs/sdk/content/mod.js @@ -11,7 +11,7 @@ const { Ci } = require("chrome"); const { dispatcher } = require("../util/dispatcher"); const { add, remove, iterator } = require("../lang/weak-set"); -let getTargetWindow = dispatcher("getTargetWindow"); +var getTargetWindow = dispatcher("getTargetWindow"); getTargetWindow.define(function (target) { if (target instanceof Ci.nsIDOMWindow) @@ -24,10 +24,10 @@ getTargetWindow.define(function (target) { exports.getTargetWindow = getTargetWindow; -let attachTo = dispatcher("attachTo"); +var attachTo = dispatcher("attachTo"); exports.attachTo = attachTo; -let detachFrom = dispatcher("detatchFrom"); +var detachFrom = dispatcher("detatchFrom"); exports.detachFrom = detachFrom; function attach(modification, target) { @@ -56,6 +56,10 @@ function detach(modification, target) { else { let documents = iterator(modification); for (let document of documents) { + let window = document.defaultView; + // The window might have already gone away + if (!window) + continue; detachFrom(modification, document.defaultView); remove(modification, document); } diff --git a/toolkit/commonjs/sdk/content/page-mod.js b/toolkit/commonjs/sdk/content/page-mod.js new file mode 100644 index 0000000000..8ff9b1e7b7 --- /dev/null +++ b/toolkit/commonjs/sdk/content/page-mod.js @@ -0,0 +1,236 @@ +/* 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/. */ +"use strict"; + +module.metadata = { + "stability": "stable" +}; + +const { getAttachEventType } = require('../content/utils'); +const { Class } = require('../core/heritage'); +const { Disposable } = require('../core/disposable'); +const { WeakReference } = require('../core/reference'); +const { WorkerChild } = require('./worker-child'); +const { EventTarget } = require('../event/target'); +const { on, emit, once, setListeners } = require('../event/core'); +const { on: domOn, removeListener: domOff } = require('../dom/events'); +const { isRegExp, isUndefined } = require('../lang/type'); +const { merge } = require('../util/object'); +const { isBrowser, getFrames } = require('../window/utils'); +const { getTabs, getURI: getTabURI } = require('../tabs/utils'); +const { ignoreWindow } = require('../private-browsing/utils'); +const { Style } = require("../stylesheet/style"); +const { attach, detach } = require("../content/mod"); +const { has, hasAny } = require("../util/array"); +const { Rules } = require("../util/rules"); +const { List, addListItem, removeListItem } = require('../util/list'); +const { when } = require("../system/unload"); +const { uuid } = require('../util/uuid'); +const { frames, process } = require('../remote/child'); + +const pagemods = new Map(); +const styles = new WeakMap(); +var styleFor = (mod) => styles.get(mod); + +// Helper functions +var modMatchesURI = (mod, uri) => mod.include.matchesAny(uri) && !mod.exclude.matchesAny(uri); + +/** + * PageMod constructor (exported below). + * @constructor + */ +const ChildPageMod = Class({ + implements: [ + EventTarget, + Disposable, + ], + setup: function PageMod(model) { + merge(this, model); + + // Set listeners on {PageMod} itself, not the underlying worker, + // like `onMessage`, as it'll get piped. + setListeners(this, model); + + function deserializeRules(rules) { + for (let rule of rules) { + yield rule.type == "string" ? rule.value + : new RegExp(rule.pattern, rule.flags); + } + } + + let include = [...deserializeRules(this.include)]; + this.include = Rules(); + this.include.add.apply(this.include, include); + + let exclude = [...deserializeRules(this.exclude)]; + this.exclude = Rules(); + this.exclude.add.apply(this.exclude, exclude); + + if (this.contentStyle || this.contentStyleFile) { + styles.set(this, Style({ + uri: this.contentStyleFile, + source: this.contentStyle + })); + } + + pagemods.set(this.id, this); + this.seenDocuments = new WeakMap(); + + // `applyOnExistingDocuments` has to be called after `pagemods.add()` + // otherwise its calls to `onContent` method won't do anything. + if (has(this.attachTo, 'existing')) + applyOnExistingDocuments(this); + }, + + dispose: function() { + let style = styleFor(this); + if (style) + detach(style); + + for (let i in this.include) + this.include.remove(this.include[i]); + + pagemods.delete(this.id); + } +}); + +function onContentWindow({ target: document }) { + // Return if we have no pagemods + if (pagemods.size === 0) + return; + + let window = document.defaultView; + // XML documents don't have windows, and we don't yet support them. + if (!window) + return; + + // Frame event listeners are bound to the frame the event came from by default + let frame = this; + // We apply only on documents in tabs of Firefox + if (!frame.isTab) + return; + + // When the tab is private, only addons with 'private-browsing' flag in + // their package.json can apply content script to private documents + if (ignoreWindow(window)) + return; + + for (let pagemod of pagemods.values()) { + if (modMatchesURI(pagemod, window.location.href)) + onContent(pagemod, window); + } +} +frames.addEventListener("DOMDocElementInserted", onContentWindow, true); + +function applyOnExistingDocuments (mod) { + for (let frame of frames) { + // Fake a newly created document + let window = frame.content; + // on startup with e10s, contentWindow might not exist yet, + // in which case we will get notified by "document-element-inserted". + if (!window || !window.frames) + return; + let uri = window.location.href; + if (has(mod.attachTo, "top") && modMatchesURI(mod, uri)) + onContent(mod, window); + if (has(mod.attachTo, "frame")) + getFrames(window). + filter(iframe => modMatchesURI(mod, iframe.location.href)). + forEach(frame => onContent(mod, frame)); + } +} + +function createWorker(mod, window) { + let workerId = String(uuid()); + + // Instruct the parent to connect to this worker. Do this first so the parent + // side is connected before the worker attempts to send any messages there + let frame = frames.getFrameForWindow(window.top); + frame.port.emit('sdk/page-mod/worker-create', mod.id, { + id: workerId, + url: window.location.href + }); + + // Create a child worker and notify the parent + let worker = WorkerChild({ + id: workerId, + window: window, + contentScript: mod.contentScript, + contentScriptFile: mod.contentScriptFile, + contentScriptOptions: mod.contentScriptOptions + }); + + once(worker, 'detach', () => worker.destroy()); +} + +function onContent (mod, window) { + let isTopDocument = window.top === window; + // Is a top level document and `top` is not set, ignore + if (isTopDocument && !has(mod.attachTo, "top")) + return; + // Is a frame document and `frame` is not set, ignore + if (!isTopDocument && !has(mod.attachTo, "frame")) + return; + + // ensure we attach only once per document + let seen = mod.seenDocuments; + if (seen.has(window.document)) + return; + seen.set(window.document, true); + + let style = styleFor(mod); + if (style) + attach(style, window); + + // Immediately evaluate content script if the document state is already + // matching contentScriptWhen expectations + if (isMatchingAttachState(mod, window)) { + createWorker(mod, window); + return; + } + + let eventName = getAttachEventType(mod) || 'load'; + domOn(window, eventName, function onReady (e) { + if (e.target.defaultView !== window) + return; + domOff(window, eventName, onReady, true); + createWorker(mod, window); + + // Attaching is asynchronous so if the document is already loaded we will + // miss the pageshow event so send a synthetic one. + if (window.document.readyState == "complete") { + mod.on('attach', worker => { + try { + worker.send('pageshow'); + emit(worker, 'pageshow'); + } + catch (e) { + // This can fail if an earlier attach listener destroyed the worker + } + }); + } + }, true); +} + +function isMatchingAttachState (mod, window) { + let state = window.document.readyState; + return 'start' === mod.contentScriptWhen || + // Is `load` event already dispatched? + 'complete' === state || + // Is DOMContentLoaded already dispatched and waiting for it? + ('ready' === mod.contentScriptWhen && state === 'interactive') +} + +process.port.on('sdk/page-mod/create', (process, model) => { + if (pagemods.has(model.id)) + return; + + new ChildPageMod(model); +}); + +process.port.on('sdk/page-mod/destroy', (process, id) => { + let mod = pagemods.get(id); + if (mod) + mod.destroy(); +}); diff --git a/toolkit/commonjs/sdk/content/page-worker.js b/toolkit/commonjs/sdk/content/page-worker.js new file mode 100644 index 0000000000..cd70902761 --- /dev/null +++ b/toolkit/commonjs/sdk/content/page-worker.js @@ -0,0 +1,153 @@ +/* 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/. */ +"use strict"; + +const { frames } = require("../remote/child"); +const { Class } = require("../core/heritage"); +const { Disposable } = require('../core/disposable'); +const { data } = require("../self"); +const { once } = require("../dom/events"); +const { getAttachEventType } = require("./utils"); +const { Rules } = require('../util/rules'); +const { uuid } = require('../util/uuid'); +const { WorkerChild } = require("./worker-child"); +const { Cc, Ci, Cu } = require("chrome"); +const { observe } = require("../event/chrome"); +const { on } = require("../event/core"); + +const appShell = Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci.nsIAppShellService); + +const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm"); + +const pages = new Map(); + +const DOC_INSERTED = "document-element-inserted"; + +function isValidURL(page, url) { + return !page.rules || page.rules.matchesAny(url); +} + +const ChildPage = Class({ + implements: [ Disposable ], + setup: function(frame, id, options) { + this.id = id; + this.frame = frame; + this.options = options; + + this.webNav = appShell.createWindowlessBrowser(false); + this.docShell.allowJavascript = this.options.allow.script; + + // Accessing the browser's window forces the initial about:blank document to + // be created before we start listening for notifications + this.contentWindow; + + this.webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION); + + pages.set(this.id, this); + + this.contentURL = options.contentURL; + + if (options.include) { + this.rules = Rules(); + this.rules.add.apply(this.rules, [].concat(options.include)); + } + }, + + dispose: function() { + pages.delete(this.id); + this.webProgress.removeProgressListener(this); + this.webNav = null; + }, + + attachWorker: function() { + if (!isValidURL(this, this.contentWindow.location.href)) + return; + + this.options.id = uuid().toString(); + this.options.window = this.contentWindow; + this.frame.port.emit("sdk/frame/connect", this.id, { + id: this.options.id, + url: this.contentWindow.document.documentURIObject.spec + }); + new WorkerChild(this.options); + }, + + get docShell() { + return this.webNav.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell); + }, + + get webProgress() { + return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress); + }, + + get contentWindow() { + return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + }, + + get contentURL() { + return this.options.contentURL; + }, + set contentURL(url) { + this.options.contentURL = url; + + url = this.options.contentURL ? data.url(this.options.contentURL) : "about:blank"; + this.webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null); + }, + + onLocationChange: function(progress, request, location, flags) { + // Ignore inner-frame events + if (progress != this.webProgress) + return; + // Ignore events that don't change the document + if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) + return; + + let event = getAttachEventType(this.options); + // Attaching at the start of the load is handled by the + // document-element-inserted listener. + if (event == DOC_INSERTED) + return; + + once(this.contentWindow, event, () => { + this.attachWorker(); + }, false); + }, + + QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]) +}); + +on(observe(DOC_INSERTED), "data", ({ target }) => { + let page = Array.from(pages.values()).find(p => p.contentWindow.document === target); + if (!page) + return; + + if (getAttachEventType(page.options) == DOC_INSERTED) + page.attachWorker(); +}); + +frames.port.on("sdk/frame/create", (frame, id, options) => { + new ChildPage(frame, id, options); +}); + +frames.port.on("sdk/frame/set", (frame, id, params) => { + let page = pages.get(id); + if (!page) + return; + + if ("allowScript" in params) + page.docShell.allowJavascript = params.allowScript; + if ("contentURL" in params) + page.contentURL = params.contentURL; +}); + +frames.port.on("sdk/frame/destroy", (frame, id) => { + let page = pages.get(id); + if (!page) + return; + + page.destroy(); +}); diff --git a/toolkit/commonjs/sdk/content/sandbox.js b/toolkit/commonjs/sdk/content/sandbox.js index 9fef60cca1..096ba5c870 100644 --- a/toolkit/commonjs/sdk/content/sandbox.js +++ b/toolkit/commonjs/sdk/content/sandbox.js @@ -10,6 +10,7 @@ module.metadata = { const { Class } = require('../core/heritage'); const { EventTarget } = require('../event/target'); const { on, off, emit } = require('../event/core'); +const { events } = require('./sandbox/events'); const { requiresAddonGlobal } = require('./utils'); const { delay: async } = require('../lang/functional'); const { Ci, Cu, Cc } = require('chrome'); @@ -17,10 +18,10 @@ const timer = require('../timers'); const { URL } = require('../url'); const { sandbox, evaluate, load } = require('../loader/sandbox'); const { merge } = require('../util/object'); -const { getTabForContentWindow } = require('../tabs/utils'); +const { getTabForContentWindowNoShim } = require('../tabs/utils'); const { getInnerId } = require('../window/utils'); const { PlainTextConsole } = require('../console/plain-text'); -const { data } = require('../self'); +const { data } = require('../self');const { isChildLoader } = require('../remote/core'); // WeakMap of sandboxes so we can access private values const sandboxes = new WeakMap(); @@ -28,7 +29,7 @@ const sandboxes = new WeakMap(); require('./content-worker.js'); Then, retrieve URL of these files in the XPI: */ -let prefix = module.uri.split('sandbox.js')[0]; +var prefix = module.uri.split('sandbox.js')[0]; const CONTENT_WORKER_URL = prefix + 'content-worker.js'; const metadata = require('@loader/options').metadata; @@ -47,6 +48,19 @@ const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]. const JS_VERSION = '1.8'; +// Tests whether this window is loaded in a tab +function isWindowInTab(window) { + if (isChildLoader) { + let { frames } = require('../remote/child'); + let frame = frames.getFrameForWindow(window.top); + return frame && frame.isTab; + } + else { + // The deprecated sync worker API still does everything in the main process + return getTabForContentWindowNoShim(window); + } +} + const WorkerSandbox = Class({ implements: [ EventTarget ], @@ -102,7 +116,7 @@ const WorkerSandbox = Class({ // (This behavior can be turned off for now with the unsafe-content-script // flag to give addon developers time for making the necessary changes) // But prevent it when the Worker isn't used for a content script but for - // injecting `addon` object into a Panel, Widget, ... scope. + // injecting `addon` object into a Panel scope, for example. // That's because: // 1/ It is useless to use multiple domains as the worker is only used // to communicate with the addon, @@ -158,6 +172,7 @@ const WorkerSandbox = Class({ return parent; } }); + // Use the Greasemonkey naming convention to provide access to the // unwrapped window object so the content script can access document // JavaScript values. @@ -206,9 +221,9 @@ const WorkerSandbox = Class({ } // Inject our `console` into target document if worker doesn't have a tab - // (e.g Panel, PageWorker, Widget). + // (e.g Panel, PageWorker). // `worker.tab` can't be used because bug 804935. - if (!getTabForContentWindow(window)) { + if (!isWindowInTab(window)) { let win = getUnsafeWindow(window); // export our chrome console to content window, as described here: @@ -235,8 +250,16 @@ const WorkerSandbox = Class({ timeEnd: genPropDesc('timeEnd'), profile: genPropDesc('profile'), profileEnd: genPropDesc('profileEnd'), - __noSuchMethod__: { enumerable: true, configurable: true, writable: true, - value: function() {} } + exception: genPropDesc('exception'), + assert: genPropDesc('assert'), + count: genPropDesc('count'), + table: genPropDesc('table'), + clear: genPropDesc('clear'), + dirxml: genPropDesc('dirxml'), + markTimeline: genPropDesc('markTimeline'), + timeline: genPropDesc('timeline'), + timelineEnd: genPropDesc('timelineEnd'), + timeStamp: genPropDesc('timeStamp'), }; Object.defineProperties(con, properties); @@ -245,6 +268,11 @@ const WorkerSandbox = Class({ win.console = con; }; + emit(events, "content-script-before-inserted", { + window: window, + worker: worker + }); + // The order of `contentScriptFile` and `contentScript` evaluation is // intentional, so programs can load libraries like jQuery from script URLs // and use them in scripts. @@ -257,6 +285,7 @@ const WorkerSandbox = Class({ if (contentScriptFile) importScripts.apply(null, [this].concat(contentScriptFile)); + if (contentScript) { evaluateIn( this, diff --git a/toolkit/commonjs/sdk/content/sandbox/events.js b/toolkit/commonjs/sdk/content/sandbox/events.js new file mode 100644 index 0000000000..d6f7eb0040 --- /dev/null +++ b/toolkit/commonjs/sdk/content/sandbox/events.js @@ -0,0 +1,12 @@ +/* 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/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const events = {}; +exports.events = events; diff --git a/toolkit/commonjs/sdk/content/tab-events.js b/toolkit/commonjs/sdk/content/tab-events.js new file mode 100644 index 0000000000..45363bec66 --- /dev/null +++ b/toolkit/commonjs/sdk/content/tab-events.js @@ -0,0 +1,42 @@ +/* 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/. */ +"use strict"; + +const system = require('sdk/system/events'); +const { frames } = require('sdk/remote/child'); +const { WorkerChild } = require('sdk/content/worker-child'); + +// map observer topics to tab event names +const EVENTS = { + 'content-document-interactive': 'ready', + 'chrome-document-interactive': 'ready', + 'content-document-loaded': 'load', + 'chrome-document-loaded': 'load', +// 'content-page-shown': 'pageshow', // bug 1024105 +} + +function topicListener({ subject, type }) { + let window = subject.defaultView; + if (!window) + return; + let frame = frames.getFrameForWindow(subject.defaultView); + if (frame) + frame.port.emit('sdk/tab/event', EVENTS[type]); +} + +for (let topic in EVENTS) + system.on(topic, topicListener, true); + +// bug 1024105 - content-page-shown notification doesn't pass persisted param +function eventListener({target, type, persisted}) { + let frame = this; + if (target === frame.content.document) + frame.port.emit('sdk/tab/event', type, persisted); +} +frames.addEventListener('pageshow', eventListener, true); + +frames.port.on('sdk/tab/attach', (frame, options) => { + options.window = frame.content; + new WorkerChild(options); +}); diff --git a/toolkit/commonjs/sdk/content/utils.js b/toolkit/commonjs/sdk/content/utils.js index ae1cbf6cc7..4ef4e67cd0 100644 --- a/toolkit/commonjs/sdk/content/utils.js +++ b/toolkit/commonjs/sdk/content/utils.js @@ -7,11 +7,12 @@ module.metadata = { 'stability': 'unstable' }; -let { merge } = require('../util/object'); -let { data } = require('../self'); -let assetsURI = data.url(); -let isArray = Array.isArray; -let method = require('../../method/core'); +var { merge } = require('../util/object'); +var { data } = require('../self'); +var assetsURI = data.url(); +var isArray = Array.isArray; +var method = require('../../method/core'); +var { uuid } = require('../util/uuid'); const isAddonContent = ({ contentURL }) => contentURL && data.url(contentURL).startsWith(assetsURI); @@ -42,13 +43,16 @@ function getAttachEventType(model) { } exports.getAttachEventType = getAttachEventType; -let attach = method('worker-attach'); +var attach = method('worker-attach'); exports.attach = attach; -let detach = method('worker-detach'); +var connect = method('worker-connect'); +exports.connect = connect; + +var detach = method('worker-detach'); exports.detach = detach; -let destroy = method('worker-destroy'); +var destroy = method('worker-destroy'); exports.destroy = destroy; function WorkerHost (workerFor) { @@ -81,3 +85,21 @@ function WorkerHost (workerFor) { } } exports.WorkerHost = WorkerHost; + +function makeChildOptions(options) { + function makeStringArray(arrayOrValue) { + if (!arrayOrValue) + return []; + return [String(v) for (v of [].concat(arrayOrValue))]; + } + + return { + id: String(uuid()), + contentScript: makeStringArray(options.contentScript), + contentScriptFile: makeStringArray(options.contentScriptFile), + contentScriptOptions: options.contentScriptOptions ? + JSON.stringify(options.contentScriptOptions) : + null, + } +} +exports.makeChildOptions = makeChildOptions; diff --git a/toolkit/commonjs/sdk/content/worker-child.js b/toolkit/commonjs/sdk/content/worker-child.js index 8303b3b1ad..2936e0dd74 100644 --- a/toolkit/commonjs/sdk/content/worker-child.js +++ b/toolkit/commonjs/sdk/content/worker-child.js @@ -5,12 +5,15 @@ const { merge } = require('../util/object'); const { Class } = require('../core/heritage'); +const { emit } = require('../event/core'); const { EventTarget } = require('../event/target'); const { getInnerId, getByInnerId } = require('../window/utils'); const { instanceOf, isObject } = require('../lang/type'); -const { on: observe } = require('../system/events'); +const system = require('../system/events'); +const { when } = require('../system/unload'); const { WorkerSandbox } = require('./sandbox'); const { Ci } = require('chrome'); +const { process, frames } = require('../remote/child'); const EVENTS = { 'chrome-page-shown': 'pageshow', @@ -20,10 +23,19 @@ const EVENTS = { 'inner-window-destroyed': 'detach', } +// The parent Worker must have been created (or an async message sent to spawn +// its creation) before creating the WorkerChild or messages from the content +// script to the parent will get lost. const WorkerChild = Class({ implements: [EventTarget], + initialize(options) { merge(this, options); + keepAlive.set(this.id, this); + + this.windowId = getInnerId(this.window); + if (this.contentScriptOptions) + this.contentScriptOptions = JSON.parse(this.contentScriptOptions); this.port = EventTarget(); this.port.on('*', this.send.bind(this, 'event')); @@ -32,57 +44,79 @@ const WorkerChild = Class({ this.observe = this.observe.bind(this); for (let topic in EVENTS) - observe(topic, this.observe); + system.on(topic, this.observe); this.receive = this.receive.bind(this); - this.manager.addMessageListener('sdk/worker/message', this.receive); + process.port.on('sdk/worker/message', this.receive); - let window = getByInnerId(this.window); - this.sandbox = WorkerSandbox(this, window); + this.sandbox = WorkerSandbox(this, this.window); - if (options.currentReadyState != "complete" && - window.document.readyState == "complete") { - // If we attempted to attach the worker before the document was loaded but - // it has now completed loading then the parent should reasonably expect - // to see a pageshow event. - this.sandbox.emitSync("pageshow"); - this.send("pageshow"); - } + // If the document is still loading wait for it to finish before passing on + // received messages + this.frozen = this.window.document.readyState == "loading"; + this.frozenMessages = []; + this.on('pageshow', () => { + this.frozen = false; + this.frozenMessages.forEach(args => this.sandbox.emit(...args)); + this.frozenMessages = []; + }); + this.on('pagehide', () => { + this.frozen = true; + }); }, + // messages - receive({ data: { id, args }}) { + receive(process, id, args) { if (id !== this.id) return; - this.sandbox.emit(...args); + args = JSON.parse(args); + + if (this.frozen) + this.frozenMessages.push(args); + else + this.sandbox.emit(...args); + if (args[0] === 'detach') this.destroy(args[1]); }, + send(...args) { - args = JSON.parse(JSON.stringify(args, exceptions)); - if (this.manager.content) - this.manager.sendAsyncMessage('sdk/worker/event', { id: this.id, args }); + process.port.emit('sdk/worker/event', this.id, JSON.stringify(args, exceptions)); }, + // notifications observe({ type, subject }) { if (!this.sandbox) return; - if (subject.defaultView && getInnerId(subject.defaultView) === this.window) { + + if (subject.defaultView && getInnerId(subject.defaultView) === this.windowId) { this.sandbox.emitSync(EVENTS[type]); - this.send(EVENTS[type]); + emit(this, EVENTS[type]); } + if (type === 'inner-window-destroyed' && - subject.QueryInterface(Ci.nsISupportsPRUint64).data === this.window) { + subject.QueryInterface(Ci.nsISupportsPRUint64).data === this.windowId) { this.destroy(); } }, + + get frame() { + return frames.getFrameForWindow(this.window.top); + }, + // detach/destroy: unload and release the sandbox destroy(reason) { if (!this.sandbox) return; - if (this.manager.content) - this.manager.removeMessageListener('sdk/worker/message', this.receive); + + for (let topic in EVENTS) + system.off(topic, this.observe); + process.port.off('sdk/worker/message', this.receive); + this.sandbox.destroy(reason); this.sandbox = null; + keepAlive.delete(this.id); + this.send('detach'); } }) @@ -96,3 +130,19 @@ function exceptions(key, value) { let { message, fileName, lineNumber, stack, name } = value; return { _errorType, message, fileName, lineNumber, stack, name }; } + +// workers for windows in this tab +var keepAlive = new Map(); + +process.port.on('sdk/worker/create', (process, options) => { + options.window = getByInnerId(options.windowId); + if (!options.window) + return; + + let worker = new WorkerChild(options); +}); + +when(reason => { + for (let worker of keepAlive.values()) + worker.destroy(reason); +}); diff --git a/toolkit/commonjs/sdk/content/worker.js b/toolkit/commonjs/sdk/content/worker.js index 51a917f2fd..463cb9f57d 100644 --- a/toolkit/commonjs/sdk/content/worker.js +++ b/toolkit/commonjs/sdk/content/worker.js @@ -8,178 +8,182 @@ module.metadata = { }; const { emit } = require('../event/core'); -const { omit } = require('../util/object'); +const { omit, merge } = require('../util/object'); const { Class } = require('../core/heritage'); const { method } = require('../lang/functional'); const { getInnerId } = require('../window/utils'); const { EventTarget } = require('../event/target'); -const { when, ensure } = require('../system/unload'); -const { getTabForWindow } = require('../tabs/helpers'); -const { getTabForContentWindow, getBrowserForTab } = require('../tabs/utils'); const { isPrivate } = require('../private-browsing/utils'); -const { getFrameElement } = require('../window/utils'); -const { attach, detach, destroy } = require('./utils'); +const { getTabForBrowser, getTabForContentWindowNoShim, getBrowserForTab } = require('../tabs/utils'); +const { attach, connect, detach, destroy, makeChildOptions } = require('./utils'); +const { ensure } = require('../system/unload'); const { on: observe } = require('../system/events'); -const { uuid } = require('../util/uuid'); -const { Ci, Cc } = require('chrome'); - -const ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]. - getService(Ci.nsIMessageBroadcaster); - -// null-out cycles in .modules to make @loader/options JSONable -const ADDON = omit(require('@loader/options'), ['modules', 'globals']); +const { Ci, Cu } = require('chrome'); +const { modelFor: tabFor } = require('sdk/model/core'); +const { remoteRequire, processes, frames } = require('../remote/parent'); +remoteRequire('sdk/content/worker-child'); const workers = new WeakMap(); -let modelFor = (worker) => workers.get(worker); +var modelFor = (worker) => workers.get(worker); const ERR_DESTROYED = "Couldn't find the worker to receive this message. " + "The script may not be initialized yet, or may already have been unloaded."; -const ERR_FROZEN = "The page is currently hidden and can no longer be used " + - "until it is visible again."; - // a handle for communication between content script and addon code const Worker = Class({ implements: [EventTarget], + initialize(options = {}) { + ensure(this, 'detach'); let model = { - inited: false, - earlyEvents: [], // fired before worker was inited - frozen: true, // document is in BFcache, let it go + attached: false, + destroyed: false, + earlyEvents: [], // fired before worker was attached + frozen: true, // document is not yet active options, }; workers.set(this, model); - ensure(this, 'destroy'); this.on('detach', this.detach); EventTarget.prototype.initialize.call(this, options); this.receive = this.receive.bind(this); - model.observe = ({ subject }) => { - let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data; - if (model.window && getInnerId(model.window) === id) - this.detach(); - } - - observe('inner-window-destroyed', model.observe); - this.port = EventTarget(); this.port.emit = this.send.bind(this, 'event'); this.postMessage = this.send.bind(this, 'message'); - if ('window' in options) - attach(this, options.window); + if ('window' in options) { + let window = options.window; + delete options.window; + attach(this, window); + } }, + // messages - receive({ data: { id, args }}) { + receive(process, id, args) { let model = modelFor(this); - if (id !== model.id || !model.childWorker) + if (id !== model.id || !model.attached) return; + args = JSON.parse(args); + if (model.destroyed && args[0] != 'detach') + return; + if (args[0] === 'event') emit(this.port, ...args.slice(1)) else emit(this, ...args); }, + send(...args) { let model = modelFor(this); - if (!model.inited) { + if (model.destroyed && args[0] !== 'detach') + throw new Error(ERR_DESTROYED); + + if (!model.attached) { model.earlyEvents.push(args); return; } - if (!model.childWorker && args[0] !== 'detach') - throw new Error(ERR_DESTROYED); - if (model.frozen && args[0] !== 'detach') - throw new Error(ERR_FROZEN); - try { - model.manager.sendAsyncMessage('sdk/worker/message', { id: model.id, args }); - } catch (e) { - // - } + + processes.port.emit('sdk/worker/message', model.id, JSON.stringify(args)); }, + // properties get url() { - let { window } = modelFor(this); - return window && window.document.location.href; + let { url } = modelFor(this); + return url; }, + get contentURL() { - let { window } = modelFor(this); - return window && window.document.URL; + return this.url; }, + get tab() { - let { window } = modelFor(this); - return window && getTabForWindow(window); + require('sdk/tabs'); + let { frame } = modelFor(this); + if (!frame) + return null; + let rawTab = getTabForBrowser(frame.frameElement); + return rawTab && tabFor(rawTab); }, + toString: () => '[object Worker]', - // methods - attach: method(attach), + detach: method(detach), destroy: method(destroy), }) exports.Worker = Worker; attach.define(Worker, function(worker, window) { + // This method of attaching should be deprecated + + if (Cu.isCrossProcessWrapper(window)) + throw new Error("Attaching worker to a window from another " + + "process directly is not supported."); + let model = modelFor(worker); + if (model.attached) + detach(worker); model.window = window; - model.options.window = getInnerId(window); - model.options.currentReadyState = window.document.readyState; - model.id = model.options.id = String(uuid()); + let frame = null; + let tab = getTabForContentWindowNoShim(window); + if (tab) + frame = frames.getFrameForBrowser(getBrowserForTab(tab)); - let tab = getTabForContentWindow(window); - if (tab) { - model.manager = getBrowserForTab(tab).messageManager; - } else { - model.manager = getFrameElement(window.top).frameLoader.messageManager; - } + let childOptions = makeChildOptions(model.options); + childOptions.windowId = getInnerId(window); - model.manager.addMessageListener('sdk/worker/event', worker.receive); - model.manager.addMessageListener('sdk/worker/attach', attach); + processes.port.emit('sdk/worker/create', childOptions); - model.manager.sendAsyncMessage('sdk/worker/create', { - options: model.options, - addon: ADDON - }); - - function attach({ data }) { - if (data.id !== model.id) - return; - model.manager.removeMessageListener('sdk/worker/attach', attach); - model.childWorker = true; - - worker.on('pageshow', () => model.frozen = false); - worker.on('pagehide', () => model.frozen = true); - - model.inited = true; - model.frozen = false; - - model.earlyEvents.forEach(args => worker.send(...args)); - emit(worker, 'attach', window); - } + connect(worker, frame, { id: childOptions.id, url: String(window.location) }); }) +connect.define(Worker, function(worker, frame, { id, url }) { + let model = modelFor(worker); + if (model.attached) + detach(worker); + + model.id = id; + model.frame = frame; + model.url = url; + + // Messages from content -> chrome come through the process message manager + // since that lives longer than the frame message manager + processes.port.on('sdk/worker/event', worker.receive); + + model.attached = true; + model.destroyed = false; + model.frozen = false; + + model.earlyEvents.forEach(args => worker.send(...args)); + model.earlyEvents = []; + emit(worker, 'attach', model.window); +}); + // unload and release the child worker, release window reference -detach.define(Worker, function(worker, reason) { +detach.define(Worker, function(worker) { let model = modelFor(worker); - worker.send('detach', reason); - if (!model.childWorker) + if (!model.attached) return; - model.childWorker = null; - model.earlyEvents = []; + processes.port.off('sdk/worker/event', worker.receive); + model.attached = false; + model.destroyed = true; model.window = null; emit(worker, 'detach'); - model.manager.removeMessageListener('sdk/worker/event', this.receive); -}) +}); isPrivate.define(Worker, ({ tab }) => isPrivate(tab)); -// unlod worker, release references +// Something in the parent side has destroyed the worker, tell the child to +// detach, the child will respond when it has detached destroy.define(Worker, function(worker, reason) { - detach(worker, reason); - modelFor(worker).inited = true; -}) + let model = modelFor(worker); + model.destroyed = true; + if (!model.attached) + return; -// unload Loaders used for creating WorkerChild instances in each process -when(() => ppmm.broadcastAsyncMessage('sdk/loader/unload', { data: ADDON })); + worker.send('detach', reason); +}); diff --git a/toolkit/commonjs/sdk/core/observer.js b/toolkit/commonjs/sdk/core/observer.js index 786650e1c8..7e11bf8f93 100644 --- a/toolkit/commonjs/sdk/core/observer.js +++ b/toolkit/commonjs/sdk/core/observer.js @@ -9,14 +9,17 @@ module.metadata = { }; -const { Cc, Ci, Cr } = require("chrome"); +const { Cc, Ci, Cr, Cu } = require("chrome"); const { Class } = require("./heritage"); const { isWeak } = require("./reference"); const method = require("../../method/core"); -const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1']. - getService(Ci.nsIObserverService); +const observerService = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); +const addObserver = ShimWaiver.getProperty(observerService, "addObserver"); +const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver"); // This is a method that will be invoked when notification observer // subscribed to occurs. diff --git a/toolkit/commonjs/sdk/dom/events-shimmed.js b/toolkit/commonjs/sdk/dom/events-shimmed.js new file mode 100644 index 0000000000..7a17276812 --- /dev/null +++ b/toolkit/commonjs/sdk/dom/events-shimmed.js @@ -0,0 +1,18 @@ +/* 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/. */ + +'use strict'; + +module.metadata = { + 'stability': 'unstable' +}; + +const events = require('./events.js'); + +exports.emit = (element, type, obj) => events.emit(element, type, obj, true); +exports.on = (element, type, listener, capture) => events.on(element, type, listener, capture, true); +exports.once = (element, type, listener, capture) => events.once(element, type, listener, capture, true); +exports.removeListener = (element, type, listener, capture) => events.removeListener(element, type, listener, capture, true); +exports.removed = events.removed; +exports.when = (element, eventName, capture) => events.when(element, eventName, capture ? capture : false, true); diff --git a/toolkit/commonjs/sdk/dom/events.js b/toolkit/commonjs/sdk/dom/events.js index bdf108312a..502d2350f0 100644 --- a/toolkit/commonjs/sdk/dom/events.js +++ b/toolkit/commonjs/sdk/dom/events.js @@ -8,6 +8,9 @@ module.metadata = { "stability": "unstable" }; +const { Cu } = require("chrome"); +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); + // Utility function that returns copy of the given `text` with last character // removed if it is `"s"`. function singularify(text) { @@ -44,10 +47,14 @@ function getInitializerName(category) { * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) * for a detailed explanation. */ -function on(element, type, listener, capture) { +function on(element, type, listener, capture, shimmed = false) { // `capture` defaults to `false`. capture = capture || false; - element.addEventListener(type, listener, capture); + if (shimmed) { + element.addEventListener(type, listener, capture); + } else { + ShimWaiver.getProperty(element, "addEventListener")(type, listener, capture); + } } exports.on = on; @@ -73,11 +80,11 @@ exports.on = on; * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) * for a detailed explanation. */ -function once(element, type, listener, capture) { +function once(element, type, listener, capture, shimmed = false) { on(element, type, function selfRemovableListener(event) { - removeListener(element, type, selfRemovableListener, capture); + removeListener(element, type, selfRemovableListener, capture, shimmed); listener.apply(this, arguments); - }, capture); + }, capture, shimmed); } exports.once = once; @@ -103,8 +110,12 @@ exports.once = once; * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) * for a detailed explanation. */ -function removeListener(element, type, listener, capture) { - element.removeEventListener(type, listener, capture); +function removeListener(element, type, listener, capture, shimmed = false) { + if (shimmed) { + element.removeEventListener(type, listener, capture); + } else { + ShimWaiver.getProperty(element, "removeEventListener")(type, listener, capture); + } } exports.removeListener = removeListener; @@ -128,13 +139,17 @@ exports.removeListener = removeListener; * initializer after firs `type` argument. * @see https://developer.mozilla.org/En/DOM/Document.createEvent */ -function emit(element, type, { category, initializer, settings }) { +function emit(element, type, { category, initializer, settings }, shimmed = false) { category = category || "UIEvents"; initializer = initializer || getInitializerName(category); let document = element.ownerDocument; let event = document.createEvent(category); event[initializer].apply(event, [type].concat(settings)); - element.dispatchEvent(event); + if (shimmed) { + element.dispatchEvent(event); + } else { + ShimWaiver.getProperty(element, "dispatchEvent")(event); + } }; exports.emit = emit; @@ -158,12 +173,20 @@ const removed = element => { }; exports.removed = removed; -const when = (element, eventName, capture=false) => new Promise(resolve => { +const when = (element, eventName, capture=false, shimmed=false) => new Promise(resolve => { const listener = event => { - element.removeEventListener(eventName, listener, capture); + if (shimmed) { + element.removeEventListener(eventName, listener, capture); + } else { + ShimWaiver.getProperty(element, "removeEventListener")(eventName, listener, capture); + } resolve(event); }; - element.addEventListener(eventName, listener, capture); + if (shimmed) { + element.addEventListener(eventName, listener, capture); + } else { + ShimWaiver.getProperty(element, "addEventListener")(eventName, listener, capture); + } }); exports.when = when; diff --git a/toolkit/commonjs/sdk/event/chrome.js b/toolkit/commonjs/sdk/event/chrome.js index 41b7a34df0..9044fef99e 100644 --- a/toolkit/commonjs/sdk/event/chrome.js +++ b/toolkit/commonjs/sdk/event/chrome.js @@ -8,10 +8,15 @@ module.metadata = { "stability": "unstable" }; -const { Cc, Ci, Cr } = require("chrome"); +const { Cc, Ci, Cr, Cu } = require("chrome"); const { emit, on, off } = require("./core"); -const { addObserver, removeObserver } = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); +var observerService = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); +const addObserver = ShimWaiver.getProperty(observerService, "addObserver"); +const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver"); + const { when: unload } = require("../system/unload"); // Simple class that can be used to instantiate event channel that diff --git a/toolkit/commonjs/sdk/event/core.js b/toolkit/commonjs/sdk/event/core.js index 67e2618094..c16dd2df57 100644 --- a/toolkit/commonjs/sdk/event/core.js +++ b/toolkit/commonjs/sdk/event/core.js @@ -46,6 +46,10 @@ function on(target, type, listener) { } exports.on = on; + +var onceWeakMap = new WeakMap(); + + /** * Registers an event `listener` that is called only the next time an event * of the specified `type` is emitted on the given event `target`. @@ -57,10 +61,13 @@ exports.on = on; * The listener function that processes the event. */ function once(target, type, listener) { - on(target, type, function observer(...args) { + let replacement = function observer(...args) { off(target, type, observer); + onceWeakMap.delete(listener); listener.apply(target, args); - }); + }; + onceWeakMap.set(listener, replacement); + on(target, type, replacement); } exports.once = once; @@ -79,6 +86,14 @@ exports.once = once; * Arguments that will be passed to listeners. */ function emit (target, type, ...args) { + emitOnObject(target, type, target, ...args); +} +exports.emit = emit; + +/** + * A variant of emit that allows setting the this property for event listeners + */ +function emitOnObject(target, type, thisArg, ...args) { let all = observers(target, '*').length; let state = observers(target, type); let listeners = state.slice(); @@ -94,7 +109,7 @@ function emit (target, type, ...args) { let listener = listeners[index]; // Dispatch only if listener is still registered. if (~state.indexOf(listener)) - listener.apply(target, args); + listener.apply(thisArg, args); } catch (error) { // If exception is not thrown by a error listener and error listener is @@ -107,7 +122,7 @@ function emit (target, type, ...args) { // Also emit on `"*"` so that one could listen for all events. if (type !== '*') emit(target, '*', type, ...args); } -exports.emit = emit; +exports.emitOnObject = emitOnObject; /** * Removes an event `listener` for the given event `type` on the given event @@ -124,6 +139,11 @@ exports.emit = emit; function off(target, type, listener) { let length = arguments.length; if (length === 3) { + if (onceWeakMap.has(listener)) { + listener = onceWeakMap.get(listener); + onceWeakMap.delete(listener); + } + let listeners = observers(target, type); let index = listeners.indexOf(listener); if (~index) diff --git a/toolkit/commonjs/sdk/event/dom.js b/toolkit/commonjs/sdk/event/dom.js index 54963230ff..df85f09e3f 100644 --- a/toolkit/commonjs/sdk/event/dom.js +++ b/toolkit/commonjs/sdk/event/dom.js @@ -10,17 +10,24 @@ module.metadata = { const { Ci } = require("chrome"); -let { emit } = require("./core"); -let { when: unload } = require("../system/unload"); -let listeners = new Map(); +var { emit } = require("./core"); +var { when: unload } = require("../system/unload"); +var listeners = new Map(); -let getWindowFrom = x => +const { Cu } = require("chrome"); +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); + +var getWindowFrom = x => x instanceof Ci.nsIDOMWindow ? x : x instanceof Ci.nsIDOMDocument ? x.defaultView : x instanceof Ci.nsIDOMNode ? x.ownerDocument.defaultView : null; function removeFromListeners() { + ShimWaiver.getProperty(this, "removeEventListener")("DOMWindowClose", removeFromListeners); + for (let cleaner of listeners.get(this)) + cleaner(); + listeners.delete(this); } @@ -45,26 +52,25 @@ function open(target, type, options) { if (!window) throw new Error("Unable to obtain the owner window from the target given."); - let cleaners = listeners.get(window) || []; - cleaners.push(() => target.removeEventListener(type, listener, capture)); + let cleaners = listeners.get(window); + if (!cleaners) { + cleaners = []; + listeners.set(window, cleaners); - listeners.set(window, cleaners); + // We need to remove from our map the `window` once is closed, to prevent + // memory leak + ShimWaiver.getProperty(window, "addEventListener")("DOMWindowClose", removeFromListeners); + } - // We need to remove from our map the `window` once is closed, to prevent - // memory leak - window.addEventListener("DOMWindowClose", removeFromListeners); - - target.addEventListener(type, listener, capture); + cleaners.push(() => ShimWaiver.getProperty(target, "removeEventListener")(type, listener, capture)); + ShimWaiver.getProperty(target, "addEventListener")(type, listener, capture); return output; } unload(() => { - for (let [window, cleaners] of listeners) { - cleaners.forEach(callback => callback()) - } - - listeners.clear(); + for (let window of listeners.keys()) + removeFromListeners.call(window); }); exports.open = open; diff --git a/toolkit/commonjs/sdk/event/utils.js b/toolkit/commonjs/sdk/event/utils.js index 65456ff574..ec9b61d87e 100644 --- a/toolkit/commonjs/sdk/event/utils.js +++ b/toolkit/commonjs/sdk/event/utils.js @@ -7,7 +7,7 @@ module.metadata = { "stability": "unstable" }; -let { emit, on, once, off, EVENT_TYPE_PATTERN } = require("./core"); +var { emit, on, once, off, EVENT_TYPE_PATTERN } = require("./core"); // This module provides set of high order function for working with event // streams (streams in a NodeJS style that dispatch data, end and error @@ -17,7 +17,7 @@ let { emit, on, once, off, EVENT_TYPE_PATTERN } = require("./core"); // (non property references) it keeps. This basically allows defining // references between objects without storing the explicitly. See transform for // more details. -let refs = (function() { +var refs = (function() { let refSets = new WeakMap(); return function refs(target) { if (!refSets.has(target)) refSets.set(target, new Set()); diff --git a/toolkit/commonjs/sdk/frame/utils.js b/toolkit/commonjs/sdk/frame/utils.js index 800b20083f..d9fccec4d5 100644 --- a/toolkit/commonjs/sdk/frame/utils.js +++ b/toolkit/commonjs/sdk/frame/utils.js @@ -57,38 +57,31 @@ function create(target, options) { frame.setAttribute('type', options.type || 'content'); frame.setAttribute('src', options.uri || 'about:blank'); + // Must set the remote attribute before attaching the frame to the document + if (remote && isXUL) { + // We remove XBL binding to avoid execution of code that is not going to + // work because browser has no docShell attribute in remote mode + // (for example) + frame.setAttribute('style', '-moz-binding: none;'); + frame.setAttribute('remote', 'true'); + } + target.appendChild(frame); // Load in separate process if `options.remote` is `true`. // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347 - if (remote) { - if (isXUL) { - // We remove XBL binding to avoid execution of code that is not going to - // work because browser has no docShell attribute in remote mode - // (for example) - frame.setAttribute('style', '-moz-binding: none;'); - frame.setAttribute('remote', 'true'); - } - else { - frame.QueryInterface(Ci.nsIMozBrowserFrame); - frame.createRemoteFrameLoader(null); - } + if (remote && !isXUL) { + frame.QueryInterface(Ci.nsIMozBrowserFrame); + frame.createRemoteFrameLoader(null); } - - // If browser is remote it won't have a `docShell`. if (!remote) { let docShell = getDocShell(frame); docShell.allowAuth = options.allowAuth || false; docShell.allowJavascript = options.allowJavascript || false; docShell.allowPlugins = options.allowPlugins || false; - - // Control whether the document can move/resize the window. Requires - // recently added platform capability, so we test to avoid exceptions - // in cases where capability is not present yet. - if ("allowWindowControl" in docShell && "allowWindowControl" in options) - docShell.allowWindowControl = !!options.allowWindowControl; + docShell.allowWindowControl = options.allowWindowControl || false; } return frame; diff --git a/toolkit/commonjs/sdk/input/system.js b/toolkit/commonjs/sdk/input/system.js index 7ce07aab1b..66bc6daeca 100644 --- a/toolkit/commonjs/sdk/input/system.js +++ b/toolkit/commonjs/sdk/input/system.js @@ -9,8 +9,12 @@ const { once, off } = require("../event/core"); const { id: addonID } = require("../self"); const unloadMessage = require("@loader/unload"); -const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1']. - getService(Ci.nsIObserverService); +const observerService = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); +const addObserver = ShimWaiver.getProperty(observerService, "addObserver"); +const removeObserver = ShimWaiver.getProperty(observerService, "removeObserver"); + const addonUnloadTopic = "sdk:loader:destroy"; diff --git a/toolkit/commonjs/sdk/keyboard/observer.js b/toolkit/commonjs/sdk/keyboard/observer.js index b3d672660d..b8e32b95cb 100644 --- a/toolkit/commonjs/sdk/keyboard/observer.js +++ b/toolkit/commonjs/sdk/keyboard/observer.js @@ -50,7 +50,8 @@ const Observer = Class({ * Keyboard event being emitted. */ handleEvent(event) { - emit(this, event.type, event, event.target.ownerDocument.defaultView); + emit(this, event.type, event, event.target.ownerDocument ? event.target.ownerDocument.defaultView + : undefined); } }); diff --git a/toolkit/commonjs/sdk/loader/sandbox.js b/toolkit/commonjs/sdk/loader/sandbox.js index e58f34461b..912d0ad882 100644 --- a/toolkit/commonjs/sdk/loader/sandbox.js +++ b/toolkit/commonjs/sdk/loader/sandbox.js @@ -12,7 +12,7 @@ const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')(); const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']. getService(Ci.mozIJSSubScriptLoader); const self = require('sdk/self'); -const { getTabId, getTabForContentWindow } = require('../tabs/utils'); +const { getTabId } = require('../tabs/utils'); const { getInnerId } = require('../window/utils'); const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); diff --git a/toolkit/commonjs/sdk/page-worker.js b/toolkit/commonjs/sdk/page-worker.js index dbf83cb9cd..837cf774be 100644 --- a/toolkit/commonjs/sdk/page-worker.js +++ b/toolkit/commonjs/sdk/page-worker.js @@ -8,50 +8,61 @@ module.metadata = { }; const { Class } = require('./core/heritage'); -const { on, emit, off, setListeners } = require('./event/core'); -const { filter, pipe, map, merge: streamMerge, stripListeners } = require('./event/utils'); -const { detach, attach, destroy, WorkerHost } = require('./content/utils'); +const { ns } = require('./core/namespace'); +const { pipe, stripListeners } = require('./event/utils'); +const { connect, destroy, WorkerHost } = require('./content/utils'); const { Worker } = require('./content/worker'); const { Disposable } = require('./core/disposable'); -const { WeakReference } = require('./core/reference'); const { EventTarget } = require('./event/target'); -const { unload } = require('./system/unload'); -const { events, streamEventsFrom } = require('./content/events'); -const { getAttachEventType } = require('./content/utils'); +const { setListeners } = require('./event/core'); const { window } = require('./addon/window'); -const { getParentWindow } = require('./window/utils'); const { create: makeFrame, getDocShell } = require('./frame/utils'); const { contract } = require('./util/contract'); const { contract: loaderContract } = require('./content/loader'); -const { has } = require('./util/array'); const { Rules } = require('./util/rules'); const { merge } = require('./util/object'); -const { data } = require('./self'); +const { uuid } = require('./util/uuid'); +const { useRemoteProcesses, remoteRequire, frames } = require("./remote/parent"); +remoteRequire("sdk/content/page-worker"); -const views = new WeakMap(); const workers = new WeakMap(); -const pages = new WeakMap(); +const pages = new Map(); -const readyEventNames = [ - 'DOMContentLoaded', - 'document-element-inserted', - 'load' -]; +const internal = ns(); -function workerFor(page) { - return workers.get(page); -} -function pageFor(view) { - return pages.get(view); -} -function viewFor(page) { - return views.get(page); -} -function isDisposed (page) { - return !views.get(page, false); +let workerFor = (page) => workers.get(page); +let isDisposed = (page) => !pages.has(internal(page).id); + +// The frame is used to ensure we have a remote process to load workers in +let remoteFrame = null; +let framePromise = null; +function getFrame() { + if (framePromise) + return framePromise; + + framePromise = new Promise(resolve => { + let view = makeFrame(window.document, { + namespaceURI: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", + nodeName: "iframe", + type: "content", + remote: useRemoteProcesses, + uri: "about:blank" + }); + + // Wait for the remote side to connect + let listener = (frame) => { + if (frame.frameElement != view) + return; + frames.off("attach", listener); + remoteFrame = frame; + resolve(frame); + } + frames.on("attach", listener); + }); + return framePromise; } -let pageContract = contract(merge({ +var pageContract = contract(merge({ allow: { is: ['object', 'undefined', 'null'], map: function (allow) { return { script: !allow || allow.script !== false }} @@ -60,10 +71,11 @@ let pageContract = contract(merge({ is: ['function', 'undefined'] }, include: { - is: ['string', 'array', 'undefined'] + is: ['string', 'array', 'regexp', 'undefined'] }, contentScriptWhen: { - is: ['string', 'undefined'] + is: ['string', 'undefined'], + map: (when) => when || "end" } }, loaderContract.rules)); @@ -77,16 +89,18 @@ function disableScript (page) { function Allow (page) { return { - get script() { return getDocShell(viewFor(page)).allowJavascript; }, - set script(value) { return value ? enableScript(page) : disableScript(page); } - }; -} + get script() { + return internal(page).options.allow.script; + }, + set script(value) { + internal(page).options.allow.script = value; -function injectWorker ({page}) { - let worker = workerFor(page); - let view = viewFor(page); - if (isValidURL(page, view.contentDocument.URL)) - attach(worker, view.contentWindow); + if (isDisposed(page)) + return; + + remoteFrame.port.emit("sdk/frame/set", internal(page).id, { allowScript: value }); + } + }; } function isValidURL(page, url) { @@ -96,30 +110,23 @@ function isValidURL(page, url) { const Page = Class({ implements: [ EventTarget, - Disposable, - WeakReference + Disposable ], extends: WorkerHost(workerFor), setup: function Page(options) { - let page = this; options = pageContract(options); + // Sanitize the options + if ("contentScriptOptions" in options) + options.contentScriptOptions = JSON.stringify(options.contentScriptOptions); - let uri = options.contentURL; + internal(this).id = uuid().toString(); + internal(this).options = options; - let view = makeFrame(window.document, { - nodeName: 'iframe', - type: 'content', - uri: uri ? data.url(uri) : '', - allowJavascript: options.allow.script, - allowPlugins: true, - allowAuth: true - }); + for (let prop of ['contentScriptFile', 'contentScript', 'contentScriptWhen']) { + this[prop] = options[prop]; + } - ['contentScriptFile', 'contentScript', 'contentScriptWhen'] - .forEach(prop => page[prop] = options[prop]); - - views.set(this, view); - pages.set(view, this); + pages.set(internal(this).id, this); // Set listeners on the {Page} object itself, not the underlying worker, // like `onMessage`, as it gets piped @@ -128,66 +135,60 @@ const Page = Class({ workers.set(this, worker); pipe(worker, this); - if (this.include || options.include) { + if (options.include) { this.rules = Rules(); - this.rules.add.apply(this.rules, [].concat(this.include || options.include)); + this.rules.add.apply(this.rules, [].concat(options.include)); } + + getFrame().then(frame => { + if (isDisposed(this)) + return; + + frame.port.emit("sdk/frame/create", internal(this).id, stripListeners(options)); + }); }, get allow() { return Allow(this); }, set allow(value) { - let allowJavascript = pageContract({ allow: value }).allow.script; - return allowJavascript ? enableScript(this) : disableScript(this); + if (isDisposed(this)) + return; + this.allow.script = pageContract({ allow: value }).allow.script; + }, + get contentURL() { + return internal(this).options.contentURL; }, - get contentURL() { return viewFor(this).getAttribute("data-src") }, set contentURL(value) { - if (!isValidURL(this, value)) return; - let view = viewFor(this); - let contentURL = pageContract({ contentURL: value }).contentURL; + if (!isValidURL(this, value)) + return; + internal(this).options.contentURL = value; + if (isDisposed(this)) + return; - // page-worker doesn't have a model like other APIs, so to be consitent - // with the behavior "what you set is what you get", we need to store - // the original `contentURL` given. - // Even if XUL elements doesn't support `dataset`, properties, to - // indicate that is a custom attribute the syntax "data-*" is used. - view.setAttribute('data-src', contentURL); - view.setAttribute('src', data.url(contentURL)); + remoteFrame.port.emit("sdk/frame/set", internal(this).id, { contentURL: value }); }, dispose: function () { - if (isDisposed(this)) return; - let view = viewFor(this); - if (view.parentNode) view.parentNode.removeChild(view); - views.delete(this); - destroy(workers.get(this)); + if (isDisposed(this)) + return; + pages.delete(internal(this).id); + let worker = workerFor(this); + if (worker) + destroy(worker); + remoteFrame.port.emit("sdk/frame/destroy", internal(this).id); + + // Destroy the remote frame if all the pages have been destroyed + if (pages.size == 0) { + framePromise = null; + remoteFrame.frameElement.remove(); + remoteFrame = null; + } }, toString: function () { return '[object Page]' } }); exports.Page = Page; -let pageEvents = streamMerge([events, streamEventsFrom(window)]); -let readyEvents = filter(pageEvents, isReadyEvent); -let formattedEvents = map(readyEvents, function({target, type}) { - return { type: type, page: pageFromDoc(target) }; +frames.port.on("sdk/frame/connect", (frame, id, params) => { + let page = pages.get(id); + if (!page) + return; + connect(workerFor(page), frame, params); }); -let pageReadyEvents = filter(formattedEvents, function({page, type}) { - return getAttachEventType(page) === type}); -on(pageReadyEvents, 'data', injectWorker); - -function isReadyEvent ({type}) { - return has(readyEventNames, type); -} - -/* - * Takes a document, finds its doc shell tree root and returns the - * matching Page instance if found - */ -function pageFromDoc(doc) { - let parentWindow = getParentWindow(doc.defaultView), page; - if (!parentWindow) return; - - let frames = parentWindow.document.getElementsByTagName('iframe'); - for (let i = frames.length; i--;) - if (frames[i].contentDocument === doc && (page = pageFor(frames[i]))) - return page; - return null; -} diff --git a/toolkit/commonjs/sdk/selection.js b/toolkit/commonjs/sdk/selection.js index 55a31ace41..8682e8c6d7 100644 --- a/toolkit/commonjs/sdk/selection.js +++ b/toolkit/commonjs/sdk/selection.js @@ -20,7 +20,7 @@ const { Ci, Cc } = require("chrome"), { ns } = require("./core/namespace"), { when: unload } = require("./system/unload"), { ignoreWindow } = require('./private-browsing/utils'), - { getTabs, getTabContentWindow, getTabForContentWindow, + { getTabs, getTabForContentWindow, getAllTabContentWindows } = require('./tabs/utils'), winUtils = require("./window/utils"), events = require("./system/events"); diff --git a/toolkit/commonjs/sdk/system/events-shimmed.js b/toolkit/commonjs/sdk/system/events-shimmed.js new file mode 100644 index 0000000000..14496f1f0b --- /dev/null +++ b/toolkit/commonjs/sdk/system/events-shimmed.js @@ -0,0 +1,16 @@ +/* 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/. */ + +'use strict'; + +module.metadata = { + 'stability': 'unstable' +}; + +const events = require('./events.js'); + +exports.emit = (type, event) => events.emit(type, event, true); +exports.on = (type, listener, strong) => events.on(type, listener, strong, true); +exports.once = (type, listener) => events.once(type, listener, true); +exports.off = (type, listener) => events.off(type, listener, true); diff --git a/toolkit/commonjs/sdk/system/events.js b/toolkit/commonjs/sdk/system/events.js index 9e2e826088..0cf525aa19 100644 --- a/toolkit/commonjs/sdk/system/events.js +++ b/toolkit/commonjs/sdk/system/events.js @@ -12,8 +12,13 @@ const { Cc, Ci, Cu } = require('chrome'); const { Unknown } = require('../platform/xpcom'); const { Class } = require('../core/heritage'); const { ns } = require('../core/namespace'); -const { addObserver, removeObserver, notifyObservers } = +const observerService = Cc['@mozilla.org/observer-service;1'].getService(Ci.nsIObserverService); +const { addObserver, removeObserver, notifyObservers } = observerService; +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); +const addObserverNoShim = ShimWaiver.getProperty(observerService, "addObserver"); +const removeObserverNoShim = ShimWaiver.getProperty(observerService, "removeObserver"); +const notifyObserversNoShim = ShimWaiver.getProperty(observerService, "notifyObservers"); const unloadSubject = require('@loader/unload'); const Subject = Class({ @@ -29,11 +34,11 @@ const Subject = Class({ object: object }; }, - getHelperForLanguage: function() {}, + getScriptableHelper: function() {}, getInterfaces: function() {} }); -function emit(type, event) { +function emit(type, event, shimmed = false) { // From bug 910599 // We must test to see if 'subject' or 'data' is a defined property // of the event object, but also allow primitives to be passed in, @@ -48,7 +53,11 @@ function emit(type, event) { // All other types return themselves (and cast to strings/null // via observer service) event; - notifyObservers(subject, type, data); + if (shimmed) { + notifyObservers(subject, type, data); + } else { + notifyObserversNoShim(subject, type, data); + } } exports.emit = emit; @@ -83,7 +92,7 @@ const Observer = Class({ const subscribers = ns(); -function on(type, listener, strong) { +function on(type, listener, strong, shimmed = false) { // Unless last optional argument is `true` we use a weak reference to a // listener. let weak = !strong; @@ -94,29 +103,34 @@ function on(type, listener, strong) { if (!(type in observers)) { let observer = Observer(listener); observers[type] = observer; - addObserver(observer, type, weak); + if (shimmed) { + addObserver(observer, type, weak); + } else { + addObserverNoShim(observer, type, weak); + } // WeakRef gymnastics to remove all alive observers on unload let ref = Cu.getWeakReference(observer); weakRefs.set(observer, ref); stillAlive.set(ref, type); + wasShimmed.set(ref, shimmed); } } exports.on = on; -function once(type, listener) { +function once(type, listener, shimmed = false) { // Note: this code assumes order in which listeners are called, which is fine // as long as dispatch happens in same order as listener registration which // is the case now. That being said we should be aware that this may break // in a future if order will change. - on(type, listener); + on(type, listener, shimmed); on(type, function cleanup() { - off(type, listener); - off(type, cleanup); - }, true); + off(type, listener, shimmed); + off(type, cleanup, shimmed); + }, true, shimmed); } exports.once = once; -function off(type, listener) { +function off(type, listener, shimmed = false) { // Take list of observers as with the given `listener`. let observers = subscribers(listener); // If `observer` for the given `type` is registered, then @@ -124,22 +138,29 @@ function off(type, listener) { if (type in observers) { let observer = observers[type]; delete observers[type]; - removeObserver(observer, type); + if (shimmed) { + removeObserver(observer, type); + } else { + removeObserverNoShim(observer, type); + } stillAlive.delete(weakRefs.get(observer)); + wasShimmed.delete(weakRefs.get(observer)); } } exports.off = off; // must use WeakMap to keep reference to all the WeakRefs (!), see bug 986115 -let weakRefs = new WeakMap(); +var weakRefs = new WeakMap(); // and we're out of beta, we're releasing on time! -let stillAlive = new Map(); +var stillAlive = new Map(); + +var wasShimmed = new Map(); on('sdk:loader:destroy', function onunload({ subject, data: reason }) { // using logic from ./unload, to avoid a circular module reference if (subject.wrappedJSObject === unloadSubject) { - off('sdk:loader:destroy', onunload); + off('sdk:loader:destroy', onunload, false); // don't bother if (reason === 'shutdown') @@ -147,9 +168,14 @@ on('sdk:loader:destroy', function onunload({ subject, data: reason }) { stillAlive.forEach( (type, ref) => { let observer = ref.get(); - if (observer) - removeObserver(observer, type); + if (observer) { + if (wasShimmed.get(ref)) { + removeObserver(observer, type); + } else { + removeObserverNoShim(observer, type); + } + } }) } // a strong reference -}, true); +}, true, false); diff --git a/toolkit/commonjs/sdk/tabs/helpers.js b/toolkit/commonjs/sdk/tabs/helpers.js index 96584351a6..b2c8aa0137 100644 --- a/toolkit/commonjs/sdk/tabs/helpers.js +++ b/toolkit/commonjs/sdk/tabs/helpers.js @@ -11,31 +11,12 @@ module.metadata = { // NOTE: This file should only export Tab instances -const { getTabForContentWindow, getTabForBrowser: getRawTabForBrowser } = require('./utils'); -const { Tab } = require('./tab'); -const { rawTabNS } = require('./namespace'); +const { getTabForBrowser: getRawTabForBrowser } = require('./utils'); +const { modelFor } = require('../model/core'); -function getTabForWindow(win) { - let tab = getTabForContentWindow(win); - // We were unable to find the related tab! - if (!tab) - return null; - - return getTabForRawTab(tab) || Tab({ tab: tab }); -} -exports.getTabForWindow = getTabForWindow; - -// only works on fennec atm -function getTabForRawTab(rawTab) { - let tab = rawTabNS(rawTab).tab; - if (tab) { - return tab; - } - return null; -} -exports.getTabForRawTab = getTabForRawTab; +exports.getTabForRawTab = modelFor; function getTabForBrowser(browser) { - return getTabForRawTab(getRawTabForBrowser(browser)); + return modelFor(getRawTabForBrowser(browser)) || null; } exports.getTabForBrowser = getTabForBrowser; diff --git a/toolkit/commonjs/sdk/tabs/namespace.js b/toolkit/commonjs/sdk/tabs/namespace.js index 07bd170535..3553b1a991 100644 --- a/toolkit/commonjs/sdk/tabs/namespace.js +++ b/toolkit/commonjs/sdk/tabs/namespace.js @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 'use strict'; -let { ns } = require('../core/namespace'); +var { ns } = require('../core/namespace'); exports.tabsNS = ns(); exports.tabNS = ns(); diff --git a/toolkit/commonjs/sdk/tabs/observer.js b/toolkit/commonjs/sdk/tabs/observer.js index f3f2f9caa4..4e935cd628 100644 --- a/toolkit/commonjs/sdk/tabs/observer.js +++ b/toolkit/commonjs/sdk/tabs/observer.js @@ -15,6 +15,7 @@ const { getActiveTab, getTabs } = require("./utils"); const { browserWindowIterator } = require("../deprecated/window-utils"); const { isBrowser, windows, getMostRecentBrowserWindow } = require("../window/utils"); const { observer: windowObserver } = require("../windows/observer"); +const { when } = require("../system/unload"); const EVENTS = { "TabOpen": "open", @@ -87,6 +88,11 @@ const Observer = Class({ for (let chromeWindow of browserWindowIterator()) { this.observe(chromeWindow); } + + when(_ => { + // Don't dispatch a deactivate event during unload. + this[selectedTab] = null; + }); }, /** * Events that are supported and emitted by the module. diff --git a/toolkit/commonjs/sdk/tabs/tab-fennec.js b/toolkit/commonjs/sdk/tabs/tab-fennec.js index 1d1719f927..3927337f67 100644 --- a/toolkit/commonjs/sdk/tabs/tab-fennec.js +++ b/toolkit/commonjs/sdk/tabs/tab-fennec.js @@ -9,7 +9,7 @@ const { tabNS, rawTabNS } = require('./namespace'); const { EventTarget } = require('../event/target'); const { activateTab, getTabTitle, setTabTitle, closeTab, getTabURL, getTabContentWindow, getTabForBrowser, setTabURL, getOwnerWindow, - getTabContentDocument, getTabContentType, getTabId } = require('./utils'); + getTabContentDocument, getTabContentType, getTabId, isTab } = require('./utils'); const { emit } = require('../event/core'); const { isPrivate } = require('../private-browsing/utils'); const { isWindowPrivate } = require('../window/utils'); @@ -17,6 +17,7 @@ const { when: unload } = require('../system/unload'); const { BLANK } = require('../content/thumbnail'); const { viewFor } = require('../view/core'); const { EVENTS } = require('./events'); +const { modelFor } = require('../model/core'); const ERR_FENNEC_MSG = 'This method is not yet supported by Fennec'; @@ -72,26 +73,8 @@ const Tab = Class({ get url() { return tabNS(this).closed ? undefined : getTabURL(tabNS(this).tab); }, - set url(url) setTabURL(tabNS(this).tab, url), - - /** - * URI of the favicon for the page currently loaded in this tab. - * @type {String} - */ - get favicon() { - /* - * Synchronous favicon services were never supported on Fennec, - * and as of FF22, are now deprecated. When/if favicon services - * are supported for Fennec, this getter should reference - * `require('sdk/places/favicon').getFavicon` - */ - console.error( - 'tab.favicon is deprecated, and currently ' + - 'favicon helpers are not yet supported by Fennec' - ); - - // return 16x16 blank default - return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAEklEQVQ4jWNgGAWjYBSMAggAAAQQAAF/TXiOAAAAAElFTkSuQmCC'; + set url(url) { + setTabURL(tabNS(this).tab, url); }, getThumbnail: function() { @@ -259,3 +242,8 @@ function onTabClose(event) { isPrivate.implement(Tab, tab => { return isWindowPrivate(getTabContentWindow(tabNS(tab).tab)); }); + +// Implement `modelFor` function for the Tab instances. +modelFor.when(isTab, rawTab => { + return rawTabNS(rawTab).tab; +}); diff --git a/toolkit/commonjs/sdk/tabs/tab-firefox.js b/toolkit/commonjs/sdk/tabs/tab-firefox.js index 4b1d207a56..ef2e17356c 100644 --- a/toolkit/commonjs/sdk/tabs/tab-firefox.js +++ b/toolkit/commonjs/sdk/tabs/tab-firefox.js @@ -1,311 +1,341 @@ - /*This Source Code Form is subject to the terms of the Mozilla Public +/* 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/. */ 'use strict'; -const { Trait } = require("../deprecated/traits"); -const { EventEmitter } = require("../deprecated/events"); -const { defer } = require("../lang/functional"); -const { has } = require("../util/array"); -const { each } = require("../util/object"); -const { EVENTS } = require("./events"); -const { getThumbnailURIForWindow, BLANK } = require("../content/thumbnail"); -const { getFaviconURIForLocation } = require("../io/data"); -const { activateTab, getOwnerWindow, getBrowserForTab, getTabTitle, - setTabTitle, getTabContentDocument, getTabURL, setTabURL, - getTabContentType, getTabId } = require('./utils'); -const { isPrivate } = require('../private-browsing/utils'); -const { isWindowPrivate } = require('../window/utils'); -const viewNS = require('../core/namespace').ns(); -const { deprecateUsage } = require('../util/deprecate'); -const { getURL } = require('../url/utils'); -const { viewFor } = require('../view/core'); +const { Class } = require('../core/heritage'); const { observer } = require('./observer'); -const { when: unload } = require("../system/unload"); +const { observer: windowObserver } = require('../windows/observer'); +const { addListItem, removeListItem } = require('../util/list'); +const { viewFor } = require('../view/core'); +const { modelFor } = require('../model/core'); +const { emit, setListeners } = require('../event/core'); +const { EventTarget } = require('../event/target'); +const { getBrowserForTab, setTabURL, getTabId, getTabURL, getTabForBrowser, + getTabs, getTabTitle, setTabTitle, getIndex, closeTab, reload, move, + activateTab, pin, unpin, isTab } = require('./utils'); +const { isBrowser, getInnerId, isWindowPrivate } = require('../window/utils'); +const { getThumbnailURIForWindow, BLANK } = require("../content/thumbnail"); +const { when } = require('../system/unload'); +const { ignoreWindow, isPrivate } = require('../private-browsing/utils') +const { defer } = require('../lang/functional'); +const { getURL } = require('../url/utils'); +const { frames, remoteRequire } = require('../remote/parent'); +remoteRequire('sdk/content/tab-events'); -require('../../framescript/FrameScriptManager.jsm').enableTabEvents(); +const modelsFor = new WeakMap(); +const viewsFor = new WeakMap(); +const destroyed = new WeakMap(); -// Array of the inner instances of all the wrapped tabs. -const TABS = []; +const tabEvents = {}; +exports.tabEvents = tabEvents; -/** - * Trait used to create tab wrappers. - */ -const TabTrait = Trait.compose(EventEmitter, { - on: Trait.required, - _emit: Trait.required, - /** - * Tab DOM element that is being wrapped. - */ - _tab: null, - /** - * Window wrapper whose tab this object represents. - */ - window: null, - constructor: function Tab(options) { - this._tab = options.tab; - // TODO: Remove this dependency - let window = this.window = options.window || require('../windows').BrowserWindow({ window: getOwnerWindow(this._tab) }); +function browser(tab) { + return getBrowserForTab(viewsFor.get(tab)); +} - // Setting event listener if was passed. - each(EVENTS, (type) => { - let listener = options[type.listener]; - if (listener) { - this.on(type.name, options[type.listener]); - } - // window spreads this event. - if (!has(['ready', 'load', 'pageshow'], (type.name))) - window.tabs.on(type.name, this._onEvent.bind(this, type.name)); +function isDestroyed(tab) { + return destroyed.has(tab); +} + +function isClosed(tab) { + if (!viewsFor.has(tab)) + return true; + return viewsFor.get(tab).closing; +} + +const Tab = Class({ + implements: [EventTarget], + initialize: function(tabElement, options = null) { + modelsFor.set(tabElement, this); + viewsFor.set(this, tabElement); + + if (options) { + EventTarget.prototype.initialize.call(this, options); + + if (options.isPinned) + this.pin(); + + // Note that activate is defered and so will run after any open event + // is sent out + if (!options.inBackground) + this.activate(); + } + + getURL.implement(this, tab => tab.url); + isPrivate.implement(this, tab => { + return isWindowPrivate(viewsFor.get(tab).ownerDocument.defaultView); }); - - this.on(EVENTS.close.name, this.destroy.bind(this)); - - this._onContentEvent = this._onContentEvent.bind(this); - this._window.messageManager.addMessageListener('sdk/tab/event', this._onContentEvent); - - // bug 1024632 - first tab inNewWindow gets events from the synthetic - // about:blank document. ignore them unless that is the actual target url. - this._skipBlankEvents = options.inNewWindow && options.url !== 'about:blank'; - - if (options.isPinned) - this.pin(); - - viewNS(this._public).tab = this._tab; - viewFor.implement(this._public, getTabView); - isPrivate.implement(this._public, tab => isWindowPrivate(getChromeTab(tab))); - - // Add tabs to getURL method - getURL.implement(this._public, (function (obj) this._public.url).bind(this)); - - // Since we will have to identify tabs by a DOM elements facade function - // is used as constructor that collects all the instances and makes sure - // that they more then one wrapper is not created per tab. - return this; - }, - destroy: function destroy() { - this._removeAllListeners(); - if (this._tab) { - this._window.messageManager.removeMessageListener('sdk/tab/event', this._onContentEvent); - this._tab = null; - TABS.splice(TABS.indexOf(this), 1); - } }, - /** - * internal message listener emits public events (ready, load and pageshow) - * forwarded from content frame script tab-event.js - */ - _onContentEvent: function({ target, data }) { - if (target !== this._browser) + get id() { + return isDestroyed(this) ? undefined : getTabId(viewsFor.get(this)); + }, + + get title() { + return isDestroyed(this) ? undefined : getTabTitle(viewsFor.get(this)); + }, + + set title(val) { + if (isDestroyed(this)) return; - // bug 1024632 - skip initial events from synthetic about:blank document - if (this._skipBlankEvents && this.window.tabs.length === 1 && this.url === 'about:blank') + setTabTitle(viewsFor.get(this), val); + }, + + get url() { + return isDestroyed(this) ? undefined : getTabURL(viewsFor.get(this)); + }, + + set url(val) { + if (isDestroyed(this)) return; - // first time we don't skip blank events, disable further skipping - this._skipBlankEvents = false; - - this._emit(data.type, this._public, data.persisted); + setTabURL(viewsFor.get(this), val); }, - /** - * Internal tab event router. Window will emit tab related events for all it's - * tabs, this listener will propagate all the events for this tab to it's - * listeners. - */ - _onEvent: function _onEvent(type, tab) { - if (viewNS(tab).tab == this._tab) - this._emit(type, tab); - }, - /** - * Browser DOM element where page of this tab is currently loaded. - */ - get _browser() getBrowserForTab(this._tab), - /** - * Window DOM element containing this tab. - */ - get _window() getOwnerWindow(this._tab), - /** - * Document object of the page that is currently loaded in this tab. - */ - get _contentDocument() getTabContentDocument(this._tab), - /** - * Window object of the page that is currently loaded in this tab. - */ - get _contentWindow() this._browser.contentWindow, - - /** - * tab's document readyState, or 'uninitialized' if it doesn't even exist yet. - */ - get readyState() { - let doc = this._contentDocument; - return doc && doc.readyState || 'uninitialized'; + get contentType() { + return isDestroyed(this) ? undefined : browser(this).documentContentType; }, - /** - * Unique id for the tab, actually maps to tab.linkedPanel but with some munging. - */ - get id() this._tab ? getTabId(this._tab) : undefined, - - /** - * The title of the page currently loaded in the tab. - * Changing this property changes an actual title. - * @type {String} - */ - get title() this._tab ? getTabTitle(this._tab) : undefined, - set title(title) this._tab && setTabTitle(this._tab, title), - - /** - * Returns the MIME type that the document loaded in the tab is being - * rendered as. - * @type {String} - */ - get contentType() this._tab ? getTabContentType(this._tab) : undefined, - - /** - * Location of the page currently loaded in this tab. - * Changing this property will loads page under under the specified location. - * @type {String} - */ - get url() this._tab ? getTabURL(this._tab) : undefined, - set url(url) this._tab && setTabURL(this._tab, url), - /** - * URI of the favicon for the page currently loaded in this tab. - * @type {String} - */ - get favicon() { - deprecateUsage( - 'tab.favicon is deprecated, ' + - 'please use require("sdk/places/favicon").getFavicon instead.' - ); - return this._tab ? getFaviconURIForLocation(this.url) : undefined + get index() { + return isDestroyed(this) ? undefined : getIndex(viewsFor.get(this)); }, - /** - * The CSS style for the tab - */ - get style() null, // TODO - /** - * The index of the tab relative to other tabs in the application window. - * Changing this property will change order of the actual position of the tab. - * @type {Number} - */ - get index() - this._tab ? - this._window.gBrowser.getBrowserIndexForDocument(this._contentDocument) : - undefined, - set index(value) - this._tab && this._window.gBrowser.moveTabTo(this._tab, value), - /** - * Thumbnail data URI of the page currently loaded in this tab. - * @type {String} - */ - getThumbnail() { - if (!this._tab) + + set index(val) { + if (isDestroyed(this)) + return; + + move(viewsFor.get(this), val); + }, + + get isPinned() { + return isDestroyed(this) ? undefined : viewsFor.get(this).pinned; + }, + + get window() { + if (isClosed(this)) return undefined; - if (this._tab.getAttribute('remote')) { - console.error('This method is not supported with E10S'); - return BLANK; - } - return getThumbnailURIForWindow(this._contentWindow); - }, - /** - * Whether or not tab is pinned (Is an app-tab). - * @type {Boolean} - */ - get isPinned() this._tab ? this._tab.pinned : undefined, - pin: function pin() { - if (!this._tab) - return; - this._window.gBrowser.pinTab(this._tab); - }, - unpin: function unpin() { - if (!this._tab) - return; - this._window.gBrowser.unpinTab(this._tab); + + // TODO: Remove the dependency on the windows module, see bug 792670 + require('../windows'); + let tabElement = viewsFor.get(this); + let domWindow = tabElement.ownerDocument.defaultView; + return modelFor(domWindow); }, - /** - * Create a worker for this tab, first argument is options given to Worker. - * @type {Worker} - */ - attach: function attach(options) { - if (!this._tab) - return; - // BUG 792946 https://bugzilla.mozilla.org/show_bug.cgi?id=792946 - // TODO: fix this circular dependency - let { Worker } = require('./worker'); - return Worker(options, this._contentWindow); + get readyState() { + // TODO: This will use CPOWs in e10s: bug 1146606 + return isDestroyed(this) ? undefined : browser(this).contentDocument.readyState; }, - /** - * Make this tab active. - * Please note: That this function is called asynchronous since in E10S that - * will be the case. Besides this function is called from a constructor where - * we would like to return instance before firing a 'TabActivated' event. - */ - activate: defer(function activate() { - if (!this._tab) + pin: function() { + if (isDestroyed(this)) return; - activateTab(this._tab); - }), - /** - * Close the tab - */ - close: function close(callback) { - // Bug 699450: the tab may already have been detached - if (!this._tab || !this._tab.parentNode) { + + pin(viewsFor.get(this)); + }, + + unpin: function() { + if (isDestroyed(this)) + return; + + unpin(viewsFor.get(this)); + }, + + close: function(callback) { + let tabElement = viewsFor.get(this); + + if (isDestroyed(this) || !tabElement || !tabElement.parentNode) { if (callback) callback(); return; } - if (callback) { - if (this.window.tabs.activeTab && (this.window.tabs.activeTab.id == this.id)) - observer.once('select', callback); - else - this.once(EVENTS.close.name, callback); - } - this._window.gBrowser.removeTab(this._tab); + + this.once('close', () => { + this.destroy(); + if (callback) + callback(); + }); + + closeTab(tabElement); }, - /** - * Reload the tab - */ - reload: function reload() { - if (!this._tab) + + reload: function() { + if (isDestroyed(this)) return; - this._window.gBrowser.reloadTab(this._tab); + + reload(viewsFor.get(this)); + }, + + activate: defer(function() { + if (isDestroyed(this)) + return; + + activateTab(viewsFor.get(this)); + }), + + getThumbnail: function() { + if (isDestroyed(this)) + return BLANK; + + // TODO: This is unimplemented in e10s: bug 1148601 + if (browser(this).isRemoteBrowser) { + console.error('This method is not supported with E10S'); + return BLANK; + } + return getThumbnailURIForWindow(browser(this).contentWindow); + }, + + attach: function(options) { + if (isDestroyed(this)) + return; + + let { Worker } = require('../content/worker'); + let { connect, makeChildOptions } = require('../content/utils'); + + let worker = Worker(options); + worker.once("detach", () => { + worker.destroy(); + }); + + let attach = frame => { + let childOptions = makeChildOptions(options); + frame.port.emit("sdk/tab/attach", childOptions); + connect(worker, frame, { id: childOptions.id, url: this.url }); + }; + + // Do this synchronously if possible + let frame = frames.getFrameForBrowser(browser(this)); + if (frame) { + attach(frame); + } + else { + let listener = (frame) => { + if (frame.frameElement != browser(this)) + return; + + listener.off("attach", listener); + attach(frame); + }; + frames.on("attach", listener); + } + + return worker; + }, + + destroy: function() { + if (isDestroyed(this)) + return; + + destroyed.set(this, true); } }); - -function getChromeTab(tab) { - return getOwnerWindow(viewNS(tab).tab); -} - -// Implement `viewFor` polymorphic function for the Tab -// instances. -const getTabView = tab => viewNS(tab).tab; - -function Tab(options, existingOnly) { - let chromeTab = options.tab; - for (let tab of TABS) { - if (chromeTab == tab._tab) - return tab._public; - } - // If called asked to return only existing wrapper, - // we should return null here as no matching Tab object has been found - if (existingOnly) - return null; - - let tab = TabTrait(options); - TABS.push(tab); - return tab._public; -} -Tab.prototype = TabTrait.prototype; exports.Tab = Tab; -// Unregister event listeners when the addon is unloaded, to avoid leaks. -unload(() => { - for (let tab of TABS.slice()) { - tab.destroy(); +viewFor.define(Tab, tab => viewsFor.get(tab)); + +// Returns the high-level window for this DOM window if the windows module has +// ever been loaded otherwise returns null +function maybeWindowFor(domWindow) { + try { + return modelFor(domWindow); } + catch (e) { + return null; + } +} + +function tabEmit(tab, event, ...args) { + // Don't emit events for destroyed tabs + if (isDestroyed(tab)) + return; + + // If the windows module was never loaded this will return null. We don't need + // to emit to the window.tabs object in this case as nothing can be listening. + let tabElement = viewsFor.get(tab); + let window = maybeWindowFor(tabElement.ownerDocument.defaultView); + if (window) + emit(window.tabs, event, tab, ...args); + + emit(tabEvents, event, tab, ...args); + emit(tab, event, tab, ...args); +} + +function windowClosed(domWindow) { + if (!isBrowser(domWindow)) + return; + + for (let tabElement of getTabs(domWindow)) { + tabEventListener("close", tabElement); + } +} +windowObserver.on('close', windowClosed); + +// Don't want to send close events after unloaded +when(_ => { + windowObserver.off('close', windowClosed); +}); + +// Listen for tabbrowser events +function tabEventListener(event, tabElement, ...args) { + let domWindow = tabElement.ownerDocument.defaultView; + + if (ignoreWindow(domWindow)) + return; + + // Don't send events for tabs that are already closing + if (event != "close" && (tabElement.closing || !tabElement.parentNode)) + return; + + let tab = modelsFor.get(tabElement); + if (!tab) + tab = new Tab(tabElement); + + let window = maybeWindowFor(domWindow); + + if (event == "open") { + // Note, add to the window tabs first because if this is the first access to + // window.tabs it will be prefilling itself with everything from tabs + if (window) + addListItem(window.tabs, tab); + // The tabs module will take care of adding to its internal list + } + else if (event == "close") { + if (window) + removeListItem(window.tabs, tab); + // The tabs module will take care of removing from its internal list + } + else if (event == "ready" || event == "load") { + // Ignore load events from before browser windows have fully loaded, these + // are for about:blank in the initial tab + if (isBrowser(domWindow) && !domWindow.gBrowserInit.delayedStartupFinished) + return; + } + + tabEmit(tab, event, ...args); + + // The tab object shouldn't be reachable after closed + if (event == "close") { + viewsFor.delete(tab); + modelsFor.delete(tabElement); + } +} +observer.on('*', tabEventListener); + +// Listen for tab events from content +frames.port.on('sdk/tab/event', (frame, event, ...args) => { + if (!frame.isTab) + return; + + let tabElement = getTabForBrowser(frame.frameElement); + if (!tabElement) + return; + + tabEventListener(event, tabElement, ...args); +}); + +// Implement `modelFor` function for the Tab instances.. +modelFor.when(isTab, view => { + return modelsFor.get(view); }); diff --git a/toolkit/commonjs/sdk/tabs/tabs-firefox.js b/toolkit/commonjs/sdk/tabs/tabs-firefox.js index 3fac2f47eb..5b48772f07 100644 --- a/toolkit/commonjs/sdk/tabs/tabs-firefox.js +++ b/toolkit/commonjs/sdk/tabs/tabs-firefox.js @@ -1,68 +1,134 @@ /* 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/. */ -'use strict'; +"use strict"; -// TODO: BUG 792670 - remove dependency below -const { browserWindows: windows } = require('../windows'); -const { tabs } = require('../windows/tabs-firefox'); +const { Class } = require('../core/heritage'); +const { Tab, tabEvents } = require('./tab'); +const { EventTarget } = require('../event/target'); +const { emit, setListeners } = require('../event/core'); +const { pipe } = require('../event/utils'); +const { observer: windowObserver } = require('../windows/observer'); +const { List, addListItem, removeListItem } = require('../util/list'); +const { modelFor } = require('../model/core'); +const { viewFor } = require('../view/core'); +const { getTabs, getSelectedTab } = require('./utils'); +const { getMostRecentBrowserWindow, isBrowser } = require('../window/utils'); +const { Options } = require('./common'); const { isPrivate } = require('../private-browsing'); -const { isWindowPBSupported } = require('../private-browsing/utils') +const { ignoreWindow, isWindowPBSupported } = require('../private-browsing/utils') const { isPrivateBrowsingSupported } = require('sdk/self'); const supportPrivateTabs = isPrivateBrowsingSupported && isWindowPBSupported; -function newTabWindow(options) { - // `tabs` option is under review and may be removed. - return windows.open({ - tabs: [ options ], - isPrivate: options.isPrivate - }); -} +const Tabs = Class({ + implements: [EventTarget], + extends: List, + initialize: function() { + List.prototype.initialize.call(this); -Object.defineProperties(tabs, { - open: { value: function open(options) { - if (options.inNewWindow) { - newTabWindow(options); - return undefined; + // We must do the list manipulation here where the object is extensible + this.on("open", tab => { + addListItem(this, tab); + }); + + this.on("close", tab => { + removeListItem(this, tab); + }); + }, + + get activeTab() { + let activeDomWin = getMostRecentBrowserWindow(); + if (!activeDomWin) + return null; + return modelFor(getSelectedTab(activeDomWin)); + }, + + open: function(options) { + options = Options(options); + + // TODO: Remove the dependency on the windows module: bug 792670 + let windows = require('../windows').browserWindows; + let activeWindow = windows.activeWindow; + + let privateState = supportPrivateTabs && options.isPrivate; + // When no isPrivate option was passed use the private state of the active + // window + if (activeWindow && privateState === undefined) + privateState = isPrivate(activeWindow); + + function getWindow(privateState) { + for (let window of windows) { + if (privateState === isPrivate(window)) { + return window; + } + } + return null; } - let activeWindow = windows.activeWindow; - let privateState = (supportPrivateTabs && (options.isPrivate || isPrivate(activeWindow))) || false; + function openNewWindowWithTab() { + windows.open({ + url: options.url, + isPrivate: privateState, + onOpen: function(newWindow) { + let tab = newWindow.tabs[0]; + setListeners(tab, options); + + if (options.isPinned) + tab.pin(); + + // We don't emit the open event for the first tab in a new window so + // do it now the listeners are attached + emit(tab, "open", tab); + } + }); + } + + if (options.inNewWindow) + return openNewWindowWithTab(); // if the active window is in the state that we need then use it - if (activeWindow && (!supportPrivateTabs || privateState === isPrivate(activeWindow))) { - activeWindow.tabs.open(options); - } - else { - // find a window in the state that we need - let window = getWindow(privateState); - if (window) { - window.tabs.open(options); - } - // open a window in the state that we need - else { - newTabWindow(options); - } - } + if (activeWindow && (privateState === isPrivate(activeWindow))) + return activeWindow.tabs.open(options); - return undefined; - }} + // find a window in the state that we need + let window = getWindow(privateState); + if (window) + return window.tabs.open(options); + + return openNewWindowWithTab(); + } }); -function getWindow(privateState) { - for (let window of windows) { - if (privateState === isPrivate(window)) { - return window; - } - } - return null; +const allTabs = new Tabs(); +// Export a new object with allTabs as the prototype, otherwise allTabs becomes +// frozen and addListItem and removeListItem don't work correctly. +module.exports = Object.create(allTabs); +pipe(tabEvents, module.exports); + +function addWindowTab(window, tabElement) { + let tab = new Tab(tabElement); + if (window) + addListItem(window.tabs, tab); + addListItem(allTabs, tab); } -// Workaround for bug 674195. Freezing objects from other compartments fail, -// so we use `Object.freeze` from the same component as objects -// `hasOwnProperty`. Since `hasOwnProperty` here will be from other component -// we override it to support our workaround. -module.exports = Object.create(tabs, { - isPrototypeOf: { value: Object.prototype.isPrototypeOf } +// Find tabs in already open windows +for (let tabElement of getTabs()) + addWindowTab(null, tabElement); + +// Detect tabs in new windows +windowObserver.on('open', domWindow => { + if (!isBrowser(domWindow) || ignoreWindow(domWindow)) + return; + + let window = null; + try { + modelFor(domWindow); + } + catch (e) { } + + for (let tabElement of getTabs(domWindow)) { + addWindowTab(window, tabElement); + } }); diff --git a/toolkit/commonjs/sdk/tabs/utils.js b/toolkit/commonjs/sdk/tabs/utils.js index 323fa81915..8cfdb0748a 100644 --- a/toolkit/commonjs/sdk/tabs/utils.js +++ b/toolkit/commonjs/sdk/tabs/utils.js @@ -11,10 +11,11 @@ module.metadata = { // NOTE: This file should only deal with xul/native tabs -const { Ci } = require('chrome'); +const { Ci, Cu } = require('chrome'); const { defer } = require("../lang/functional"); const { windows, isBrowser } = require('../window/utils'); const { isPrivateBrowsingSupported } = require('../self'); +const { ShimWaiver } = Cu.import("resource://gre/modules/ShimWaiver.jsm"); // Bug 834961: ignore private windows when they are not supported function getWindows() { @@ -215,14 +216,25 @@ function getTabForId(id) { exports.getTabForId = getTabForId; function getTabTitle(tab) { - return getBrowserForTab(tab).contentDocument.title || tab.label || ""; + return getBrowserForTab(tab).contentTitle || tab.label || ""; } exports.getTabTitle = getTabTitle; function setTabTitle(tab, title) { title = String(title); - if (tab.browser) + if (tab.browser) { + // Fennec tab.browser.contentDocument.title = title; + } + else { + let browser = getBrowserForTab(tab); + // Note that we aren't actually setting the document title in e10s, just + // the title the browser thinks the content has + if (browser.isRemoteBrowser) + browser._contentTitle = title; + else + browser.contentDocument.title = title; + } tab.label = String(title); } exports.setTabTitle = setTabTitle; @@ -251,18 +263,24 @@ function getTabForContentWindow(window) { } exports.getTabForContentWindow = getTabForContentWindow; +// only sdk/selection.js is relying on shims +function getTabForContentWindowNoShim(window) { + function getTabContentWindowNoShim(tab) { + let browser = getBrowserForTab(tab); + return ShimWaiver.getProperty(browser, "contentWindow"); + } + return getTabs().find(tab => getTabContentWindowNoShim(tab) === window.top) || null; +} +exports.getTabForContentWindowNoShim = getTabForContentWindowNoShim; + function getTabURL(tab) { - if (tab.browser) // fennec - return String(tab.browser.currentURI.spec); return String(getBrowserForTab(tab).currentURI.spec); } exports.getTabURL = getTabURL; function setTabURL(tab, url) { - url = String(url); - if (tab.browser) - return tab.browser.loadURI(url); - return getBrowserForTab(tab).loadURI(url); + let browser = getBrowserForTab(tab); + browser.loadURI(String(url)); } // "TabOpen" event is fired when it's still "about:blank" is loaded in the // changing `location` property of the `contentDocument` has no effect since @@ -296,7 +314,9 @@ function getTabForBrowser(browser) { return tab; } } - return null; + + let tabbrowser = browser.getTabBrowser && browser.getTabBrowser() + return !!tabbrowser && tabbrowser.getTabForBrowser(browser); } exports.getTabForBrowser = getTabForBrowser; @@ -320,11 +340,7 @@ function isPinned(tab) { exports.isPinned = isPinned; function reload(tab) { - let gBrowser = getTabBrowserForTab(tab); - // Firefox - if (gBrowser) gBrowser.unpinTab(tab); - // Fennec - else if (tab.browser) tab.browser.reload(); + getBrowserForTab(tab).reload(); } exports.reload = reload @@ -332,8 +348,7 @@ function getIndex(tab) { let gBrowser = getTabBrowserForTab(tab); // Firefox if (gBrowser) { - let document = getBrowserForTab(tab).contentDocument; - return gBrowser.getBrowserIndexForDocument(document); + return tab._tPos; } // Fennec else { diff --git a/toolkit/components/addoncompat/ShimWaiver.jsm b/toolkit/components/addoncompat/ShimWaiver.jsm new file mode 100644 index 0000000000..402ab4c322 --- /dev/null +++ b/toolkit/components/addoncompat/ShimWaiver.jsm @@ -0,0 +1,15 @@ +// 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/. + +this.EXPORTED_SYMBOLS = ["ShimWaiver"]; + +this.ShimWaiver = { + getProperty: function(obj, prop) { + let rv = obj[prop]; + if (rv instanceof Function) { + rv = rv.bind(obj); + } + return rv; + } +}; diff --git a/toolkit/components/addoncompat/moz.build b/toolkit/components/addoncompat/moz.build index b5e1769403..8672aff924 100644 --- a/toolkit/components/addoncompat/moz.build +++ b/toolkit/components/addoncompat/moz.build @@ -16,4 +16,5 @@ EXTRA_JS_MODULES += [ 'Prefetcher.jsm', 'RemoteAddonsChild.jsm', 'RemoteAddonsParent.jsm', + 'ShimWaiver.jsm' ]