diff --git a/devtools/client/themes/images/commandline-icon.svg b/devtools/client/themes/images/commandline-icon.svg index 429d6a73b51b..d48ede604159 100644 --- a/devtools/client/themes/images/commandline-icon.svg +++ b/devtools/client/themes/images/commandline-icon.svg @@ -16,10 +16,10 @@ } #light-theme-focus:target ~ #light-theme { - fill: #4A90E2; + fill: #0060DF; } #dark-theme-focus:target ~ #dark-theme { - fill: #00FF7F; + fill: #75BFFF; } /* Unfocused states */ diff --git a/devtools/client/themes/webconsole.css b/devtools/client/themes/webconsole.css index 58873ee7652a..e5bdc5138f48 100644 --- a/devtools/client/themes/webconsole.css +++ b/devtools/client/themes/webconsole.css @@ -846,6 +846,16 @@ a.learn-more-link.webconsole-learn-more-link { .webconsole-filterbar-primary .devtools-plaininput { flex: 1 1 100%; + align-self: stretch; + margin-left: 1px; + padding-inline-start: 4px; + border: 1px solid transparent; +} + +.devtools-plaininput:focus { + border: 1px solid var(--blue-50); + transition: all 0.2s ease-in-out; + outline: none; } .webconsole-filterbar-primary .filter-checkbox { diff --git a/devtools/server/actors/highlighters/css-grid.js b/devtools/server/actors/highlighters/css-grid.js index f1ca1c16bc48..4a89fae13523 100644 --- a/devtools/server/actors/highlighters/css-grid.js +++ b/devtools/server/actors/highlighters/css-grid.js @@ -841,10 +841,11 @@ class CssGridHighlighter extends AutoRefreshHighlighter { let row = fragment.rows.tracks[rowNumber - 1]; let column = fragment.cols.tracks[columnNumber - 1]; - // Check if the font size is exceeds the bounds of the containing grid cell. + // If the font size exceeds the bounds of the containing grid cell, size it its + // row or column dimension, whichever is smallest. if (fontSize > (column.breadth * displayPixelRatio) || fontSize > (row.breadth * displayPixelRatio)) { - fontSize = (column.breadth + row.breadth) / 2; + fontSize = Math.min([column.breadth, row.breadth]); this.ctx.font = fontSize + "px " + GRID_FONT_FAMILY; } diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp index 364e82033707..f78534abe75e 100644 --- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -804,21 +804,12 @@ Animation::CancelNoUpdate() mHoldTime.SetNull(); mStartTime.SetNull(); + UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); + if (mTimeline) { mTimeline->RemoveAnimation(this); } MaybeQueueCancelEvent(activeTime); - - // When an animation is cancelled it no longer needs further ticks from the - // timeline. However, if we queued a cancel event and this was the last - // animation attached to the timeline, the timeline will stop observing the - // refresh driver and there may be no subsequent refresh driver tick for - // dispatching the queued event. - // - // By calling UpdateTiming *after* removing ourselves from our timeline, we - // ensure the timeline will register with the refresh driver for at least one - // more tick. - UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async); } bool diff --git a/dom/animation/AnimationEventDispatcher.cpp b/dom/animation/AnimationEventDispatcher.cpp new file mode 100644 index 000000000000..970103d04854 --- /dev/null +++ b/dom/animation/AnimationEventDispatcher.cpp @@ -0,0 +1,58 @@ +/* -*- 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 "mozilla/AnimationEventDispatcher.h" + +#include "mozilla/EventDispatcher.h" +#include "nsRefreshDriver.h" + +namespace mozilla { + +NS_IMPL_CYCLE_COLLECTION_CLASS(AnimationEventDispatcher) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AnimationEventDispatcher) + tmp->ClearEventQueue(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEventDispatcher) + for (auto& info : tmp->mPendingEvents) { + ImplCycleCollectionTraverse(cb, info.mElement, + "mozilla::AnimationEventDispatcher.mPendingEvents.mElement"); + ImplCycleCollectionTraverse(cb, info.mAnimation, + "mozilla::AnimationEventDispatcher.mPendingEvents.mAnimation"); + } +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationEventDispatcher, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationEventDispatcher, Release) + +void +AnimationEventDispatcher::Disconnect() +{ + if (mIsObserving) { + MOZ_ASSERT(mPresContext && mPresContext->RefreshDriver(), + "The pres context and the refresh driver should be still " + "alive if we haven't disassociated from the refresh driver"); + mPresContext->RefreshDriver()->CancelPendingAnimationEvents(this); + mIsObserving = false; + } + mPresContext = nullptr; +} + +void +AnimationEventDispatcher::QueueEvents(nsTArray&& aEvents) +{ + MOZ_ASSERT(mPresContext, + "The pres context should be valid"); + + mPendingEvents.AppendElements(Move(aEvents)); + mIsSorted = false; + if (!mIsObserving) { + mPresContext->RefreshDriver()->ScheduleAnimationEventDispatch(this); + mIsObserving = true; + } +} + +} // namespace mozilla + diff --git a/dom/animation/AnimationEventDispatcher.h b/dom/animation/AnimationEventDispatcher.h new file mode 100644 index 000000000000..99532e224f03 --- /dev/null +++ b/dom/animation/AnimationEventDispatcher.h @@ -0,0 +1,209 @@ +/* -*- 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_AnimationEventDispatcher_h +#define mozilla_AnimationEventDispatcher_h + +#include // For +#include "mozilla/AnimationComparator.h" +#include "mozilla/Assertions.h" +#include "mozilla/ContentEvents.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/Variant.h" +#include "nsCSSProps.h" +#include "nsCycleCollectionParticipant.h" + +class nsPresContext; +class nsRefreshDriver; + +namespace mozilla { + +struct AnimationEventInfo +{ + RefPtr mElement; + RefPtr mAnimation; + TimeStamp mTimeStamp; + + typedef Variant EventVariant; + EventVariant mEvent; + + // For CSS animation events + AnimationEventInfo(nsAtom* aAnimationName, + const NonOwningAnimationTarget& aTarget, + EventMessage aMessage, + double aElapsedTime, + const TimeStamp& aTimeStamp, + dom::Animation* aAnimation) + : mElement(aTarget.mElement) + , mAnimation(aAnimation) + , mTimeStamp(aTimeStamp) + , mEvent(EventVariant(InternalAnimationEvent(true, aMessage))) + { + InternalAnimationEvent& event = mEvent.as(); + + aAnimationName->ToString(event.mAnimationName); + // XXX Looks like nobody initialize WidgetEvent::time + event.mElapsedTime = aElapsedTime; + event.mPseudoElement = + nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType); + } + + // For CSS transition events + AnimationEventInfo(nsCSSPropertyID aProperty, + const NonOwningAnimationTarget& aTarget, + EventMessage aMessage, + double aElapsedTime, + const TimeStamp& aTimeStamp, + dom::Animation* aAnimation) + : mElement(aTarget.mElement) + , mAnimation(aAnimation) + , mTimeStamp(aTimeStamp) + , mEvent(EventVariant(InternalTransitionEvent(true, aMessage))) + { + InternalTransitionEvent& event = mEvent.as(); + + event.mPropertyName = + NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty)); + // XXX Looks like nobody initialize WidgetEvent::time + event.mElapsedTime = aElapsedTime; + event.mPseudoElement = + nsCSSPseudoElements::PseudoTypeAsString(aTarget.mPseudoType); + } + + // InternalAnimationEvent and InternalTransitionEvent don't support + // copy-construction, so we need to ourselves in order to work with nsTArray. + // + // FIXME: Drop this copy constructor and copy assignment below once + // WidgetEvent have move constructor and move assignment (bug 1433008). + AnimationEventInfo(const AnimationEventInfo& aOther) = default; + AnimationEventInfo& operator=(const AnimationEventInfo& aOther) = default; + + WidgetEvent* AsWidgetEvent() + { + if (mEvent.is()) { + return &mEvent.as(); + } + if (mEvent.is()) { + return &mEvent.as(); + } + + MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Unexpected event type"); + return nullptr; + } +}; + +class AnimationEventDispatcher final +{ +public: + explicit AnimationEventDispatcher(nsPresContext* aPresContext) + : mPresContext(aPresContext) + , mIsSorted(true) + , mIsObserving(false) + { + } + + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEventDispatcher) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AnimationEventDispatcher) + + void Disconnect(); + + void QueueEvents(nsTArray&& aEvents); + + // This will call SortEvents automatically if it has not already been + // called. + void DispatchEvents() + { + mIsObserving = false; + if (!mPresContext || mPendingEvents.IsEmpty()) { + return; + } + + SortEvents(); + + EventArray events; + mPendingEvents.SwapElements(events); + // mIsSorted will be set to true by SortEvents above, and we leave it + // that way since mPendingEvents is now empty + for (AnimationEventInfo& info : events) { + MOZ_ASSERT(!info.AsWidgetEvent()->mFlags.mIsBeingDispatched && + !info.AsWidgetEvent()->mFlags.mDispatchedAtLeastOnce, + "The WidgetEvent should be fresh"); + EventDispatcher::Dispatch(info.mElement, + mPresContext, + info.AsWidgetEvent()); + + // Bail out if our mPresContext was nullified due to destroying the pres + // context. + if (!mPresContext) { + break; + } + } + } + + void ClearEventQueue() + { + mPendingEvents.Clear(); + mIsSorted = true; + } + bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); } + +private: +#ifndef DEBUG + ~AnimationEventDispatcher() = default; +#else + ~AnimationEventDispatcher() + { + MOZ_ASSERT(!mIsObserving, + "AnimationEventDispatcher should have disassociated from " + "nsRefreshDriver"); + } +#endif + + class AnimationEventInfoLessThan + { + public: + bool operator()(const AnimationEventInfo& a, const AnimationEventInfo& b) const + { + if (a.mTimeStamp != b.mTimeStamp) { + // Null timestamps sort first + if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) { + return a.mTimeStamp.IsNull(); + } else { + return a.mTimeStamp < b.mTimeStamp; + } + } + + AnimationPtrComparator> comparator; + return comparator.LessThan(a.mAnimation, b.mAnimation); + } + }; + + // Sort all pending CSS animation/transition events by scheduled event time + // and composite order. + // https://drafts.csswg.org/web-animations/#update-animations-and-send-events + void SortEvents() + { + if (mIsSorted) { + return; + } + + // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is + // fixed. + std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(), + AnimationEventInfoLessThan()); + mIsSorted = true; + } + + nsPresContext* mPresContext; + typedef nsTArray EventArray; + EventArray mPendingEvents; + bool mIsSorted; + bool mIsObserving; +}; + +} // namespace mozilla + +#endif // mozilla_AnimationEventDispatcher_h diff --git a/dom/animation/moz.build b/dom/animation/moz.build index 00aee26893c9..b254fbe069e6 100644 --- a/dom/animation/moz.build +++ b/dom/animation/moz.build @@ -24,6 +24,7 @@ EXPORTS.mozilla.dom += [ EXPORTS.mozilla += [ 'AnimationComparator.h', + 'AnimationEventDispatcher.h', 'AnimationPerformanceWarning.h', 'AnimationPropertySegment.h', 'AnimationTarget.h', @@ -46,6 +47,7 @@ UNIFIED_SOURCES += [ 'AnimationEffectReadOnly.cpp', 'AnimationEffectTiming.cpp', 'AnimationEffectTimingReadOnly.cpp', + 'AnimationEventDispatcher.cpp', 'AnimationPerformanceWarning.cpp', 'AnimationTimeline.cpp', 'AnimationUtils.cpp', diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp index 6770ced9b4e7..e0fe45fd8b1f 100644 --- a/layout/base/PresShell.cpp +++ b/layout/base/PresShell.cpp @@ -1362,8 +1362,7 @@ PresShell::Destroy() } if (mPresContext) { - mPresContext->AnimationManager()->ClearEventQueue(); - mPresContext->TransitionManager()->ClearEventQueue(); + rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher()); } // Revoke any pending events. We need to do this and cancel pending reflows diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index ffb183bf25a5..e25ef4ff4a8d 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -48,6 +48,7 @@ #include "gfxPlatform.h" #include "nsCSSRules.h" #include "nsFontFaceLoader.h" +#include "mozilla/AnimationEventDispatcher.h" #include "mozilla/EffectCompositor.h" #include "mozilla/EventListenerManager.h" #include "prenv.h" @@ -442,8 +443,7 @@ nsPresContext::LastRelease() NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationManager); - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransitionManager); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument); // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor); @@ -457,8 +457,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationManager); - NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransitionManager); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering? NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor); @@ -888,6 +887,7 @@ nsPresContext::Init(nsDeviceContext* aDeviceContext) mEventManager = new mozilla::EventStateManager(); + mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this); mEffectCompositor = new mozilla::EffectCompositor(this); mTransitionManager = new nsTransitionManager(this); mAnimationManager = new nsAnimationManager(this); @@ -1066,6 +1066,10 @@ nsPresContext::DetachShell() mShell = nullptr; + if (mAnimationEventDispatcher) { + mAnimationEventDispatcher->Disconnect(); + mAnimationEventDispatcher = nullptr; + } if (mEffectCompositor) { mEffectCompositor->Disconnect(); mEffectCompositor = nullptr; diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index b200c6e86966..e18f4bacd0e2 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -73,6 +73,7 @@ class nsDeviceContext; class gfxMissingFontRecorder; namespace mozilla { +class AnimationEventDispatcher; class EffectCompositor; class Encoding; class EventStateManager; @@ -236,6 +237,11 @@ public: nsCSSFrameConstructor* FrameConstructor() { return PresShell()->FrameConstructor(); } + mozilla::AnimationEventDispatcher* AnimationEventDispatcher() + { + return mAnimationEventDispatcher; + } + mozilla::EffectCompositor* EffectCompositor() { return mEffectCompositor; } nsTransitionManager* TransitionManager() { return mTransitionManager; } nsAnimationManager* AnimationManager() { return mAnimationManager; } @@ -1296,6 +1302,7 @@ protected: // from gfx back to layout. RefPtr mEventManager; RefPtr mRefreshDriver; + RefPtr mAnimationEventDispatcher; RefPtr mEffectCompositor; RefPtr mTransitionManager; RefPtr mAnimationManager; diff --git a/layout/base/nsRefreshDriver.cpp b/layout/base/nsRefreshDriver.cpp index 01bb8b5d3666..9f4545e782b7 100644 --- a/layout/base/nsRefreshDriver.cpp +++ b/layout/base/nsRefreshDriver.cpp @@ -25,6 +25,7 @@ #include "WinUtils.h" #endif +#include "mozilla/AnimationEventDispatcher.h" #include "mozilla/ArrayUtils.h" #include "mozilla/AutoRestore.h" #include "mozilla/IntegerRange.h" @@ -1428,6 +1429,7 @@ nsRefreshDriver::ObserverCount() const // changes can trigger transitions which fire events when they complete, and // layout changes can affect media queries on child documents, triggering // style changes, etc. + sum += mAnimationEventFlushObservers.Length(); sum += mResizeEventFlushObservers.Length(); sum += mStyleFlushObservers.Length(); sum += mLayoutFlushObservers.Length(); @@ -1439,14 +1441,36 @@ nsRefreshDriver::ObserverCount() const return sum; } -uint32_t -nsRefreshDriver::ImageRequestCount() const +bool +nsRefreshDriver::HasObservers() const { - uint32_t count = 0; - for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) { - count += iter.UserData()->mEntries.Count(); + for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) { + if (!mObservers[i].IsEmpty()) { + return true; + } } - return count + mRequests.Count(); + + return mViewManagerFlushIsPending || + !mStyleFlushObservers.IsEmpty() || + !mLayoutFlushObservers.IsEmpty() || + !mAnimationEventFlushObservers.IsEmpty() || + !mResizeEventFlushObservers.IsEmpty() || + !mPendingEvents.IsEmpty() || + !mFrameRequestCallbackDocs.IsEmpty() || + !mThrottledFrameRequestCallbackDocs.IsEmpty() || + !mEarlyRunners.IsEmpty(); +} + +bool +nsRefreshDriver::HasImageRequests() const +{ + for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) { + if (!iter.UserData()->mEntries.IsEmpty()) { + return true; + } + } + + return !mRequests.IsEmpty(); } nsRefreshDriver::ObserverArray& @@ -1578,15 +1602,6 @@ nsRefreshDriver::DispatchPendingEvents() } } -static bool -CollectDocuments(nsIDocument* aDocument, void* aDocArray) -{ - static_cast, 32>*>(aDocArray)-> - AppendElement(aDocument); - aDocument->EnumerateSubDocuments(CollectDocuments, aDocArray); - return true; -} - void nsRefreshDriver::UpdateIntersectionObservations() { @@ -1616,31 +1631,19 @@ nsRefreshDriver::DispatchAnimationEvents() return; } - AutoTArray, 32> documents; - CollectDocuments(mPresContext->Document(), &documents); + // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as + // a RefPtr<> array since each AnimationEventDispatcher might be destroyed + // during processing the previous dispatcher. + size_t len = mAnimationEventFlushObservers.Length(); + AutoTArray, 16> dispatchers; + RefPtr* elems = dispatchers.AppendElements(len); + for (size_t i = 0; i < len; i++) { + elems[i] = mAnimationEventFlushObservers[i]; + } + mAnimationEventFlushObservers.Clear(); - for (uint32_t i = 0; i < documents.Length(); ++i) { - nsIDocument* doc = documents[i]; - nsIPresShell* shell = doc->GetShell(); - if (!shell) { - continue; - } - - RefPtr context = shell->GetPresContext(); - if (!context || context->RefreshDriver() != this) { - continue; - } - - context->TransitionManager()->SortEvents(); - context->AnimationManager()->SortEvents(); - - // Dispatch transition events first since transitions conceptually sit - // below animations in terms of compositing order. - context->TransitionManager()->DispatchEvents(); - // Check that the presshell has not been destroyed - if (context->GetPresShell()) { - context->AnimationManager()->DispatchEvents(); - } + for (auto& dispatcher : dispatchers) { + dispatcher->DispatchEvents(); } } @@ -1818,7 +1821,8 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime) mWarningThreshold = 1; nsCOMPtr presShell = mPresContext->GetPresShell(); - if (!presShell || (ObserverCount() == 0 && ImageRequestCount() == 0 && mScrollEvents.Length() == 0)) { + if (!presShell || + (!HasObservers() && !HasImageRequests() && mScrollEvents.IsEmpty())) { // Things are being destroyed, or we no longer have any observers. // We don't want to stop the timer when observers are initially // removed, because sometimes observers can be added and removed @@ -2140,7 +2144,7 @@ nsRefreshDriver::Thaw() } if (mFreezeCount == 0) { - if (ObserverCount() || ImageRequestCount()) { + if (HasObservers() || HasImageRequests()) { // FIXME: This isn't quite right, since our EnsureTimerStarted call // updates our mMostRecentRefresh, but the DoRefresh call won't run // and notify our observers until we get back to the event loop. @@ -2165,7 +2169,7 @@ nsRefreshDriver::FinishedWaitingForTransaction() mWaitingForTransaction = false; if (mSkippedPaints && !IsInRefresh() && - (ObserverCount() || ImageRequestCount())) { + (HasObservers() || HasImageRequests())) { AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick"); DoRefresh(); } @@ -2398,6 +2402,14 @@ nsRefreshDriver::CancelPendingEvents(nsIDocument* aDocument) } } +void +nsRefreshDriver::CancelPendingAnimationEvents(AnimationEventDispatcher* aDispatcher) +{ + MOZ_ASSERT(aDispatcher); + aDispatcher->ClearEventQueue(); + mAnimationEventFlushObservers.RemoveElement(aDispatcher); +} + /* static */ TimeStamp nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) { diff --git a/layout/base/nsRefreshDriver.h b/layout/base/nsRefreshDriver.h index 10babae47898..8b8cc765aeae 100644 --- a/layout/base/nsRefreshDriver.h +++ b/layout/base/nsRefreshDriver.h @@ -22,6 +22,7 @@ #include "nsTObserverArray.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" +#include "mozilla/AnimationEventDispatcher.h" #include "mozilla/Attributes.h" #include "mozilla/Maybe.h" #include "mozilla/layers/TransactionIdAllocator.h" @@ -243,6 +244,24 @@ public: */ void CancelPendingEvents(nsIDocument* aDocument); + /** + * Queue new animation events to dispatch in next tick. + */ + void ScheduleAnimationEventDispatch( + mozilla::AnimationEventDispatcher* aDispatcher) + { + NS_ASSERTION(!mAnimationEventFlushObservers.Contains(aDispatcher), + "Double-adding animation event flush observer"); + mAnimationEventFlushObservers.AppendElement(aDispatcher); + EnsureTimerStarted(); + } + + /** + * Cancel all pending animation events associated with |aDispatcher|. + */ + void CancelPendingAnimationEvents( + mozilla::AnimationEventDispatcher* aDispatcher); + /** * Schedule a frame visibility update "soon", subject to the heuristics and * throttling we apply to visibility updates. @@ -397,8 +416,9 @@ private: void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone); void StopTimer(); + bool HasObservers() const; uint32_t ObserverCount() const; - uint32_t ImageRequestCount() const; + bool HasImageRequests() const; ObserverArray& ArrayFor(mozilla::FlushType aFlushType); // Trigger a refresh immediately, if haven't been disconnected or frozen. void DoRefresh(); @@ -495,6 +515,8 @@ private: nsTArray mThrottledFrameRequestCallbackDocs; nsTObserverArray mPostRefreshObservers; nsTArray mPendingEvents; + AutoTArray + mAnimationEventFlushObservers; void BeginRefreshingImages(RequestTable& aEntries, mozilla::TimeStamp aDesired); diff --git a/layout/style/AnimationCollection.cpp b/layout/style/AnimationCollection.cpp index 3fce2d5eb629..e5d3055376af 100644 --- a/layout/style/AnimationCollection.cpp +++ b/layout/style/AnimationCollection.cpp @@ -116,23 +116,6 @@ AnimationCollection::GetOrCreateAnimationCollection( return collection; } -template -/* static */ nsString -AnimationCollection::PseudoTypeAsString( - CSSPseudoElementType aPseudoType) -{ - switch (aPseudoType) { - case CSSPseudoElementType::before: - return NS_LITERAL_STRING("::before"); - case CSSPseudoElementType::after: - return NS_LITERAL_STRING("::after"); - default: - MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo, - "Unexpected pseudo type"); - return EmptyString(); - } -} - template void AnimationCollection::UpdateCheckGeneration( diff --git a/layout/style/AnimationCollection.h b/layout/style/AnimationCollection.h index bf5fafc34c58..fb66c71e4de5 100644 --- a/layout/style/AnimationCollection.h +++ b/layout/style/AnimationCollection.h @@ -88,8 +88,6 @@ public: CSSPseudoElementType aPseudoType, bool* aCreatedCollection); - static nsString PseudoTypeAsString(CSSPseudoElementType aPseudoType); - dom::Element *mElement; // the atom we use in mElement's prop table (must be a static atom, diff --git a/layout/style/AnimationCommon.h b/layout/style/AnimationCommon.h index ecfb59fa78b3..ec39be8c3622 100644 --- a/layout/style/AnimationCommon.h +++ b/layout/style/AnimationCommon.h @@ -7,33 +7,25 @@ #ifndef mozilla_css_AnimationCommon_h #define mozilla_css_AnimationCommon_h -#include // For #include "mozilla/AnimationCollection.h" -#include "mozilla/AnimationComparator.h" -#include "mozilla/EventDispatcher.h" #include "mozilla/LinkedList.h" -#include "mozilla/MemoryReporting.h" #include "mozilla/dom/Animation.h" -#include "mozilla/AnimationTarget.h" #include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF #include "mozilla/Assertions.h" #include "mozilla/TimingParams.h" #include "nsContentUtils.h" -#include "nsCSSPseudoElements.h" -#include "nsCycleCollectionParticipant.h" class nsIFrame; class nsPresContext; namespace mozilla { enum class CSSPseudoElementType : uint8_t; -template class DelayedEventDispatcher; namespace dom { class Element; } -template +template class CommonAnimationManager { public: explicit CommonAnimationManager(nsPresContext *aPresContext) @@ -75,18 +67,6 @@ public: collection->Destroy(); } - /** - * Add pending events. - */ - void QueueEvents(nsTArray&& aEvents) - { - mEventDispatcher.QueueEvents( - mozilla::Forward>(aEvents)); - } - - void SortEvents() { mEventDispatcher.SortEvents(); } - void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); } - protected: virtual ~CommonAnimationManager() { @@ -107,8 +87,6 @@ protected: LinkedList> mElementCollections; nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect) - - mozilla::DelayedEventDispatcher mEventDispatcher; }; /** @@ -179,122 +157,6 @@ private: NonOwningAnimationTarget mTarget; }; -template -class DelayedEventDispatcher -{ -public: - DelayedEventDispatcher() : mIsSorted(true) { } - - void QueueEvents(nsTArray&& aEvents) - { - mPendingEvents.AppendElements(Forward>(aEvents)); - mIsSorted = false; - } - - // This is exposed as a separate method so that when we are dispatching - // *both* transition events and animation events we can sort both lists - // once using the current state of the document before beginning any - // dispatch. - void SortEvents() - { - if (mIsSorted) { - return; - } - - // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is - // fixed. - std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(), - EventInfoLessThan()); - mIsSorted = true; - } - - // Takes a reference to the owning manager's pres context so it can - // detect if the pres context is destroyed while dispatching one of - // the events. - // - // This will call SortEvents automatically if it has not already been - // called. - void DispatchEvents(nsPresContext* const & aPresContext) - { - if (!aPresContext || mPendingEvents.IsEmpty()) { - return; - } - - SortEvents(); - - EventArray events; - mPendingEvents.SwapElements(events); - // mIsSorted will be set to true by SortEvents above, and we leave it - // that way since mPendingEvents is now empty - for (EventInfo& info : events) { - EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent); - - if (!aPresContext) { - break; - } - } - } - - void ClearEventQueue() - { - mPendingEvents.Clear(); - mIsSorted = true; - } - bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); } - - // Methods for supporting cycle-collection - void Traverse(nsCycleCollectionTraversalCallback* aCallback, - const char* aName) - { - for (EventInfo& info : mPendingEvents) { - ImplCycleCollectionTraverse(*aCallback, info.mElement, aName); - ImplCycleCollectionTraverse(*aCallback, info.mAnimation, aName); - } - } - void Unlink() { ClearEventQueue(); } - -protected: - class EventInfoLessThan - { - public: - bool operator()(const EventInfo& a, const EventInfo& b) const - { - if (a.mTimeStamp != b.mTimeStamp) { - // Null timestamps sort first - if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) { - return a.mTimeStamp.IsNull(); - } else { - return a.mTimeStamp < b.mTimeStamp; - } - } - - AnimationPtrComparator> comparator; - return comparator.LessThan(a.mAnimation, b.mAnimation); - } - }; - - typedef nsTArray EventArray; - EventArray mPendingEvents; - bool mIsSorted; -}; - -template -inline void -ImplCycleCollectionUnlink(DelayedEventDispatcher& aField) -{ - aField.Unlink(); -} - -template -inline void -ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, - DelayedEventDispatcher& aField, - const char* aName, - uint32_t aFlags = 0) -{ - aField.Traverse(&aCallback, aName); -} - // Return the TransitionPhase or AnimationPhase to use when the animation // doesn't have a target effect. template @@ -303,6 +165,12 @@ PhaseType GetAnimationPhaseWithoutEffect(const dom::Animation& aAnimation) MOZ_ASSERT(!aAnimation.GetEffect(), "Should only be called when we do not have an effect"); +// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to +// GetTickCount(). +#ifdef GetCurrentTime +#undef GetCurrentTime +#endif + Nullable currentTime = aAnimation.GetCurrentTime(); if (currentTime.IsNull()) { return PhaseType::Idle; diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index 2609a4d11f96..6d636937148b 100644 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -8,6 +8,7 @@ #include "nsTransitionManager.h" #include "mozilla/dom/CSSAnimationBinding.h" +#include "mozilla/AnimationEventDispatcher.h" #include "mozilla/AnimationTarget.h" #include "mozilla/EffectCompositor.h" #include "mozilla/EffectSet.h" @@ -242,9 +243,9 @@ CSSAnimation::QueueEvents(const StickyTimeDuration& aActiveTime) if (aMessage == eAnimationCancel) { elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime); } - events.AppendElement(AnimationEventInfo(mOwningElement.Target(), + events.AppendElement(AnimationEventInfo(mAnimationName, + mOwningElement.Target(), aMessage, - mAnimationName, elapsedTime, aTimeStamp, this)); @@ -299,7 +300,7 @@ CSSAnimation::QueueEvents(const StickyTimeDuration& aActiveTime) mPreviousIteration = currentIteration; if (!events.IsEmpty()) { - presContext->AnimationManager()->QueueEvents(Move(events)); + presContext->AnimationEventDispatcher()->QueueEvents(Move(events)); } } @@ -317,11 +318,6 @@ CSSAnimation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag) ////////////////////////// nsAnimationManager //////////////////////////// -NS_IMPL_CYCLE_COLLECTION(nsAnimationManager, mEventDispatcher) - -NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsAnimationManager, AddRef) -NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsAnimationManager, Release) - // Find the matching animation by |aName| in the old list // of animations and remove the matched animation from the list. static already_AddRefed diff --git a/layout/style/nsAnimationManager.h b/layout/style/nsAnimationManager.h index caa796b8334e..39dac93ab4f6 100644 --- a/layout/style/nsAnimationManager.h +++ b/layout/style/nsAnimationManager.h @@ -14,6 +14,7 @@ #include "mozilla/Keyframe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/TimeStamp.h" +#include "nsISupportsImpl.h" class nsIGlobalObject; class nsStyleContext; @@ -34,43 +35,6 @@ class ServoStyleContext; enum class CSSPseudoElementType : uint8_t; struct NonOwningAnimationTarget; -struct AnimationEventInfo { - RefPtr mElement; - RefPtr mAnimation; - InternalAnimationEvent mEvent; - TimeStamp mTimeStamp; - - AnimationEventInfo(const NonOwningAnimationTarget& aTarget, - EventMessage aMessage, - nsAtom* aAnimationName, - double aElapsedTime, - const TimeStamp& aTimeStamp, - dom::Animation* aAnimation) - : mElement(aTarget.mElement) - , mAnimation(aAnimation) - , mEvent(true, aMessage) - , mTimeStamp(aTimeStamp) - { - // XXX Looks like nobody initialize WidgetEvent::time - aAnimationName->ToString(mEvent.mAnimationName); - mEvent.mElapsedTime = aElapsedTime; - mEvent.mPseudoElement = - AnimationCollection::PseudoTypeAsString( - aTarget.mPseudoType); - } - - // InternalAnimationEvent doesn't support copy-construction, so we need - // to ourselves in order to work with nsTArray - AnimationEventInfo(const AnimationEventInfo& aOther) - : mElement(aOther.mElement) - , mAnimation(aOther.mAnimation) - , mEvent(true, aOther.mEvent.mMessage) - , mTimeStamp(aOther.mTimeStamp) - { - mEvent.AssignAnimationEventData(aOther.mEvent, false); - } -}; - namespace dom { class CSSAnimation final : public Animation @@ -311,18 +275,15 @@ struct AnimationTypeTraits } /* namespace mozilla */ class nsAnimationManager final - : public mozilla::CommonAnimationManager + : public mozilla::CommonAnimationManager { public: explicit nsAnimationManager(nsPresContext *aPresContext) - : mozilla::CommonAnimationManager(aPresContext) + : mozilla::CommonAnimationManager(aPresContext) { } - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager) - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager) + NS_INLINE_DECL_REFCOUNTING(nsAnimationManager) typedef mozilla::AnimationCollection CSSAnimationCollection; @@ -349,19 +310,6 @@ public: mozilla::CSSPseudoElementType aPseudoType, const mozilla::ServoStyleContext* aComputedValues); - /** - * Dispatch any pending events. We accumulate animationend and - * animationiteration events only during refresh driver notifications - * (and dispatch them at the end of such notifications), but we - * accumulate animationstart events at other points when style - * contexts are created. - */ - void DispatchEvents() - { - RefPtr kungFuDeathGrip(this); - mEventDispatcher.DispatchEvents(mPresContext); - } - // Utility function to walk through |aIter| to find the Keyframe with // matching offset and timing function but stopping as soon as the offset // differs from |aOffset| (i.e. it assumes a sorted iterator). diff --git a/layout/style/nsCSSPseudoElements.cpp b/layout/style/nsCSSPseudoElements.cpp index b1d4c66da002..fdaaed6ef544 100644 --- a/layout/style/nsCSSPseudoElements.cpp +++ b/layout/style/nsCSSPseudoElements.cpp @@ -157,3 +157,19 @@ nsCSSPseudoElements::PseudoElementSupportsUserActionState(const Type aType) return PseudoElementHasFlags(aType, CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE); } + +/* static */ nsString +nsCSSPseudoElements::PseudoTypeAsString(Type aPseudoType) +{ + switch (aPseudoType) { + case CSSPseudoElementType::before: + return NS_LITERAL_STRING("::before"); + case CSSPseudoElementType::after: + return NS_LITERAL_STRING("::after"); + default: + MOZ_ASSERT(aPseudoType == CSSPseudoElementType::NotPseudo, + "Unexpected pseudo type"); + return EmptyString(); + } +} + diff --git a/layout/style/nsCSSPseudoElements.h b/layout/style/nsCSSPseudoElements.h index 3794eb33c6de..4268106f0d85 100644 --- a/layout/style/nsCSSPseudoElements.h +++ b/layout/style/nsCSSPseudoElements.h @@ -141,6 +141,8 @@ public: (aEnabledState & EnabledState::eInUASheets); } + static nsString PseudoTypeAsString(Type aPseudoType); + private: // Does the given pseudo-element have all of the flags given? diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp index c76dc92d44de..13991bd86e01 100644 --- a/layout/style/nsTransitionManager.cpp +++ b/layout/style/nsTransitionManager.cpp @@ -19,6 +19,7 @@ #include "nsRuleProcessorData.h" #include "nsRuleWalker.h" #include "nsCSSPropertyIDSet.h" +#include "mozilla/AnimationEventDispatcher.h" #include "mozilla/EffectCompositor.h" #include "mozilla/EffectSet.h" #include "mozilla/EventDispatcher.h" @@ -249,7 +250,7 @@ CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime) currentPhase = TransitionPhase::Pending; } - AutoTArray events; + AutoTArray events; auto appendTransitionEvent = [&](EventMessage aMessage, const StickyTimeDuration& aElapsedTime, @@ -258,12 +259,12 @@ CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime) if (aMessage == eTransitionCancel) { elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime); } - events.AppendElement(TransitionEventInfo(mOwningElement.Target(), - aMessage, - TransitionProperty(), - elapsedTime, - aTimeStamp, - this)); + events.AppendElement(AnimationEventInfo(TransitionProperty(), + mOwningElement.Target(), + aMessage, + elapsedTime, + aTimeStamp, + this)); }; // Handle cancel events first @@ -336,7 +337,7 @@ CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime) mPreviousTransitionPhase = currentPhase; if (!events.IsEmpty()) { - presContext->TransitionManager()->QueueEvents(Move(events)); + presContext->AnimationEventDispatcher()->QueueEvents(Move(events)); } } @@ -422,11 +423,6 @@ CSSTransition::SetEffectFromStyle(dom::AnimationEffectReadOnly* aEffect) ////////////////////////// nsTransitionManager //////////////////////////// -NS_IMPL_CYCLE_COLLECTION(nsTransitionManager, mEventDispatcher) - -NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsTransitionManager, AddRef) -NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsTransitionManager, Release) - static inline bool ExtractNonDiscreteComputedValue(nsCSSPropertyID aProperty, GeckoStyleContext* aStyleContext, diff --git a/layout/style/nsTransitionManager.h b/layout/style/nsTransitionManager.h index d942d95f40b6..67621abeb963 100644 --- a/layout/style/nsTransitionManager.h +++ b/layout/style/nsTransitionManager.h @@ -10,13 +10,11 @@ #define nsTransitionManager_h_ #include "mozilla/ComputedTiming.h" -#include "mozilla/ContentEvents.h" #include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel -#include "mozilla/MemoryReporting.h" #include "mozilla/dom/Animation.h" #include "mozilla/dom/KeyframeEffectReadOnly.h" #include "AnimationCommon.h" -#include "nsCSSProps.h" +#include "nsISupportsImpl.h" class nsIGlobalObject; class nsStyleContext; @@ -301,60 +299,19 @@ struct AnimationTypeTraits } }; -struct TransitionEventInfo { - RefPtr mElement; - RefPtr mAnimation; - InternalTransitionEvent mEvent; - TimeStamp mTimeStamp; - - TransitionEventInfo(const NonOwningAnimationTarget& aTarget, - EventMessage aMessage, - nsCSSPropertyID aProperty, - double aElapsedTime, - const TimeStamp& aTimeStamp, - dom::Animation* aAnimation) - : mElement(aTarget.mElement) - , mAnimation(aAnimation) - , mEvent(true, aMessage) - , mTimeStamp(aTimeStamp) - { - // XXX Looks like nobody initialize WidgetEvent::time - mEvent.mPropertyName = - NS_ConvertUTF8toUTF16(nsCSSProps::GetStringValue(aProperty)); - mEvent.mElapsedTime = aElapsedTime; - mEvent.mPseudoElement = - AnimationCollection::PseudoTypeAsString( - aTarget.mPseudoType); - } - - // InternalTransitionEvent doesn't support copy-construction, so we need - // to ourselves in order to work with nsTArray - TransitionEventInfo(const TransitionEventInfo& aOther) - : mElement(aOther.mElement) - , mAnimation(aOther.mAnimation) - , mEvent(aOther.mEvent) - , mTimeStamp(aOther.mTimeStamp) - { - mEvent.AssignTransitionEventData(aOther.mEvent, false); - } -}; - } // namespace mozilla class nsTransitionManager final - : public mozilla::CommonAnimationManager + : public mozilla::CommonAnimationManager { public: explicit nsTransitionManager(nsPresContext *aPresContext) - : mozilla::CommonAnimationManager(aPresContext) + : mozilla::CommonAnimationManager(aPresContext) , mInAnimationOnlyStyleUpdate(false) { } - NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsTransitionManager) - NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsTransitionManager) + NS_INLINE_DECL_REFCOUNTING(nsTransitionManager) typedef mozilla::AnimationCollection CSSTransitionCollection; @@ -406,12 +363,6 @@ public: return mInAnimationOnlyStyleUpdate; } - void DispatchEvents() - { - RefPtr kungFuDeathGrip(this); - mEventDispatcher.DispatchEvents(mPresContext); - } - protected: virtual ~nsTransitionManager() {} diff --git a/servo/components/layout/query.rs b/servo/components/layout/query.rs index eac23f563acf..c5da8694077a 100644 --- a/servo/components/layout/query.rs +++ b/servo/components/layout/query.rs @@ -16,9 +16,9 @@ use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; use opaque_node::OpaqueNodeMethods; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; -use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse}; -use script_layout_interface::rpc::{NodeOverflowResponse, NodeScrollRootIdResponse}; -use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse, TextIndexResponse}; +use script_layout_interface::rpc::{NodeGeometryResponse, NodeScrollRootIdResponse}; +use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse, StyleResponse}; +use script_layout_interface::rpc::TextIndexResponse; use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use script_traits::LayoutMsg as ConstellationMsg; use script_traits::UntrustedNodeAddress; @@ -59,9 +59,6 @@ pub struct LayoutThreadData { /// A queued response for the scroll root id for a given node. pub scroll_root_id_response: Option, - /// A pair of overflow property in x and y - pub overflow_response: NodeOverflowResponse, - /// A queued response for the scroll {top, left, width, height} of a node in pixels. pub scroll_area_response: Rect, @@ -71,8 +68,8 @@ pub struct LayoutThreadData { /// A queued response for the offset parent/rect of a node. pub offset_parent_response: OffsetParentResponse, - /// A queued response for the offset parent/rect of a node. - pub margin_style_response: MarginStyleResponse, + /// A queued response for the style of a node. + pub style_response: StyleResponse, /// Scroll offsets of scrolling regions. pub scroll_offsets: ScrollOffsetMap, @@ -128,10 +125,6 @@ impl LayoutRPC for LayoutRPCImpl { } } - fn node_overflow(&self) -> NodeOverflowResponse { - NodeOverflowResponse(self.0.lock().unwrap().overflow_response.0) - } - fn node_scroll_area(&self) -> NodeGeometryResponse { NodeGeometryResponse { client_rect: self.0.lock().unwrap().scroll_area_response @@ -157,10 +150,10 @@ impl LayoutRPC for LayoutRPCImpl { rw_data.offset_parent_response.clone() } - fn margin_style(&self) -> MarginStyleResponse { + fn style(&self) -> StyleResponse { let &LayoutRPCImpl(ref rw_data) = self; let rw_data = rw_data.lock().unwrap(); - rw_data.margin_style_response.clone() + rw_data.style_response.clone() } fn text_index(&self) -> TextIndexResponse { @@ -863,24 +856,10 @@ pub fn process_offset_parent_query(requested_node: N, layout_root } } -pub fn process_node_overflow_request(requested_node: N) -> NodeOverflowResponse { - let layout_node = requested_node.to_threadsafe(); - let style = &*layout_node.as_element().unwrap().resolved_style(); - let style_box = style.get_box(); +pub fn process_style_query(requested_node: N) + -> StyleResponse { + let element = requested_node.as_element().unwrap(); + let data = element.borrow_data(); - NodeOverflowResponse(Some(Point2D::new(style_box.overflow_x, style_box.overflow_y))) -} - -pub fn process_margin_style_query(requested_node: N) - -> MarginStyleResponse { - let layout_node = requested_node.to_threadsafe(); - let style = &*layout_node.as_element().unwrap().resolved_style(); - let margin = style.get_margin(); - - MarginStyleResponse { - top: margin.margin_top, - right: margin.margin_right, - bottom: margin.margin_bottom, - left: margin.margin_left, - } + StyleResponse(data.map(|d| d.styles.primary().clone())) } diff --git a/servo/components/layout_thread/lib.rs b/servo/components/layout_thread/lib.rs index 66f328f4465b..5606b8782f76 100644 --- a/servo/components/layout_thread/lib.rs +++ b/servo/components/layout_thread/lib.rs @@ -75,9 +75,9 @@ use layout::incremental::{LayoutDamageComputation, RelayoutMode, SpecialRestyleD use layout::layout_debug; use layout::parallel; use layout::query::{LayoutRPCImpl, LayoutThreadData, process_content_box_request, process_content_boxes_request}; -use layout::query::{process_margin_style_query, process_node_overflow_request, process_resolved_style_request}; use layout::query::{process_node_geometry_request, process_node_scroll_area_request}; -use layout::query::{process_node_scroll_root_id_request, process_offset_parent_query}; +use layout::query::{process_node_scroll_root_id_request, process_offset_parent_query, process_resolved_style_request}; +use layout::query::process_style_query; use layout::sequential; use layout::traversal::{ComputeStackingRelativePositions, PreorderFlowTraversal, RecalcStyleAndConstructFlows}; use layout::wrapper::LayoutNodeLayoutData; @@ -94,7 +94,7 @@ use profile_traits::time::{self, TimerMetadata, profile}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; use script_layout_interface::message::{Msg, NewLayoutThreadInfo, NodesFromPointQueryType, Reflow}; use script_layout_interface::message::{ReflowComplete, ReflowGoal, ScriptReflow}; -use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse}; +use script_layout_interface::rpc::{LayoutRPC, StyleResponse, OffsetParentResponse}; use script_layout_interface::rpc::TextIndexResponse; use script_layout_interface::wrapper_traits::LayoutNode; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; @@ -519,10 +519,9 @@ impl LayoutThread { client_rect_response: Rect::zero(), scroll_root_id_response: None, scroll_area_response: Rect::zero(), - overflow_response: NodeOverflowResponse(None), resolved_style_response: String::new(), offset_parent_response: OffsetParentResponse::empty(), - margin_style_response: MarginStyleResponse::empty(), + style_response: StyleResponse(None), scroll_offsets: HashMap::new(), text_index_response: TextIndexResponse(None), nodes_from_point_response: vec![], @@ -1092,9 +1091,6 @@ impl LayoutThread { ReflowGoal::NodeScrollGeometryQuery(_) => { rw_data.scroll_area_response = Rect::zero(); }, - ReflowGoal::NodeOverflowQuery(_) => { - rw_data.overflow_response = NodeOverflowResponse(None); - }, ReflowGoal::NodeScrollRootIdQuery(_) => { rw_data.scroll_root_id_response = None; }, @@ -1104,8 +1100,8 @@ impl LayoutThread { ReflowGoal::OffsetParentQuery(_) => { rw_data.offset_parent_response = OffsetParentResponse::empty(); }, - ReflowGoal::MarginStyleQuery(_) => { - rw_data.margin_style_response = MarginStyleResponse::empty(); + ReflowGoal::StyleQuery(_) => { + rw_data.style_response = StyleResponse(None); }, ReflowGoal::TextIndexQuery(..) => { rw_data.text_index_response = TextIndexResponse(None); @@ -1379,10 +1375,6 @@ impl LayoutThread { let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.scroll_area_response = process_node_scroll_area_request(node, root_flow); }, - ReflowGoal::NodeOverflowQuery(node) => { - let node = unsafe { ServoLayoutNode::new(&node) }; - rw_data.overflow_response = process_node_overflow_request(node); - }, ReflowGoal::NodeScrollRootIdQuery(node) => { let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.scroll_root_id_response = Some(process_node_scroll_root_id_request(self.id, @@ -1401,9 +1393,9 @@ impl LayoutThread { let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.offset_parent_response = process_offset_parent_query(node, root_flow); }, - ReflowGoal::MarginStyleQuery(node) => { + ReflowGoal::StyleQuery(node) => { let node = unsafe { ServoLayoutNode::new(&node) }; - rw_data.margin_style_response = process_margin_style_query(node); + rw_data.style_response = process_style_query(node); }, ReflowGoal::NodesFromPointQuery(client_point, ref reflow_goal) => { let mut flags = match reflow_goal { diff --git a/servo/components/script/devtools.rs b/servo/components/script/devtools.rs index 585ecd47a15f..0d2b4a9e4eb3 100644 --- a/servo/components/script/devtools.rs +++ b/servo/components/script/devtools.rs @@ -154,12 +154,13 @@ pub fn handle_get_layout(documents: &Documents, } fn determine_auto_margins(window: &Window, node: &Node) -> AutoMargins { - let margin = window.margin_style_query(node.to_trusted_node_address()); + let style = window.style_query(node.to_trusted_node_address()).unwrap(); + let margin = style.get_margin(); AutoMargins { - top: margin.top == margin_top::computed_value::T::Auto, - right: margin.right == margin_right::computed_value::T::Auto, - bottom: margin.bottom == margin_bottom::computed_value::T::Auto, - left: margin.left == margin_left::computed_value::T::Auto, + top: margin.margin_top == margin_top::computed_value::T::Auto, + right: margin.margin_right == margin_right::computed_value::T::Auto, + bottom: margin.margin_bottom == margin_bottom::computed_value::T::Auto, + left: margin.margin_left == margin_left::computed_value::T::Auto, } } diff --git a/servo/components/script/dom/element.rs b/servo/components/script/dom/element.rs index c6224e7dc877..16a84737c47e 100644 --- a/servo/components/script/dom/element.rs +++ b/servo/components/script/dom/element.rs @@ -107,7 +107,8 @@ use style::context::QuirksMode; use style::dom_apis; use style::element_state::ElementState; use style::invalidation::element::restyle_hints::RestyleHint; -use style::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute}; +use style::properties::{ComputedValues, Importance, PropertyDeclaration}; +use style::properties::{PropertyDeclarationBlock, parse_style_attribute}; use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size}; use style::properties::longhands::{overflow_x, overflow_y}; use style::rule_tree::CascadeLevel; @@ -347,28 +348,30 @@ impl Element { } } + /// style will be `None` for elements in a `display: none` subtree. otherwise, the element has a + /// layout box iff it doesn't have `display: none`. + fn style(&self) -> Option> { + window_from_node(self).style_query( + self.upcast::().to_trusted_node_address() + ) + } + // https://drafts.csswg.org/cssom-view/#css-layout-box - // - // We'll have no content box if there's no fragment for the node, and we use - // bounding_content_box, for simplicity, to detect this (rather than making a more specific - // query to the layout thread). fn has_css_layout_box(&self) -> bool { - self.upcast::().bounding_content_box().is_some() + self.style() + .map_or(false, |s| !s.get_box().clone_display().is_none()) } // https://drafts.csswg.org/cssom-view/#potentially-scrollable fn potentially_scrollable(&self) -> bool { - self.has_css_layout_box() && - !self.overflow_x_is_visible() && - !self.overflow_y_is_visible() + self.has_css_layout_box() && !self.has_any_visible_overflow() } // https://drafts.csswg.org/cssom-view/#scrolling-box fn has_scrolling_box(&self) -> bool { // TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet) // self.has_scrolling_mechanism() - self.overflow_x_is_hidden() || - self.overflow_y_is_hidden() + self.has_any_hidden_overflow() } fn has_overflow(&self) -> bool { @@ -376,32 +379,29 @@ impl Element { self.ScrollWidth() > self.ClientWidth() } - // used value of overflow-x is "visible" - fn overflow_x_is_visible(&self) -> bool { - let window = window_from_node(self); - let overflow_pair = window.overflow_query(self.upcast::().to_trusted_node_address()); - overflow_pair.x == overflow_x::computed_value::T::Visible + // TODO: Once #19183 is closed (overflow-x/y types moved out of mako), then we could implement + // a more generic `fn has_some_overflow(&self, overflow: Overflow)` rather than have + // these two `has_any_{visible,hidden}_overflow` methods which are very structurally + // similar. + + /// Computed value of overflow-x or overflow-y is "visible" + fn has_any_visible_overflow(&self) -> bool { + self.style().map_or(false, |s| { + let box_ = s.get_box(); + + box_.clone_overflow_x() == overflow_x::computed_value::T::Visible || + box_.clone_overflow_y() == overflow_y::computed_value::T::Visible + }) } - // used value of overflow-y is "visible" - fn overflow_y_is_visible(&self) -> bool { - let window = window_from_node(self); - let overflow_pair = window.overflow_query(self.upcast::().to_trusted_node_address()); - overflow_pair.y == overflow_y::computed_value::T::Visible - } + /// Computed value of overflow-x or overflow-y is "hidden" + fn has_any_hidden_overflow(&self) -> bool { + self.style().map_or(false, |s| { + let box_ = s.get_box(); - // used value of overflow-x is "hidden" - fn overflow_x_is_hidden(&self) -> bool { - let window = window_from_node(self); - let overflow_pair = window.overflow_query(self.upcast::().to_trusted_node_address()); - overflow_pair.x == overflow_x::computed_value::T::Hidden - } - - // used value of overflow-y is "hidden" - fn overflow_y_is_hidden(&self) -> bool { - let window = window_from_node(self); - let overflow_pair = window.overflow_query(self.upcast::().to_trusted_node_address()); - overflow_pair.y == overflow_y::computed_value::T::Hidden + box_.clone_overflow_x() == overflow_x::computed_value::T::Hidden || + box_.clone_overflow_y() == overflow_y::computed_value::T::Hidden + }) } } diff --git a/servo/components/script/dom/window.rs b/servo/components/script/dom/window.rs index 21713696f829..e9055f54ea0b 100644 --- a/servo/components/script/dom/window.rs +++ b/servo/components/script/dom/window.rs @@ -72,8 +72,7 @@ use script_layout_interface::{TrustedNodeAddress, PendingImageState}; use script_layout_interface::message::{Msg, Reflow, ReflowGoal, ScriptReflow}; use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; -use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse}; -use script_layout_interface::rpc::{ResolvedStyleResponse, TextIndexResponse}; +use script_layout_interface::rpc::{NodeScrollRootIdResponse, ResolvedStyleResponse, TextIndexResponse}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory, Runtime}; use script_thread::{ImageCacheMsg, MainThreadScriptChan, MainThreadScriptMsg}; use script_thread::{ScriptThread, SendableMainThreadScriptChan}; @@ -82,6 +81,7 @@ use script_traits::{ScriptToConstellationChan, ScriptMsg, ScrollState, TimerEven use script_traits::{TimerSchedulerMsg, UntrustedNodeAddress, WindowSizeData, WindowSizeType}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use selectors::attr::CaseSensitivity; +use servo_arc; use servo_config::opts; use servo_config::prefs::PREFS; use servo_geometry::{f32_rect_to_au_rect, MaxRect}; @@ -102,8 +102,7 @@ use std::sync::mpsc::{Sender, channel}; use std::sync::mpsc::TryRecvError::{Disconnected, Empty}; use style::media_queries; use style::parser::ParserContext as CssParserContext; -use style::properties::PropertyId; -use style::properties::longhands::overflow_x; +use style::properties::{ComputedValues, PropertyId}; use style::selector_parser::PseudoElement; use style::str::HTML_SPACE_CHARACTERS; use style::stylesheets::CssRuleType; @@ -1403,16 +1402,6 @@ impl Window { self.layout_rpc.node_scroll_area().client_rect } - pub fn overflow_query(&self, - node: TrustedNodeAddress) -> Point2D { - // NB: This is only called if the document is fully active, and the only - // reason to bail out from a query is if there's no viewport, so this - // *must* issue a reflow. - assert!(self.reflow(ReflowGoal::NodeOverflowQuery(node), ReflowReason::Query)); - - self.layout_rpc.node_overflow().0.unwrap() - } - pub fn scroll_offset_query(&self, node: &Node) -> Vector2D { if let Some(scroll_offset) = self.scroll_offsets .borrow() @@ -1477,11 +1466,11 @@ impl Window { (element, response.rect) } - pub fn margin_style_query(&self, node: TrustedNodeAddress) -> MarginStyleResponse { - if !self.reflow(ReflowGoal::MarginStyleQuery(node), ReflowReason::Query) { - return MarginStyleResponse::empty(); + pub fn style_query(&self, node: TrustedNodeAddress) -> Option> { + if !self.reflow(ReflowGoal::StyleQuery(node), ReflowReason::Query) { + return None } - self.layout_rpc.margin_style() + self.layout_rpc.style().0 } pub fn text_index_query( @@ -1898,12 +1887,11 @@ fn debug_reflow_events(id: PipelineId, reflow_goal: &ReflowGoal, reason: &Reflow ReflowGoal::ContentBoxesQuery(_n) => "\tContentBoxesQuery", ReflowGoal::NodesFromPointQuery(..) => "\tNodesFromPointQuery", ReflowGoal::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", - ReflowGoal::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery", ReflowGoal::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery", ReflowGoal::NodeScrollRootIdQuery(_n) => "\tNodeScrollRootIdQuery", ReflowGoal::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery", ReflowGoal::OffsetParentQuery(_n) => "\tOffsetParentQuery", - ReflowGoal::MarginStyleQuery(_n) => "\tMarginStyleQuery", + ReflowGoal::StyleQuery(_n) => "\tStyleQuery", ReflowGoal::TextIndexQuery(..) => "\tTextIndexQuery", ReflowGoal::TickAnimations => "\tTickAnimations", }); diff --git a/servo/components/script_layout_interface/message.rs b/servo/components/script_layout_interface/message.rs index 7a1cb7e53166..6f2ab91420d2 100644 --- a/servo/components/script_layout_interface/message.rs +++ b/servo/components/script_layout_interface/message.rs @@ -113,13 +113,12 @@ pub enum ReflowGoal { TickAnimations, ContentBoxQuery(TrustedNodeAddress), ContentBoxesQuery(TrustedNodeAddress), - NodeOverflowQuery(TrustedNodeAddress), NodeScrollRootIdQuery(TrustedNodeAddress), NodeGeometryQuery(TrustedNodeAddress), NodeScrollGeometryQuery(TrustedNodeAddress), ResolvedStyleQuery(TrustedNodeAddress, Option, PropertyId), OffsetParentQuery(TrustedNodeAddress), - MarginStyleQuery(TrustedNodeAddress), + StyleQuery(TrustedNodeAddress), TextIndexQuery(TrustedNodeAddress, Point2D), NodesFromPointQuery(Point2D, NodesFromPointQueryType), } @@ -133,9 +132,9 @@ impl ReflowGoal { ReflowGoal::TickAnimations | ReflowGoal::Full => true, ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) | ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) | - ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) | + ReflowGoal::NodeScrollRootIdQuery(_) | ReflowGoal::ResolvedStyleQuery(..) | ReflowGoal::OffsetParentQuery(_) | - ReflowGoal::MarginStyleQuery(_) => false, + ReflowGoal::StyleQuery(_) => false, } } @@ -143,10 +142,10 @@ impl ReflowGoal { /// false if a layout_thread display list is sufficient. pub fn needs_display(&self) -> bool { match *self { - ReflowGoal::MarginStyleQuery(_) | ReflowGoal::TextIndexQuery(..) | + ReflowGoal::StyleQuery(_) | ReflowGoal::TextIndexQuery(..) | ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) | ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) | - ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) | + ReflowGoal::NodeScrollRootIdQuery(_) | ReflowGoal::ResolvedStyleQuery(..) | ReflowGoal::OffsetParentQuery(_) => false, ReflowGoal::NodesFromPointQuery(..) | ReflowGoal::Full | diff --git a/servo/components/script_layout_interface/rpc.rs b/servo/components/script_layout_interface/rpc.rs index 5702c8cce9d5..3988711eeaa5 100644 --- a/servo/components/script_layout_interface/rpc.rs +++ b/servo/components/script_layout_interface/rpc.rs @@ -5,7 +5,9 @@ use app_units::Au; use euclid::{Point2D, Rect}; use script_traits::UntrustedNodeAddress; -use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left, overflow_x}; +use servo_arc::Arc; +use style::properties::ComputedValues; +use style::properties::longhands::overflow_x; use webrender_api::ClipId; /// Synchronous messages that script can send to layout. @@ -23,8 +25,6 @@ pub trait LayoutRPC { fn content_boxes(&self) -> ContentBoxesResponse; /// Requests the geometry of this node. Used by APIs such as `clientTop`. fn node_geometry(&self) -> NodeGeometryResponse; - /// Requests the overflow-x and overflow-y of this node. Used by `scrollTop` etc. - fn node_overflow(&self) -> NodeOverflowResponse; /// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`. fn node_scroll_area(&self) -> NodeGeometryResponse; /// Requests the scroll root id of this node. Used by APIs such as `scrollTop` @@ -32,8 +32,9 @@ pub trait LayoutRPC { /// Query layout for the resolved value of a given CSS property fn resolved_style(&self) -> ResolvedStyleResponse; fn offset_parent(&self) -> OffsetParentResponse; - /// Query layout for the resolve values of the margin properties for an element. - fn margin_style(&self) -> MarginStyleResponse; + /// Requests the styles for an element. Contains a `None` value if the element is in a `display: + /// none` subtree. + fn style(&self) -> StyleResponse; fn text_index(&self) -> TextIndexResponse; /// Requests the list of nodes from the given point. fn nodes_from_point_response(&self) -> Vec; @@ -70,23 +71,7 @@ impl OffsetParentResponse { } #[derive(Clone)] -pub struct MarginStyleResponse { - pub top: margin_top::computed_value::T, - pub right: margin_right::computed_value::T, - pub bottom: margin_bottom::computed_value::T, - pub left: margin_left::computed_value::T, -} - -impl MarginStyleResponse { - pub fn empty() -> MarginStyleResponse { - MarginStyleResponse { - top: margin_top::computed_value::T::Auto, - right: margin_right::computed_value::T::Auto, - bottom: margin_bottom::computed_value::T::Auto, - left: margin_left::computed_value::T::Auto, - } - } -} +pub struct StyleResponse(pub Option>); #[derive(Clone)] pub struct TextIndexResponse(pub Option); diff --git a/servo/components/style/data.rs b/servo/components/style/data.rs index 56efedc41f03..275c8c4294d3 100644 --- a/servo/components/style/data.rs +++ b/servo/components/style/data.rs @@ -11,7 +11,6 @@ use invalidation::element::restyle_hints::RestyleHint; #[cfg(feature = "gecko")] use malloc_size_of::MallocSizeOfOps; use properties::ComputedValues; -use properties::longhands::display::computed_value::T as Display; use rule_tree::StrongRuleNode; use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage}; use selectors::NthIndexCache; @@ -169,7 +168,7 @@ impl ElementStyles { /// Whether this element `display` value is `none`. pub fn is_display_none(&self) -> bool { - self.primary().get_box().clone_display() == Display::None + self.primary().get_box().clone_display().is_none() } #[cfg(feature = "gecko")] diff --git a/servo/components/style/values/specified/box.rs b/servo/components/style/values/specified/box.rs index 54bb13856954..483ac9907e0c 100644 --- a/servo/components/style/values/specified/box.rs +++ b/servo/components/style/values/specified/box.rs @@ -216,6 +216,12 @@ impl Display { other => other, } } + + /// Returns true if the value is `None` + #[inline] + pub fn is_none(&self) -> bool { + *self == Display::None + } } /// A specified value for the `vertical-align` property. diff --git a/servo/etc/ci/buildbot_steps.yml b/servo/etc/ci/buildbot_steps.yml index 28e0b969bf81..37380ca5349a 100644 --- a/servo/etc/ci/buildbot_steps.yml +++ b/servo/etc/ci/buildbot_steps.yml @@ -1,4 +1,5 @@ env: + RUST_BACKTRACE: '1' RUSTFLAGS: -Dwarnings CARGO_INCREMENTAL: "0" SCCACHE_IDLE_TIMEOUT: "600" @@ -96,40 +97,52 @@ mac-rel-intermittent: - ./etc/ci/check_intermittents.sh --log-raw intermittents.log linux-dev: - - ./mach clean-nightlies --keep 3 --force - - ./mach clean-cargo-cache --keep 3 --force - - ./mach test-tidy --no-progress --all - - ./mach test-tidy --no-progress --self-test - - ./mach build --dev - - ./mach test-unit - - ./mach package --dev - - ./mach build-cef - - ./mach build --dev --no-default-features --features default-except-unstable - - ./mach build-geckolib - - ./mach test-stylo - - bash ./etc/ci/lockfile_changed.sh - - bash ./etc/ci/manifest_changed.sh - - bash ./etc/ci/check_no_panic.sh + env: + CCACHE: sccache + RUSTC_WRAPPER: sccache + commands: + - ./mach clean-nightlies --keep 3 --force + - ./mach clean-cargo-cache --keep 3 --force + - ./mach test-tidy --no-progress --all + - ./mach test-tidy --no-progress --self-test + - ./mach build --dev + - ./mach test-unit + - ./mach package --dev + - ./mach build-cef + - ./mach build --dev --no-default-features --features default-except-unstable + - ./mach build-geckolib + - ./mach test-stylo + - bash ./etc/ci/lockfile_changed.sh + - bash ./etc/ci/manifest_changed.sh + - bash ./etc/ci/check_no_panic.sh linux-rel-wpt: - - ./mach clean-nightlies --keep 3 --force - - ./mach clean-cargo-cache --keep 3 --force - - ./mach build --release --with-debug-assertions - - ./mach test-wpt-failure - - ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 1 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed - - ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default - - ./mach test-wpt --release --binary-arg=--multiprocess --processes 24 --log-raw test-wpt-mp.log --log-errorsummary wpt-mp-errorsummary.log eventsource + env: + CCACHE: sccache + RUSTC_WRAPPER: sccache + commands: + - ./mach clean-nightlies --keep 3 --force + - ./mach clean-cargo-cache --keep 3 --force + - ./mach build --release --with-debug-assertions + - ./mach test-wpt-failure + - ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 1 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed + - ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default + - ./mach test-wpt --release --binary-arg=--multiprocess --processes 24 --log-raw test-wpt-mp.log --log-errorsummary wpt-mp-errorsummary.log eventsource linux-rel-css: - - ./mach clean-nightlies --keep 3 --force - - ./mach clean-cargo-cache --keep 3 --force - - ./mach build --release --with-debug-assertions - - ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 2 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed - - ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default - - ./mach build-geckolib --release - - ./mach test-stylo --release - - bash ./etc/ci/lockfile_changed.sh - - bash ./etc/ci/manifest_changed.sh + env: + CCACHE: sccache + RUSTC_WRAPPER: sccache + commands: + - ./mach clean-nightlies --keep 3 --force + - ./mach clean-cargo-cache --keep 3 --force + - ./mach build --release --with-debug-assertions + - ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 2 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed + - ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default + - ./mach build-geckolib --release + - ./mach test-stylo --release + - bash ./etc/ci/lockfile_changed.sh + - bash ./etc/ci/manifest_changed.sh linux-nightly: - ./mach clean-nightlies --keep 3 --force diff --git a/taskcluster/scripts/misc/build-gcc-mingw32.sh b/taskcluster/scripts/misc/build-gcc-mingw32.sh index 76221aca51cd..531cc51eda91 100755 --- a/taskcluster/scripts/misc/build-gcc-mingw32.sh +++ b/taskcluster/scripts/misc/build-gcc-mingw32.sh @@ -18,7 +18,7 @@ gcc_ext=xz binutils_version=2.27 binutils_ext=bz2 binutils_configure_flags="--target=i686-w64-mingw32" -mingw_version=a39e1aa184206c59982d39b263553a2c58104cef +mingw_version=36d7b92bbcec1e72d3ce24013b01f7acc34be3b0 # GPG keys used to sign GCC (collected from 5.1.0, 5.4.0, 6.4.0) $GPG --import $data_dir/33C235A34C46AA3FFB293709A328C3A2C3C45C06.key diff --git a/toolkit/components/places/SyncedBookmarksMirror.jsm b/toolkit/components/places/SyncedBookmarksMirror.jsm index 170f65381ea7..1376f928159f 100644 --- a/toolkit/components/places/SyncedBookmarksMirror.jsm +++ b/toolkit/components/places/SyncedBookmarksMirror.jsm @@ -334,14 +334,20 @@ class SyncedBookmarksMirror { // mirror, and we're holding the Sync lock at this point. MirrorLog.debug("Building remote tree from mirror"); let remoteTree = await this.fetchRemoteTree(remoteTimeSeconds); - MirrorLog.trace("Built remote tree from mirror", remoteTree); + if (MirrorLog.level <= Log.Level.Trace) { + MirrorLog.trace("Built remote tree from mirror\n" + + remoteTree.toASCIITreeString()); + } let observersToNotify = new BookmarkObserverRecorder(this.db); let changeRecords = await this.db.executeTransaction(async () => { MirrorLog.debug("Building local tree from Places"); let localTree = await this.fetchLocalTree(localTimeSeconds); - MirrorLog.trace("Built local tree from Places", localTree); + if (MirrorLog.level <= Log.Level.Trace) { + MirrorLog.trace("Built local tree from Places\n" + + localTree.toASCIITreeString()); + } MirrorLog.debug("Fetching content info for new mirror items"); let newRemoteContents = await this.fetchNewRemoteContents(); @@ -358,8 +364,11 @@ class SyncedBookmarksMirror { } if (MirrorLog.level <= Log.Level.Trace) { - let newTreeRoot = mergedRoot.toBookmarkNode(); - MirrorLog.trace("Built new merged tree", newTreeRoot); + MirrorLog.trace([ + "Built new merged tree", + mergedRoot.toASCIITreeString(), + ...merger.deletionsToStrings(), + ].join("\n")); } // The merged tree should know about all items mentioned in the local @@ -2562,6 +2571,46 @@ class BookmarkMergeState { value() { return this.type; } + + /** + * Returns a representation of the value ("V") and structure ("S") state + * for logging. "L" is "local", "R" is "remote", and "+" is "new". We use + * compact notation here to reduce noise in trace logs, which log the + * merge state of every node in the tree. + * + * @return {String} + */ + toString() { + return `(${this.valueToString()}; ${this.structureToString()})`; + } + + valueToString() { + switch (this.value()) { + case BookmarkMergeState.TYPE.LOCAL: + return "V: L"; + case BookmarkMergeState.TYPE.REMOTE: + return "V: R"; + } + return "V: ?"; + } + + structureToString() { + switch (this.structure()) { + case BookmarkMergeState.TYPE.LOCAL: + return "S: L"; + case BookmarkMergeState.TYPE.REMOTE: + return "S: R"; + case BookmarkMergeState.TYPE.NEW: + // We intentionally don't log the new structure node here, since + // the merger already does that. + return "S: +"; + } + return "S: ?"; + } + + toJSON() { + return this.toString(); + } } BookmarkMergeState.TYPE = { @@ -2684,6 +2733,61 @@ class BookmarkNode { } } } + + /** + * Generates a human-readable, ASCII art representation of the node and its + * descendants. This is useful for visualizing the tree structure in trace + * logs. + * + * @return {String} + */ + toASCIITreeString(prefix = "") { + if (!this.isFolder()) { + return prefix + "- " + this.toString(); + } + return prefix + "+ " + this.toString() + "\n" + this.children.map(childNode => + childNode.toASCIITreeString(`${prefix}| `) + ).join("\n"); + } + + /** + * Returns a representation of the node for logging. This should be compact, + * because the merger logs every local and remote node when trace logging is + * enabled. + * + * @return {String} + * A string in the form of "bookmarkAAAA (B; 1.234s; !)", where + * "B" is the kind, "1.234s" is the age, and "!" indicates that the + * node needs to be merged. + */ + toString() { + let info = `${this.kindToString()}; ${this.age.toFixed(3)}s`; + if (this.needsMerge) { + info += "; !"; + } + return `${this.guid} (${info})`; + } + + kindToString() { + switch (this.kind) { + case SyncedBookmarksMirror.KIND.BOOKMARK: + return "B"; + case SyncedBookmarksMirror.KIND.QUERY: + return "Q"; + case SyncedBookmarksMirror.KIND.FOLDER: + return "F"; + case SyncedBookmarksMirror.KIND.LIVEMARK: + return "L"; + case SyncedBookmarksMirror.KIND.SEPARATOR: + return "S"; + } + return "?"; + } + + // Used by `Log.jsm`. + toJSON() { + return this.toString(); + } } /** @@ -2768,9 +2872,16 @@ class BookmarkTree { } } - toJSON() { - let deleted = Array.from(this.deletedGuids); - return { root: this.root, deleted }; + /** + * Generates an ASCII art representation of the complete tree. Deleted GUIDs + * are prefixed with "~". + * + * @return {String} + */ + toASCIITreeString() { + return this.root.toASCIITreeString() + "\n" + Array.from(this.deletedGuids, + guid => `~${guid}` + ).join(", "); } } @@ -2876,6 +2987,38 @@ class MergedBookmarkNode { { guid: this.guid, valueState }); throw new TypeError("Can't take unknown value state"); } + + /** + * Generates an ASCII art representation of the merged node and its + * descendants. This is similar to the format generated by + * `BookmarkNode#toASCIITreeString`, but logs value and structure states for + * merged children. + * + * @return {String} + */ + toASCIITreeString(prefix = "") { + if (!this.mergedChildren.length) { + return prefix + "- " + this.toString(); + } + return prefix + "+ " + this.toString() + "\n" + this.mergedChildren.map( + mergedChildNode => mergedChildNode.toASCIITreeString(`${prefix}| `) + ).join("\n"); + } + + /** + * Returns a representation of the merged node for logging. + * + * @return {String} + * A string in the form of "bookmarkAAAA (V: R, S: R)", where + * "V" is the value state and "R" is the structure state. + */ + toString() { + return `${this.guid} ${this.mergeState.toString()}`; + } + + toJSON() { + return this.toString(); + } } // Caches bookmark nodes containing the decided value and structure. @@ -3668,6 +3811,25 @@ class BookmarkMerger { } return newLocalNode; } + + /** + * Returns an array of local ("L: ~bookmarkAAAA, ~bookmarkBBBB") and remote + * ("R: ~bookmarkCCCC, ~bookmarkDDDD") deletions for logging. + * + * @return {String[]} + */ + deletionsToStrings() { + let infos = []; + if (this.deleteLocally.size) { + infos.push("L: " + Array.from(this.deleteLocally, + guid => `~${guid}`).join(", ")); + } + if (this.deleteRemotely.size) { + infos.push("R: " + Array.from(this.deleteRemotely, + guid => `~${guid}`).join(", ")); + } + return infos; + } } /**