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

This commit is contained in:
shindli 2018-01-29 12:00:28 +02:00
Родитель 2ced31c722 0d9139ed76
Коммит 006d377043
33 изменённых файлов: 725 добавлений и 528 удалений

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

@ -16,10 +16,10 @@
} }
#light-theme-focus:target ~ #light-theme { #light-theme-focus:target ~ #light-theme {
fill: #4A90E2; fill: #0060DF;
} }
#dark-theme-focus:target ~ #dark-theme { #dark-theme-focus:target ~ #dark-theme {
fill: #00FF7F; fill: #75BFFF;
} }
/* Unfocused states */ /* Unfocused states */

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

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

@ -846,6 +846,16 @@ a.learn-more-link.webconsole-learn-more-link {
.webconsole-filterbar-primary .devtools-plaininput { .webconsole-filterbar-primary .devtools-plaininput {
flex: 1 1 100%; 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 { .webconsole-filterbar-primary .filter-checkbox {

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

@ -841,10 +841,11 @@ class CssGridHighlighter extends AutoRefreshHighlighter {
let row = fragment.rows.tracks[rowNumber - 1]; let row = fragment.rows.tracks[rowNumber - 1];
let column = fragment.cols.tracks[columnNumber - 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) || if (fontSize > (column.breadth * displayPixelRatio) ||
fontSize > (row.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; this.ctx.font = fontSize + "px " + GRID_FONT_FAMILY;
} }

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

@ -804,21 +804,12 @@ Animation::CancelNoUpdate()
mHoldTime.SetNull(); mHoldTime.SetNull();
mStartTime.SetNull(); mStartTime.SetNull();
UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
if (mTimeline) { if (mTimeline) {
mTimeline->RemoveAnimation(this); mTimeline->RemoveAnimation(this);
} }
MaybeQueueCancelEvent(activeTime); 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 bool

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

@ -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<AnimationEventInfo>&& 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

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

@ -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 <algorithm> // For <std::stable_sort>
#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<dom::Element> mElement;
RefPtr<dom::Animation> mAnimation;
TimeStamp mTimeStamp;
typedef Variant<InternalTransitionEvent, InternalAnimationEvent> 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<InternalAnimationEvent>();
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<InternalTransitionEvent>();
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<InternalTransitionEvent>()) {
return &mEvent.as<InternalTransitionEvent>();
}
if (mEvent.is<InternalAnimationEvent>()) {
return &mEvent.as<InternalAnimationEvent>();
}
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<AnimationEventInfo>&& 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<RefPtr<dom::Animation>> 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<AnimationEventInfo> EventArray;
EventArray mPendingEvents;
bool mIsSorted;
bool mIsObserving;
};
} // namespace mozilla
#endif // mozilla_AnimationEventDispatcher_h

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

@ -24,6 +24,7 @@ EXPORTS.mozilla.dom += [
EXPORTS.mozilla += [ EXPORTS.mozilla += [
'AnimationComparator.h', 'AnimationComparator.h',
'AnimationEventDispatcher.h',
'AnimationPerformanceWarning.h', 'AnimationPerformanceWarning.h',
'AnimationPropertySegment.h', 'AnimationPropertySegment.h',
'AnimationTarget.h', 'AnimationTarget.h',
@ -46,6 +47,7 @@ UNIFIED_SOURCES += [
'AnimationEffectReadOnly.cpp', 'AnimationEffectReadOnly.cpp',
'AnimationEffectTiming.cpp', 'AnimationEffectTiming.cpp',
'AnimationEffectTimingReadOnly.cpp', 'AnimationEffectTimingReadOnly.cpp',
'AnimationEventDispatcher.cpp',
'AnimationPerformanceWarning.cpp', 'AnimationPerformanceWarning.cpp',
'AnimationTimeline.cpp', 'AnimationTimeline.cpp',
'AnimationUtils.cpp', 'AnimationUtils.cpp',

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

@ -1362,8 +1362,7 @@ PresShell::Destroy()
} }
if (mPresContext) { if (mPresContext) {
mPresContext->AnimationManager()->ClearEventQueue(); rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
mPresContext->TransitionManager()->ClearEventQueue();
} }
// Revoke any pending events. We need to do this and cancel pending reflows // Revoke any pending events. We need to do this and cancel pending reflows

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

@ -48,6 +48,7 @@
#include "gfxPlatform.h" #include "gfxPlatform.h"
#include "nsCSSRules.h" #include "nsCSSRules.h"
#include "nsFontFaceLoader.h" #include "nsFontFaceLoader.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/EffectCompositor.h" #include "mozilla/EffectCompositor.h"
#include "mozilla/EventListenerManager.h" #include "mozilla/EventListenerManager.h"
#include "prenv.h" #include "prenv.h"
@ -442,8 +443,7 @@ nsPresContext::LastRelease()
NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext) NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationManager); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationEventDispatcher);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTransitionManager);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
// NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEffectCompositor); 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_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationManager); NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationEventDispatcher);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTransitionManager);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument); NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering? NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor); NS_IMPL_CYCLE_COLLECTION_UNLINK(mEffectCompositor);
@ -888,6 +887,7 @@ nsPresContext::Init(nsDeviceContext* aDeviceContext)
mEventManager = new mozilla::EventStateManager(); mEventManager = new mozilla::EventStateManager();
mAnimationEventDispatcher = new mozilla::AnimationEventDispatcher(this);
mEffectCompositor = new mozilla::EffectCompositor(this); mEffectCompositor = new mozilla::EffectCompositor(this);
mTransitionManager = new nsTransitionManager(this); mTransitionManager = new nsTransitionManager(this);
mAnimationManager = new nsAnimationManager(this); mAnimationManager = new nsAnimationManager(this);
@ -1066,6 +1066,10 @@ nsPresContext::DetachShell()
mShell = nullptr; mShell = nullptr;
if (mAnimationEventDispatcher) {
mAnimationEventDispatcher->Disconnect();
mAnimationEventDispatcher = nullptr;
}
if (mEffectCompositor) { if (mEffectCompositor) {
mEffectCompositor->Disconnect(); mEffectCompositor->Disconnect();
mEffectCompositor = nullptr; mEffectCompositor = nullptr;

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

@ -73,6 +73,7 @@ class nsDeviceContext;
class gfxMissingFontRecorder; class gfxMissingFontRecorder;
namespace mozilla { namespace mozilla {
class AnimationEventDispatcher;
class EffectCompositor; class EffectCompositor;
class Encoding; class Encoding;
class EventStateManager; class EventStateManager;
@ -236,6 +237,11 @@ public:
nsCSSFrameConstructor* FrameConstructor() nsCSSFrameConstructor* FrameConstructor()
{ return PresShell()->FrameConstructor(); } { return PresShell()->FrameConstructor(); }
mozilla::AnimationEventDispatcher* AnimationEventDispatcher()
{
return mAnimationEventDispatcher;
}
mozilla::EffectCompositor* EffectCompositor() { return mEffectCompositor; } mozilla::EffectCompositor* EffectCompositor() { return mEffectCompositor; }
nsTransitionManager* TransitionManager() { return mTransitionManager; } nsTransitionManager* TransitionManager() { return mTransitionManager; }
nsAnimationManager* AnimationManager() { return mAnimationManager; } nsAnimationManager* AnimationManager() { return mAnimationManager; }
@ -1296,6 +1302,7 @@ protected:
// from gfx back to layout. // from gfx back to layout.
RefPtr<mozilla::EventStateManager> mEventManager; RefPtr<mozilla::EventStateManager> mEventManager;
RefPtr<nsRefreshDriver> mRefreshDriver; RefPtr<nsRefreshDriver> mRefreshDriver;
RefPtr<mozilla::AnimationEventDispatcher> mAnimationEventDispatcher;
RefPtr<mozilla::EffectCompositor> mEffectCompositor; RefPtr<mozilla::EffectCompositor> mEffectCompositor;
RefPtr<nsTransitionManager> mTransitionManager; RefPtr<nsTransitionManager> mTransitionManager;
RefPtr<nsAnimationManager> mAnimationManager; RefPtr<nsAnimationManager> mAnimationManager;

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

@ -25,6 +25,7 @@
#include "WinUtils.h" #include "WinUtils.h"
#endif #endif
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/ArrayUtils.h" #include "mozilla/ArrayUtils.h"
#include "mozilla/AutoRestore.h" #include "mozilla/AutoRestore.h"
#include "mozilla/IntegerRange.h" #include "mozilla/IntegerRange.h"
@ -1428,6 +1429,7 @@ nsRefreshDriver::ObserverCount() const
// changes can trigger transitions which fire events when they complete, and // changes can trigger transitions which fire events when they complete, and
// layout changes can affect media queries on child documents, triggering // layout changes can affect media queries on child documents, triggering
// style changes, etc. // style changes, etc.
sum += mAnimationEventFlushObservers.Length();
sum += mResizeEventFlushObservers.Length(); sum += mResizeEventFlushObservers.Length();
sum += mStyleFlushObservers.Length(); sum += mStyleFlushObservers.Length();
sum += mLayoutFlushObservers.Length(); sum += mLayoutFlushObservers.Length();
@ -1439,14 +1441,36 @@ nsRefreshDriver::ObserverCount() const
return sum; return sum;
} }
uint32_t bool
nsRefreshDriver::ImageRequestCount() const nsRefreshDriver::HasObservers() const
{ {
uint32_t count = 0; for (uint32_t i = 0; i < ArrayLength(mObservers); ++i) {
for (auto iter = mStartTable.ConstIter(); !iter.Done(); iter.Next()) { if (!mObservers[i].IsEmpty()) {
count += iter.UserData()->mEntries.Count(); 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& nsRefreshDriver::ObserverArray&
@ -1578,15 +1602,6 @@ nsRefreshDriver::DispatchPendingEvents()
} }
} }
static bool
CollectDocuments(nsIDocument* aDocument, void* aDocArray)
{
static_cast<AutoTArray<nsCOMPtr<nsIDocument>, 32>*>(aDocArray)->
AppendElement(aDocument);
aDocument->EnumerateSubDocuments(CollectDocuments, aDocArray);
return true;
}
void void
nsRefreshDriver::UpdateIntersectionObservations() nsRefreshDriver::UpdateIntersectionObservations()
{ {
@ -1616,31 +1631,19 @@ nsRefreshDriver::DispatchAnimationEvents()
return; return;
} }
AutoTArray<nsCOMPtr<nsIDocument>, 32> documents; // Hold all AnimationEventDispatcher in mAnimationEventFlushObservers as
CollectDocuments(mPresContext->Document(), &documents); // a RefPtr<> array since each AnimationEventDispatcher might be destroyed
// during processing the previous dispatcher.
size_t len = mAnimationEventFlushObservers.Length();
AutoTArray<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
RefPtr<AnimationEventDispatcher>* 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) { for (auto& dispatcher : dispatchers) {
nsIDocument* doc = documents[i]; dispatcher->DispatchEvents();
nsIPresShell* shell = doc->GetShell();
if (!shell) {
continue;
}
RefPtr<nsPresContext> 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();
}
} }
} }
@ -1818,7 +1821,8 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
mWarningThreshold = 1; mWarningThreshold = 1;
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell(); nsCOMPtr<nsIPresShell> 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. // Things are being destroyed, or we no longer have any observers.
// We don't want to stop the timer when observers are initially // We don't want to stop the timer when observers are initially
// removed, because sometimes observers can be added and removed // removed, because sometimes observers can be added and removed
@ -2140,7 +2144,7 @@ nsRefreshDriver::Thaw()
} }
if (mFreezeCount == 0) { if (mFreezeCount == 0) {
if (ObserverCount() || ImageRequestCount()) { if (HasObservers() || HasImageRequests()) {
// FIXME: This isn't quite right, since our EnsureTimerStarted call // FIXME: This isn't quite right, since our EnsureTimerStarted call
// updates our mMostRecentRefresh, but the DoRefresh call won't run // updates our mMostRecentRefresh, but the DoRefresh call won't run
// and notify our observers until we get back to the event loop. // and notify our observers until we get back to the event loop.
@ -2165,7 +2169,7 @@ nsRefreshDriver::FinishedWaitingForTransaction()
mWaitingForTransaction = false; mWaitingForTransaction = false;
if (mSkippedPaints && if (mSkippedPaints &&
!IsInRefresh() && !IsInRefresh() &&
(ObserverCount() || ImageRequestCount())) { (HasObservers() || HasImageRequests())) {
AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick"); AUTO_PROFILER_TRACING("Paint", "RefreshDriverTick");
DoRefresh(); DoRefresh();
} }
@ -2398,6 +2402,14 @@ nsRefreshDriver::CancelPendingEvents(nsIDocument* aDocument)
} }
} }
void
nsRefreshDriver::CancelPendingAnimationEvents(AnimationEventDispatcher* aDispatcher)
{
MOZ_ASSERT(aDispatcher);
aDispatcher->ClearEventQueue();
mAnimationEventFlushObservers.RemoveElement(aDispatcher);
}
/* static */ TimeStamp /* static */ TimeStamp
nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault) nsRefreshDriver::GetIdleDeadlineHint(TimeStamp aDefault)
{ {

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

@ -22,6 +22,7 @@
#include "nsTObserverArray.h" #include "nsTObserverArray.h"
#include "nsClassHashtable.h" #include "nsClassHashtable.h"
#include "nsHashKeys.h" #include "nsHashKeys.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "mozilla/layers/TransactionIdAllocator.h" #include "mozilla/layers/TransactionIdAllocator.h"
@ -243,6 +244,24 @@ public:
*/ */
void CancelPendingEvents(nsIDocument* aDocument); 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 * Schedule a frame visibility update "soon", subject to the heuristics and
* throttling we apply to visibility updates. * throttling we apply to visibility updates.
@ -397,8 +416,9 @@ private:
void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone); void EnsureTimerStarted(EnsureTimerStartedFlags aFlags = eNone);
void StopTimer(); void StopTimer();
bool HasObservers() const;
uint32_t ObserverCount() const; uint32_t ObserverCount() const;
uint32_t ImageRequestCount() const; bool HasImageRequests() const;
ObserverArray& ArrayFor(mozilla::FlushType aFlushType); ObserverArray& ArrayFor(mozilla::FlushType aFlushType);
// Trigger a refresh immediately, if haven't been disconnected or frozen. // Trigger a refresh immediately, if haven't been disconnected or frozen.
void DoRefresh(); void DoRefresh();
@ -495,6 +515,8 @@ private:
nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs; nsTArray<nsIDocument*> mThrottledFrameRequestCallbackDocs;
nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers; nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
nsTArray<PendingEvent> mPendingEvents; nsTArray<PendingEvent> mPendingEvents;
AutoTArray<mozilla::AnimationEventDispatcher*, 16>
mAnimationEventFlushObservers;
void BeginRefreshingImages(RequestTable& aEntries, void BeginRefreshingImages(RequestTable& aEntries,
mozilla::TimeStamp aDesired); mozilla::TimeStamp aDesired);

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

@ -116,23 +116,6 @@ AnimationCollection<AnimationType>::GetOrCreateAnimationCollection(
return collection; return collection;
} }
template <class AnimationType>
/* static */ nsString
AnimationCollection<AnimationType>::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 <class AnimationType> template <class AnimationType>
void void
AnimationCollection<AnimationType>::UpdateCheckGeneration( AnimationCollection<AnimationType>::UpdateCheckGeneration(

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

@ -88,8 +88,6 @@ public:
CSSPseudoElementType aPseudoType, CSSPseudoElementType aPseudoType,
bool* aCreatedCollection); bool* aCreatedCollection);
static nsString PseudoTypeAsString(CSSPseudoElementType aPseudoType);
dom::Element *mElement; dom::Element *mElement;
// the atom we use in mElement's prop table (must be a static atom, // the atom we use in mElement's prop table (must be a static atom,

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

@ -7,33 +7,25 @@
#ifndef mozilla_css_AnimationCommon_h #ifndef mozilla_css_AnimationCommon_h
#define mozilla_css_AnimationCommon_h #define mozilla_css_AnimationCommon_h
#include <algorithm> // For <std::stable_sort>
#include "mozilla/AnimationCollection.h" #include "mozilla/AnimationCollection.h"
#include "mozilla/AnimationComparator.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Animation.h" #include "mozilla/dom/Animation.h"
#include "mozilla/AnimationTarget.h"
#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF #include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF
#include "mozilla/Assertions.h" #include "mozilla/Assertions.h"
#include "mozilla/TimingParams.h" #include "mozilla/TimingParams.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsCSSPseudoElements.h"
#include "nsCycleCollectionParticipant.h"
class nsIFrame; class nsIFrame;
class nsPresContext; class nsPresContext;
namespace mozilla { namespace mozilla {
enum class CSSPseudoElementType : uint8_t; enum class CSSPseudoElementType : uint8_t;
template <class EventInfo> class DelayedEventDispatcher;
namespace dom { namespace dom {
class Element; class Element;
} }
template <class AnimationType, class AnimationEventType> template <class AnimationType>
class CommonAnimationManager { class CommonAnimationManager {
public: public:
explicit CommonAnimationManager(nsPresContext *aPresContext) explicit CommonAnimationManager(nsPresContext *aPresContext)
@ -75,18 +67,6 @@ public:
collection->Destroy(); collection->Destroy();
} }
/**
* Add pending events.
*/
void QueueEvents(nsTArray<AnimationEventType>&& aEvents)
{
mEventDispatcher.QueueEvents(
mozilla::Forward<nsTArray<AnimationEventType>>(aEvents));
}
void SortEvents() { mEventDispatcher.SortEvents(); }
void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
protected: protected:
virtual ~CommonAnimationManager() virtual ~CommonAnimationManager()
{ {
@ -107,8 +87,6 @@ protected:
LinkedList<AnimationCollection<AnimationType>> mElementCollections; LinkedList<AnimationCollection<AnimationType>> mElementCollections;
nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect) nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
mozilla::DelayedEventDispatcher<AnimationEventType> mEventDispatcher;
}; };
/** /**
@ -179,122 +157,6 @@ private:
NonOwningAnimationTarget mTarget; NonOwningAnimationTarget mTarget;
}; };
template <class EventInfo>
class DelayedEventDispatcher
{
public:
DelayedEventDispatcher() : mIsSorted(true) { }
void QueueEvents(nsTArray<EventInfo>&& aEvents)
{
mPendingEvents.AppendElements(Forward<nsTArray<EventInfo>>(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<RefPtr<dom::Animation>> comparator;
return comparator.LessThan(a.mAnimation, b.mAnimation);
}
};
typedef nsTArray<EventInfo> EventArray;
EventArray mPendingEvents;
bool mIsSorted;
};
template <class EventInfo>
inline void
ImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>& aField)
{
aField.Unlink();
}
template <class EventInfo>
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
DelayedEventDispatcher<EventInfo>& aField,
const char* aName,
uint32_t aFlags = 0)
{
aField.Traverse(&aCallback, aName);
}
// Return the TransitionPhase or AnimationPhase to use when the animation // Return the TransitionPhase or AnimationPhase to use when the animation
// doesn't have a target effect. // doesn't have a target effect.
template <typename PhaseType> template <typename PhaseType>
@ -303,6 +165,12 @@ PhaseType GetAnimationPhaseWithoutEffect(const dom::Animation& aAnimation)
MOZ_ASSERT(!aAnimation.GetEffect(), MOZ_ASSERT(!aAnimation.GetEffect(),
"Should only be called when we do not have an effect"); "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<TimeDuration> currentTime = aAnimation.GetCurrentTime(); Nullable<TimeDuration> currentTime = aAnimation.GetCurrentTime();
if (currentTime.IsNull()) { if (currentTime.IsNull()) {
return PhaseType::Idle; return PhaseType::Idle;

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

@ -8,6 +8,7 @@
#include "nsTransitionManager.h" #include "nsTransitionManager.h"
#include "mozilla/dom/CSSAnimationBinding.h" #include "mozilla/dom/CSSAnimationBinding.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/AnimationTarget.h" #include "mozilla/AnimationTarget.h"
#include "mozilla/EffectCompositor.h" #include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h" #include "mozilla/EffectSet.h"
@ -242,9 +243,9 @@ CSSAnimation::QueueEvents(const StickyTimeDuration& aActiveTime)
if (aMessage == eAnimationCancel) { if (aMessage == eAnimationCancel) {
elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime); elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime);
} }
events.AppendElement(AnimationEventInfo(mOwningElement.Target(), events.AppendElement(AnimationEventInfo(mAnimationName,
mOwningElement.Target(),
aMessage, aMessage,
mAnimationName,
elapsedTime, elapsedTime,
aTimeStamp, aTimeStamp,
this)); this));
@ -299,7 +300,7 @@ CSSAnimation::QueueEvents(const StickyTimeDuration& aActiveTime)
mPreviousIteration = currentIteration; mPreviousIteration = currentIteration;
if (!events.IsEmpty()) { if (!events.IsEmpty()) {
presContext->AnimationManager()->QueueEvents(Move(events)); presContext->AnimationEventDispatcher()->QueueEvents(Move(events));
} }
} }
@ -317,11 +318,6 @@ CSSAnimation::UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag)
////////////////////////// nsAnimationManager //////////////////////////// ////////////////////////// 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 // Find the matching animation by |aName| in the old list
// of animations and remove the matched animation from the list. // of animations and remove the matched animation from the list.
static already_AddRefed<CSSAnimation> static already_AddRefed<CSSAnimation>

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

@ -14,6 +14,7 @@
#include "mozilla/Keyframe.h" #include "mozilla/Keyframe.h"
#include "mozilla/MemoryReporting.h" #include "mozilla/MemoryReporting.h"
#include "mozilla/TimeStamp.h" #include "mozilla/TimeStamp.h"
#include "nsISupportsImpl.h"
class nsIGlobalObject; class nsIGlobalObject;
class nsStyleContext; class nsStyleContext;
@ -34,43 +35,6 @@ class ServoStyleContext;
enum class CSSPseudoElementType : uint8_t; enum class CSSPseudoElementType : uint8_t;
struct NonOwningAnimationTarget; struct NonOwningAnimationTarget;
struct AnimationEventInfo {
RefPtr<dom::Element> mElement;
RefPtr<dom::Animation> 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<dom::CSSAnimation>::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 { namespace dom {
class CSSAnimation final : public Animation class CSSAnimation final : public Animation
@ -311,18 +275,15 @@ struct AnimationTypeTraits<dom::CSSAnimation>
} /* namespace mozilla */ } /* namespace mozilla */
class nsAnimationManager final class nsAnimationManager final
: public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation, : public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>
mozilla::AnimationEventInfo>
{ {
public: public:
explicit nsAnimationManager(nsPresContext *aPresContext) explicit nsAnimationManager(nsPresContext *aPresContext)
: mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation, : mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>(aPresContext)
mozilla::AnimationEventInfo>(aPresContext)
{ {
} }
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager) NS_INLINE_DECL_REFCOUNTING(nsAnimationManager)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager)
typedef mozilla::AnimationCollection<mozilla::dom::CSSAnimation> typedef mozilla::AnimationCollection<mozilla::dom::CSSAnimation>
CSSAnimationCollection; CSSAnimationCollection;
@ -349,19 +310,6 @@ public:
mozilla::CSSPseudoElementType aPseudoType, mozilla::CSSPseudoElementType aPseudoType,
const mozilla::ServoStyleContext* aComputedValues); 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<nsAnimationManager> kungFuDeathGrip(this);
mEventDispatcher.DispatchEvents(mPresContext);
}
// Utility function to walk through |aIter| to find the Keyframe with // Utility function to walk through |aIter| to find the Keyframe with
// matching offset and timing function but stopping as soon as the offset // matching offset and timing function but stopping as soon as the offset
// differs from |aOffset| (i.e. it assumes a sorted iterator). // differs from |aOffset| (i.e. it assumes a sorted iterator).

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

@ -157,3 +157,19 @@ nsCSSPseudoElements::PseudoElementSupportsUserActionState(const Type aType)
return PseudoElementHasFlags(aType, return PseudoElementHasFlags(aType,
CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE); 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();
}
}

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

@ -141,6 +141,8 @@ public:
(aEnabledState & EnabledState::eInUASheets); (aEnabledState & EnabledState::eInUASheets);
} }
static nsString PseudoTypeAsString(Type aPseudoType);
private: private:
// Does the given pseudo-element have all of the flags given? // Does the given pseudo-element have all of the flags given?

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

@ -19,6 +19,7 @@
#include "nsRuleProcessorData.h" #include "nsRuleProcessorData.h"
#include "nsRuleWalker.h" #include "nsRuleWalker.h"
#include "nsCSSPropertyIDSet.h" #include "nsCSSPropertyIDSet.h"
#include "mozilla/AnimationEventDispatcher.h"
#include "mozilla/EffectCompositor.h" #include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h" #include "mozilla/EffectSet.h"
#include "mozilla/EventDispatcher.h" #include "mozilla/EventDispatcher.h"
@ -249,7 +250,7 @@ CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime)
currentPhase = TransitionPhase::Pending; currentPhase = TransitionPhase::Pending;
} }
AutoTArray<TransitionEventInfo, 3> events; AutoTArray<AnimationEventInfo, 3> events;
auto appendTransitionEvent = [&](EventMessage aMessage, auto appendTransitionEvent = [&](EventMessage aMessage,
const StickyTimeDuration& aElapsedTime, const StickyTimeDuration& aElapsedTime,
@ -258,12 +259,12 @@ CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime)
if (aMessage == eTransitionCancel) { if (aMessage == eTransitionCancel) {
elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime); elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime);
} }
events.AppendElement(TransitionEventInfo(mOwningElement.Target(), events.AppendElement(AnimationEventInfo(TransitionProperty(),
aMessage, mOwningElement.Target(),
TransitionProperty(), aMessage,
elapsedTime, elapsedTime,
aTimeStamp, aTimeStamp,
this)); this));
}; };
// Handle cancel events first // Handle cancel events first
@ -336,7 +337,7 @@ CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime)
mPreviousTransitionPhase = currentPhase; mPreviousTransitionPhase = currentPhase;
if (!events.IsEmpty()) { if (!events.IsEmpty()) {
presContext->TransitionManager()->QueueEvents(Move(events)); presContext->AnimationEventDispatcher()->QueueEvents(Move(events));
} }
} }
@ -422,11 +423,6 @@ CSSTransition::SetEffectFromStyle(dom::AnimationEffectReadOnly* aEffect)
////////////////////////// nsTransitionManager //////////////////////////// ////////////////////////// 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 static inline bool
ExtractNonDiscreteComputedValue(nsCSSPropertyID aProperty, ExtractNonDiscreteComputedValue(nsCSSPropertyID aProperty,
GeckoStyleContext* aStyleContext, GeckoStyleContext* aStyleContext,

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

@ -10,13 +10,11 @@
#define nsTransitionManager_h_ #define nsTransitionManager_h_
#include "mozilla/ComputedTiming.h" #include "mozilla/ComputedTiming.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel #include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/Animation.h" #include "mozilla/dom/Animation.h"
#include "mozilla/dom/KeyframeEffectReadOnly.h" #include "mozilla/dom/KeyframeEffectReadOnly.h"
#include "AnimationCommon.h" #include "AnimationCommon.h"
#include "nsCSSProps.h" #include "nsISupportsImpl.h"
class nsIGlobalObject; class nsIGlobalObject;
class nsStyleContext; class nsStyleContext;
@ -301,60 +299,19 @@ struct AnimationTypeTraits<dom::CSSTransition>
} }
}; };
struct TransitionEventInfo {
RefPtr<dom::Element> mElement;
RefPtr<dom::Animation> 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<dom::CSSTransition>::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 } // namespace mozilla
class nsTransitionManager final class nsTransitionManager final
: public mozilla::CommonAnimationManager<mozilla::dom::CSSTransition, : public mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>
mozilla::TransitionEventInfo>
{ {
public: public:
explicit nsTransitionManager(nsPresContext *aPresContext) explicit nsTransitionManager(nsPresContext *aPresContext)
: mozilla::CommonAnimationManager<mozilla::dom::CSSTransition, : mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>(aPresContext)
mozilla::TransitionEventInfo>(aPresContext)
, mInAnimationOnlyStyleUpdate(false) , mInAnimationOnlyStyleUpdate(false)
{ {
} }
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsTransitionManager) NS_INLINE_DECL_REFCOUNTING(nsTransitionManager)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsTransitionManager)
typedef mozilla::AnimationCollection<mozilla::dom::CSSTransition> typedef mozilla::AnimationCollection<mozilla::dom::CSSTransition>
CSSTransitionCollection; CSSTransitionCollection;
@ -406,12 +363,6 @@ public:
return mInAnimationOnlyStyleUpdate; return mInAnimationOnlyStyleUpdate;
} }
void DispatchEvents()
{
RefPtr<nsTransitionManager> kungFuDeathGrip(this);
mEventDispatcher.DispatchEvents(mPresContext);
}
protected: protected:
virtual ~nsTransitionManager() {} virtual ~nsTransitionManager() {}

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

@ -16,9 +16,9 @@ use ipc_channel::ipc::IpcSender;
use msg::constellation_msg::PipelineId; use msg::constellation_msg::PipelineId;
use opaque_node::OpaqueNodeMethods; use opaque_node::OpaqueNodeMethods;
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
use script_layout_interface::rpc::{MarginStyleResponse, NodeGeometryResponse}; use script_layout_interface::rpc::{NodeGeometryResponse, NodeScrollRootIdResponse};
use script_layout_interface::rpc::{NodeOverflowResponse, NodeScrollRootIdResponse}; use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse, StyleResponse};
use script_layout_interface::rpc::{OffsetParentResponse, ResolvedStyleResponse, TextIndexResponse}; use script_layout_interface::rpc::TextIndexResponse;
use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode}; use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutElement, ThreadSafeLayoutNode};
use script_traits::LayoutMsg as ConstellationMsg; use script_traits::LayoutMsg as ConstellationMsg;
use script_traits::UntrustedNodeAddress; use script_traits::UntrustedNodeAddress;
@ -59,9 +59,6 @@ pub struct LayoutThreadData {
/// A queued response for the scroll root id for a given node. /// A queued response for the scroll root id for a given node.
pub scroll_root_id_response: Option<ClipId>, pub scroll_root_id_response: Option<ClipId>,
/// 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. /// A queued response for the scroll {top, left, width, height} of a node in pixels.
pub scroll_area_response: Rect<i32>, pub scroll_area_response: Rect<i32>,
@ -71,8 +68,8 @@ pub struct LayoutThreadData {
/// A queued response for the offset parent/rect of a node. /// A queued response for the offset parent/rect of a node.
pub offset_parent_response: OffsetParentResponse, pub offset_parent_response: OffsetParentResponse,
/// A queued response for the offset parent/rect of a node. /// A queued response for the style of a node.
pub margin_style_response: MarginStyleResponse, pub style_response: StyleResponse,
/// Scroll offsets of scrolling regions. /// Scroll offsets of scrolling regions.
pub scroll_offsets: ScrollOffsetMap, 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 { fn node_scroll_area(&self) -> NodeGeometryResponse {
NodeGeometryResponse { NodeGeometryResponse {
client_rect: self.0.lock().unwrap().scroll_area_response client_rect: self.0.lock().unwrap().scroll_area_response
@ -157,10 +150,10 @@ impl LayoutRPC for LayoutRPCImpl {
rw_data.offset_parent_response.clone() rw_data.offset_parent_response.clone()
} }
fn margin_style(&self) -> MarginStyleResponse { fn style(&self) -> StyleResponse {
let &LayoutRPCImpl(ref rw_data) = self; let &LayoutRPCImpl(ref rw_data) = self;
let rw_data = rw_data.lock().unwrap(); let rw_data = rw_data.lock().unwrap();
rw_data.margin_style_response.clone() rw_data.style_response.clone()
} }
fn text_index(&self) -> TextIndexResponse { fn text_index(&self) -> TextIndexResponse {
@ -863,24 +856,10 @@ pub fn process_offset_parent_query<N: LayoutNode>(requested_node: N, layout_root
} }
} }
pub fn process_node_overflow_request<N: LayoutNode>(requested_node: N) -> NodeOverflowResponse { pub fn process_style_query<N: LayoutNode>(requested_node: N)
let layout_node = requested_node.to_threadsafe(); -> StyleResponse {
let style = &*layout_node.as_element().unwrap().resolved_style(); let element = requested_node.as_element().unwrap();
let style_box = style.get_box(); let data = element.borrow_data();
NodeOverflowResponse(Some(Point2D::new(style_box.overflow_x, style_box.overflow_y))) StyleResponse(data.map(|d| d.styles.primary().clone()))
}
pub fn process_margin_style_query<N: LayoutNode>(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,
}
} }

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

@ -75,9 +75,9 @@ use layout::incremental::{LayoutDamageComputation, RelayoutMode, SpecialRestyleD
use layout::layout_debug; use layout::layout_debug;
use layout::parallel; use layout::parallel;
use layout::query::{LayoutRPCImpl, LayoutThreadData, process_content_box_request, process_content_boxes_request}; 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_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::sequential;
use layout::traversal::{ComputeStackingRelativePositions, PreorderFlowTraversal, RecalcStyleAndConstructFlows}; use layout::traversal::{ComputeStackingRelativePositions, PreorderFlowTraversal, RecalcStyleAndConstructFlows};
use layout::wrapper::LayoutNodeLayoutData; use layout::wrapper::LayoutNodeLayoutData;
@ -94,7 +94,7 @@ use profile_traits::time::{self, TimerMetadata, profile};
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType}; use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, NodesFromPointQueryType, Reflow}; use script_layout_interface::message::{Msg, NewLayoutThreadInfo, NodesFromPointQueryType, Reflow};
use script_layout_interface::message::{ReflowComplete, ReflowGoal, ScriptReflow}; 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::rpc::TextIndexResponse;
use script_layout_interface::wrapper_traits::LayoutNode; use script_layout_interface::wrapper_traits::LayoutNode;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
@ -519,10 +519,9 @@ impl LayoutThread {
client_rect_response: Rect::zero(), client_rect_response: Rect::zero(),
scroll_root_id_response: None, scroll_root_id_response: None,
scroll_area_response: Rect::zero(), scroll_area_response: Rect::zero(),
overflow_response: NodeOverflowResponse(None),
resolved_style_response: String::new(), resolved_style_response: String::new(),
offset_parent_response: OffsetParentResponse::empty(), offset_parent_response: OffsetParentResponse::empty(),
margin_style_response: MarginStyleResponse::empty(), style_response: StyleResponse(None),
scroll_offsets: HashMap::new(), scroll_offsets: HashMap::new(),
text_index_response: TextIndexResponse(None), text_index_response: TextIndexResponse(None),
nodes_from_point_response: vec![], nodes_from_point_response: vec![],
@ -1092,9 +1091,6 @@ impl LayoutThread {
ReflowGoal::NodeScrollGeometryQuery(_) => { ReflowGoal::NodeScrollGeometryQuery(_) => {
rw_data.scroll_area_response = Rect::zero(); rw_data.scroll_area_response = Rect::zero();
}, },
ReflowGoal::NodeOverflowQuery(_) => {
rw_data.overflow_response = NodeOverflowResponse(None);
},
ReflowGoal::NodeScrollRootIdQuery(_) => { ReflowGoal::NodeScrollRootIdQuery(_) => {
rw_data.scroll_root_id_response = None; rw_data.scroll_root_id_response = None;
}, },
@ -1104,8 +1100,8 @@ impl LayoutThread {
ReflowGoal::OffsetParentQuery(_) => { ReflowGoal::OffsetParentQuery(_) => {
rw_data.offset_parent_response = OffsetParentResponse::empty(); rw_data.offset_parent_response = OffsetParentResponse::empty();
}, },
ReflowGoal::MarginStyleQuery(_) => { ReflowGoal::StyleQuery(_) => {
rw_data.margin_style_response = MarginStyleResponse::empty(); rw_data.style_response = StyleResponse(None);
}, },
ReflowGoal::TextIndexQuery(..) => { ReflowGoal::TextIndexQuery(..) => {
rw_data.text_index_response = TextIndexResponse(None); rw_data.text_index_response = TextIndexResponse(None);
@ -1379,10 +1375,6 @@ impl LayoutThread {
let node = unsafe { ServoLayoutNode::new(&node) }; let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.scroll_area_response = process_node_scroll_area_request(node, root_flow); 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) => { ReflowGoal::NodeScrollRootIdQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) }; let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.scroll_root_id_response = Some(process_node_scroll_root_id_request(self.id, 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) }; let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.offset_parent_response = process_offset_parent_query(node, root_flow); rw_data.offset_parent_response = process_offset_parent_query(node, root_flow);
}, },
ReflowGoal::MarginStyleQuery(node) => { ReflowGoal::StyleQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&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) => { ReflowGoal::NodesFromPointQuery(client_point, ref reflow_goal) => {
let mut flags = match reflow_goal { let mut flags = match reflow_goal {

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

@ -154,12 +154,13 @@ pub fn handle_get_layout(documents: &Documents,
} }
fn determine_auto_margins(window: &Window, node: &Node) -> AutoMargins { 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 { AutoMargins {
top: margin.top == margin_top::computed_value::T::Auto, top: margin.margin_top == margin_top::computed_value::T::Auto,
right: margin.right == margin_right::computed_value::T::Auto, right: margin.margin_right == margin_right::computed_value::T::Auto,
bottom: margin.bottom == margin_bottom::computed_value::T::Auto, bottom: margin.margin_bottom == margin_bottom::computed_value::T::Auto,
left: margin.left == margin_left::computed_value::T::Auto, left: margin.margin_left == margin_left::computed_value::T::Auto,
} }
} }

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

@ -107,7 +107,8 @@ use style::context::QuirksMode;
use style::dom_apis; use style::dom_apis;
use style::element_state::ElementState; use style::element_state::ElementState;
use style::invalidation::element::restyle_hints::RestyleHint; 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::{self, background_image, border_spacing, font_family, font_size};
use style::properties::longhands::{overflow_x, overflow_y}; use style::properties::longhands::{overflow_x, overflow_y};
use style::rule_tree::CascadeLevel; 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<Arc<ComputedValues>> {
window_from_node(self).style_query(
self.upcast::<Node>().to_trusted_node_address()
)
}
// https://drafts.csswg.org/cssom-view/#css-layout-box // 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 { fn has_css_layout_box(&self) -> bool {
self.upcast::<Node>().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 // https://drafts.csswg.org/cssom-view/#potentially-scrollable
fn potentially_scrollable(&self) -> bool { fn potentially_scrollable(&self) -> bool {
self.has_css_layout_box() && self.has_css_layout_box() && !self.has_any_visible_overflow()
!self.overflow_x_is_visible() &&
!self.overflow_y_is_visible()
} }
// https://drafts.csswg.org/cssom-view/#scrolling-box // https://drafts.csswg.org/cssom-view/#scrolling-box
fn has_scrolling_box(&self) -> bool { fn has_scrolling_box(&self) -> bool {
// TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet) // TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet)
// self.has_scrolling_mechanism() // self.has_scrolling_mechanism()
self.overflow_x_is_hidden() || self.has_any_hidden_overflow()
self.overflow_y_is_hidden()
} }
fn has_overflow(&self) -> bool { fn has_overflow(&self) -> bool {
@ -376,32 +379,29 @@ impl Element {
self.ScrollWidth() > self.ClientWidth() self.ScrollWidth() > self.ClientWidth()
} }
// used value of overflow-x is "visible" // TODO: Once #19183 is closed (overflow-x/y types moved out of mako), then we could implement
fn overflow_x_is_visible(&self) -> bool { // a more generic `fn has_some_overflow(&self, overflow: Overflow)` rather than have
let window = window_from_node(self); // these two `has_any_{visible,hidden}_overflow` methods which are very structurally
let overflow_pair = window.overflow_query(self.upcast::<Node>().to_trusted_node_address()); // similar.
overflow_pair.x == overflow_x::computed_value::T::Visible
/// 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" /// Computed value of overflow-x or overflow-y is "hidden"
fn overflow_y_is_visible(&self) -> bool { fn has_any_hidden_overflow(&self) -> bool {
let window = window_from_node(self); self.style().map_or(false, |s| {
let overflow_pair = window.overflow_query(self.upcast::<Node>().to_trusted_node_address()); let box_ = s.get_box();
overflow_pair.y == overflow_y::computed_value::T::Visible
}
// used value of overflow-x is "hidden" box_.clone_overflow_x() == overflow_x::computed_value::T::Hidden ||
fn overflow_x_is_hidden(&self) -> bool { box_.clone_overflow_y() == overflow_y::computed_value::T::Hidden
let window = window_from_node(self); })
let overflow_pair = window.overflow_query(self.upcast::<Node>().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::<Node>().to_trusted_node_address());
overflow_pair.y == overflow_y::computed_value::T::Hidden
} }
} }

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

@ -72,8 +72,7 @@ use script_layout_interface::{TrustedNodeAddress, PendingImageState};
use script_layout_interface::message::{Msg, Reflow, ReflowGoal, ScriptReflow}; use script_layout_interface::message::{Msg, Reflow, ReflowGoal, ScriptReflow};
use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC}; use script_layout_interface::rpc::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC};
use script_layout_interface::rpc::{MarginStyleResponse, NodeScrollRootIdResponse}; use script_layout_interface::rpc::{NodeScrollRootIdResponse, ResolvedStyleResponse, TextIndexResponse};
use script_layout_interface::rpc::{ResolvedStyleResponse, TextIndexResponse};
use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory, Runtime}; use script_runtime::{CommonScriptMsg, ScriptChan, ScriptPort, ScriptThreadEventCategory, Runtime};
use script_thread::{ImageCacheMsg, MainThreadScriptChan, MainThreadScriptMsg}; use script_thread::{ImageCacheMsg, MainThreadScriptChan, MainThreadScriptMsg};
use script_thread::{ScriptThread, SendableMainThreadScriptChan}; 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::{TimerSchedulerMsg, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult};
use selectors::attr::CaseSensitivity; use selectors::attr::CaseSensitivity;
use servo_arc;
use servo_config::opts; use servo_config::opts;
use servo_config::prefs::PREFS; use servo_config::prefs::PREFS;
use servo_geometry::{f32_rect_to_au_rect, MaxRect}; 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 std::sync::mpsc::TryRecvError::{Disconnected, Empty};
use style::media_queries; use style::media_queries;
use style::parser::ParserContext as CssParserContext; use style::parser::ParserContext as CssParserContext;
use style::properties::PropertyId; use style::properties::{ComputedValues, PropertyId};
use style::properties::longhands::overflow_x;
use style::selector_parser::PseudoElement; use style::selector_parser::PseudoElement;
use style::str::HTML_SPACE_CHARACTERS; use style::str::HTML_SPACE_CHARACTERS;
use style::stylesheets::CssRuleType; use style::stylesheets::CssRuleType;
@ -1403,16 +1402,6 @@ impl Window {
self.layout_rpc.node_scroll_area().client_rect self.layout_rpc.node_scroll_area().client_rect
} }
pub fn overflow_query(&self,
node: TrustedNodeAddress) -> Point2D<overflow_x::computed_value::T> {
// 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<f32> { pub fn scroll_offset_query(&self, node: &Node) -> Vector2D<f32> {
if let Some(scroll_offset) = self.scroll_offsets if let Some(scroll_offset) = self.scroll_offsets
.borrow() .borrow()
@ -1477,11 +1466,11 @@ impl Window {
(element, response.rect) (element, response.rect)
} }
pub fn margin_style_query(&self, node: TrustedNodeAddress) -> MarginStyleResponse { pub fn style_query(&self, node: TrustedNodeAddress) -> Option<servo_arc::Arc<ComputedValues>> {
if !self.reflow(ReflowGoal::MarginStyleQuery(node), ReflowReason::Query) { if !self.reflow(ReflowGoal::StyleQuery(node), ReflowReason::Query) {
return MarginStyleResponse::empty(); return None
} }
self.layout_rpc.margin_style() self.layout_rpc.style().0
} }
pub fn text_index_query( 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::ContentBoxesQuery(_n) => "\tContentBoxesQuery",
ReflowGoal::NodesFromPointQuery(..) => "\tNodesFromPointQuery", ReflowGoal::NodesFromPointQuery(..) => "\tNodesFromPointQuery",
ReflowGoal::NodeGeometryQuery(_n) => "\tNodeGeometryQuery", ReflowGoal::NodeGeometryQuery(_n) => "\tNodeGeometryQuery",
ReflowGoal::NodeOverflowQuery(_n) => "\tNodeOverFlowQuery",
ReflowGoal::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery", ReflowGoal::NodeScrollGeometryQuery(_n) => "\tNodeScrollGeometryQuery",
ReflowGoal::NodeScrollRootIdQuery(_n) => "\tNodeScrollRootIdQuery", ReflowGoal::NodeScrollRootIdQuery(_n) => "\tNodeScrollRootIdQuery",
ReflowGoal::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery", ReflowGoal::ResolvedStyleQuery(_, _, _) => "\tResolvedStyleQuery",
ReflowGoal::OffsetParentQuery(_n) => "\tOffsetParentQuery", ReflowGoal::OffsetParentQuery(_n) => "\tOffsetParentQuery",
ReflowGoal::MarginStyleQuery(_n) => "\tMarginStyleQuery", ReflowGoal::StyleQuery(_n) => "\tStyleQuery",
ReflowGoal::TextIndexQuery(..) => "\tTextIndexQuery", ReflowGoal::TextIndexQuery(..) => "\tTextIndexQuery",
ReflowGoal::TickAnimations => "\tTickAnimations", ReflowGoal::TickAnimations => "\tTickAnimations",
}); });

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

@ -113,13 +113,12 @@ pub enum ReflowGoal {
TickAnimations, TickAnimations,
ContentBoxQuery(TrustedNodeAddress), ContentBoxQuery(TrustedNodeAddress),
ContentBoxesQuery(TrustedNodeAddress), ContentBoxesQuery(TrustedNodeAddress),
NodeOverflowQuery(TrustedNodeAddress),
NodeScrollRootIdQuery(TrustedNodeAddress), NodeScrollRootIdQuery(TrustedNodeAddress),
NodeGeometryQuery(TrustedNodeAddress), NodeGeometryQuery(TrustedNodeAddress),
NodeScrollGeometryQuery(TrustedNodeAddress), NodeScrollGeometryQuery(TrustedNodeAddress),
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, PropertyId), ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, PropertyId),
OffsetParentQuery(TrustedNodeAddress), OffsetParentQuery(TrustedNodeAddress),
MarginStyleQuery(TrustedNodeAddress), StyleQuery(TrustedNodeAddress),
TextIndexQuery(TrustedNodeAddress, Point2D<f32>), TextIndexQuery(TrustedNodeAddress, Point2D<f32>),
NodesFromPointQuery(Point2D<f32>, NodesFromPointQueryType), NodesFromPointQuery(Point2D<f32>, NodesFromPointQueryType),
} }
@ -133,9 +132,9 @@ impl ReflowGoal {
ReflowGoal::TickAnimations | ReflowGoal::Full => true, ReflowGoal::TickAnimations | ReflowGoal::Full => true,
ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) | ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) |
ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) | ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) |
ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) |
ReflowGoal::ResolvedStyleQuery(..) | ReflowGoal::OffsetParentQuery(_) | 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. /// false if a layout_thread display list is sufficient.
pub fn needs_display(&self) -> bool { pub fn needs_display(&self) -> bool {
match *self { match *self {
ReflowGoal::MarginStyleQuery(_) | ReflowGoal::TextIndexQuery(..) | ReflowGoal::StyleQuery(_) | ReflowGoal::TextIndexQuery(..) |
ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) | ReflowGoal::ContentBoxQuery(_) | ReflowGoal::ContentBoxesQuery(_) |
ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) | ReflowGoal::NodeGeometryQuery(_) | ReflowGoal::NodeScrollGeometryQuery(_) |
ReflowGoal::NodeOverflowQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) | ReflowGoal::NodeScrollRootIdQuery(_) |
ReflowGoal::ResolvedStyleQuery(..) | ReflowGoal::ResolvedStyleQuery(..) |
ReflowGoal::OffsetParentQuery(_) => false, ReflowGoal::OffsetParentQuery(_) => false,
ReflowGoal::NodesFromPointQuery(..) | ReflowGoal::Full | ReflowGoal::NodesFromPointQuery(..) | ReflowGoal::Full |

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

@ -5,7 +5,9 @@
use app_units::Au; use app_units::Au;
use euclid::{Point2D, Rect}; use euclid::{Point2D, Rect};
use script_traits::UntrustedNodeAddress; 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; use webrender_api::ClipId;
/// Synchronous messages that script can send to layout. /// Synchronous messages that script can send to layout.
@ -23,8 +25,6 @@ pub trait LayoutRPC {
fn content_boxes(&self) -> ContentBoxesResponse; fn content_boxes(&self) -> ContentBoxesResponse;
/// Requests the geometry of this node. Used by APIs such as `clientTop`. /// Requests the geometry of this node. Used by APIs such as `clientTop`.
fn node_geometry(&self) -> NodeGeometryResponse; 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`. /// Requests the scroll geometry of this node. Used by APIs such as `scrollTop`.
fn node_scroll_area(&self) -> NodeGeometryResponse; fn node_scroll_area(&self) -> NodeGeometryResponse;
/// Requests the scroll root id of this node. Used by APIs such as `scrollTop` /// 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 /// Query layout for the resolved value of a given CSS property
fn resolved_style(&self) -> ResolvedStyleResponse; fn resolved_style(&self) -> ResolvedStyleResponse;
fn offset_parent(&self) -> OffsetParentResponse; fn offset_parent(&self) -> OffsetParentResponse;
/// Query layout for the resolve values of the margin properties for an element. /// Requests the styles for an element. Contains a `None` value if the element is in a `display:
fn margin_style(&self) -> MarginStyleResponse; /// none` subtree.
fn style(&self) -> StyleResponse;
fn text_index(&self) -> TextIndexResponse; fn text_index(&self) -> TextIndexResponse;
/// Requests the list of nodes from the given point. /// Requests the list of nodes from the given point.
fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress>; fn nodes_from_point_response(&self) -> Vec<UntrustedNodeAddress>;
@ -70,23 +71,7 @@ impl OffsetParentResponse {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct MarginStyleResponse { pub struct StyleResponse(pub Option<Arc<ComputedValues>>);
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,
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct TextIndexResponse(pub Option<usize>); pub struct TextIndexResponse(pub Option<usize>);

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

@ -11,7 +11,6 @@ use invalidation::element::restyle_hints::RestyleHint;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
use malloc_size_of::MallocSizeOfOps; use malloc_size_of::MallocSizeOfOps;
use properties::ComputedValues; use properties::ComputedValues;
use properties::longhands::display::computed_value::T as Display;
use rule_tree::StrongRuleNode; use rule_tree::StrongRuleNode;
use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage}; use selector_parser::{EAGER_PSEUDO_COUNT, PseudoElement, RestyleDamage};
use selectors::NthIndexCache; use selectors::NthIndexCache;
@ -169,7 +168,7 @@ impl ElementStyles {
/// Whether this element `display` value is `none`. /// Whether this element `display` value is `none`.
pub fn is_display_none(&self) -> bool { 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")] #[cfg(feature = "gecko")]

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

@ -216,6 +216,12 @@ impl Display {
other => other, 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. /// A specified value for the `vertical-align` property.

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

@ -1,4 +1,5 @@
env: env:
RUST_BACKTRACE: '1'
RUSTFLAGS: -Dwarnings RUSTFLAGS: -Dwarnings
CARGO_INCREMENTAL: "0" CARGO_INCREMENTAL: "0"
SCCACHE_IDLE_TIMEOUT: "600" SCCACHE_IDLE_TIMEOUT: "600"
@ -96,40 +97,52 @@ mac-rel-intermittent:
- ./etc/ci/check_intermittents.sh --log-raw intermittents.log - ./etc/ci/check_intermittents.sh --log-raw intermittents.log
linux-dev: linux-dev:
- ./mach clean-nightlies --keep 3 --force env:
- ./mach clean-cargo-cache --keep 3 --force CCACHE: sccache
- ./mach test-tidy --no-progress --all RUSTC_WRAPPER: sccache
- ./mach test-tidy --no-progress --self-test commands:
- ./mach build --dev - ./mach clean-nightlies --keep 3 --force
- ./mach test-unit - ./mach clean-cargo-cache --keep 3 --force
- ./mach package --dev - ./mach test-tidy --no-progress --all
- ./mach build-cef - ./mach test-tidy --no-progress --self-test
- ./mach build --dev --no-default-features --features default-except-unstable - ./mach build --dev
- ./mach build-geckolib - ./mach test-unit
- ./mach test-stylo - ./mach package --dev
- bash ./etc/ci/lockfile_changed.sh - ./mach build-cef
- bash ./etc/ci/manifest_changed.sh - ./mach build --dev --no-default-features --features default-except-unstable
- bash ./etc/ci/check_no_panic.sh - ./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: linux-rel-wpt:
- ./mach clean-nightlies --keep 3 --force env:
- ./mach clean-cargo-cache --keep 3 --force CCACHE: sccache
- ./mach build --release --with-debug-assertions RUSTC_WRAPPER: sccache
- ./mach test-wpt-failure commands:
- ./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 clean-nightlies --keep 3 --force
- ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default - ./mach clean-cargo-cache --keep 3 --force
- ./mach test-wpt --release --binary-arg=--multiprocess --processes 24 --log-raw test-wpt-mp.log --log-errorsummary wpt-mp-errorsummary.log eventsource - ./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: linux-rel-css:
- ./mach clean-nightlies --keep 3 --force env:
- ./mach clean-cargo-cache --keep 3 --force CCACHE: sccache
- ./mach build --release --with-debug-assertions RUSTC_WRAPPER: sccache
- ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 2 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed commands:
- ./mach filter-intermittents wpt-errorsummary.log --log-intermittents intermittents.log --log-filteredsummary filtered-wpt-errorsummary.log --tracker-api default --reporter-api default - ./mach clean-nightlies --keep 3 --force
- ./mach build-geckolib --release - ./mach clean-cargo-cache --keep 3 --force
- ./mach test-stylo --release - ./mach build --release --with-debug-assertions
- bash ./etc/ci/lockfile_changed.sh - ./mach test-wpt --release --processes 24 --total-chunks 2 --this-chunk 2 --log-raw test-wpt.log --log-errorsummary wpt-errorsummary.log --always-succeed
- bash ./etc/ci/manifest_changed.sh - ./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: linux-nightly:
- ./mach clean-nightlies --keep 3 --force - ./mach clean-nightlies --keep 3 --force

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

@ -18,7 +18,7 @@ gcc_ext=xz
binutils_version=2.27 binutils_version=2.27
binutils_ext=bz2 binutils_ext=bz2
binutils_configure_flags="--target=i686-w64-mingw32" 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 keys used to sign GCC (collected from 5.1.0, 5.4.0, 6.4.0)
$GPG --import $data_dir/33C235A34C46AA3FFB293709A328C3A2C3C45C06.key $GPG --import $data_dir/33C235A34C46AA3FFB293709A328C3A2C3C45C06.key

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

@ -334,14 +334,20 @@ class SyncedBookmarksMirror {
// mirror, and we're holding the Sync lock at this point. // mirror, and we're holding the Sync lock at this point.
MirrorLog.debug("Building remote tree from mirror"); MirrorLog.debug("Building remote tree from mirror");
let remoteTree = await this.fetchRemoteTree(remoteTimeSeconds); 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 observersToNotify = new BookmarkObserverRecorder(this.db);
let changeRecords = await this.db.executeTransaction(async () => { let changeRecords = await this.db.executeTransaction(async () => {
MirrorLog.debug("Building local tree from Places"); MirrorLog.debug("Building local tree from Places");
let localTree = await this.fetchLocalTree(localTimeSeconds); 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"); MirrorLog.debug("Fetching content info for new mirror items");
let newRemoteContents = await this.fetchNewRemoteContents(); let newRemoteContents = await this.fetchNewRemoteContents();
@ -358,8 +364,11 @@ class SyncedBookmarksMirror {
} }
if (MirrorLog.level <= Log.Level.Trace) { if (MirrorLog.level <= Log.Level.Trace) {
let newTreeRoot = mergedRoot.toBookmarkNode(); MirrorLog.trace([
MirrorLog.trace("Built new merged tree", newTreeRoot); "Built new merged tree",
mergedRoot.toASCIITreeString(),
...merger.deletionsToStrings(),
].join("\n"));
} }
// The merged tree should know about all items mentioned in the local // The merged tree should know about all items mentioned in the local
@ -2562,6 +2571,46 @@ class BookmarkMergeState {
value() { value() {
return this.type; 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 = { 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); * Generates an ASCII art representation of the complete tree. Deleted GUIDs
return { root: this.root, deleted }; * 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 }); { guid: this.guid, valueState });
throw new TypeError("Can't take unknown value state"); 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. // Caches bookmark nodes containing the decided value and structure.
@ -3668,6 +3811,25 @@ class BookmarkMerger {
} }
return newLocalNode; 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;
}
} }
/** /**