зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to inbound. a=merge CLOSED TREE
This commit is contained in:
Коммит
006d377043
|
@ -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 */
|
||||
|
|
До Ширина: | Высота: | Размер: 1.4 KiB После Ширина: | Высота: | Размер: 1.4 KiB |
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 += [
|
||||
'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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<mozilla::EventStateManager> mEventManager;
|
||||
RefPtr<nsRefreshDriver> mRefreshDriver;
|
||||
RefPtr<mozilla::AnimationEventDispatcher> mAnimationEventDispatcher;
|
||||
RefPtr<mozilla::EffectCompositor> mEffectCompositor;
|
||||
RefPtr<nsTransitionManager> mTransitionManager;
|
||||
RefPtr<nsAnimationManager> mAnimationManager;
|
||||
|
|
|
@ -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<AutoTArray<nsCOMPtr<nsIDocument>, 32>*>(aDocArray)->
|
||||
AppendElement(aDocument);
|
||||
aDocument->EnumerateSubDocuments(CollectDocuments, aDocArray);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nsRefreshDriver::UpdateIntersectionObservations()
|
||||
{
|
||||
|
@ -1616,31 +1631,19 @@ nsRefreshDriver::DispatchAnimationEvents()
|
|||
return;
|
||||
}
|
||||
|
||||
AutoTArray<nsCOMPtr<nsIDocument>, 32> documents;
|
||||
CollectDocuments(mPresContext->Document(), &documents);
|
||||
|
||||
for (uint32_t i = 0; i < documents.Length(); ++i) {
|
||||
nsIDocument* doc = documents[i];
|
||||
nsIPresShell* shell = doc->GetShell();
|
||||
if (!shell) {
|
||||
continue;
|
||||
// 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<RefPtr<AnimationEventDispatcher>, 16> dispatchers;
|
||||
RefPtr<AnimationEventDispatcher>* elems = dispatchers.AppendElements(len);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
elems[i] = mAnimationEventFlushObservers[i];
|
||||
}
|
||||
mAnimationEventFlushObservers.Clear();
|
||||
|
||||
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();
|
||||
}
|
||||
for (auto& dispatcher : dispatchers) {
|
||||
dispatcher->DispatchEvents();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1818,7 +1821,8 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
|||
mWarningThreshold = 1;
|
||||
|
||||
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.
|
||||
// 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)
|
||||
{
|
||||
|
|
|
@ -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<nsIDocument*> mThrottledFrameRequestCallbackDocs;
|
||||
nsTObserverArray<nsAPostRefreshObserver*> mPostRefreshObservers;
|
||||
nsTArray<PendingEvent> mPendingEvents;
|
||||
AutoTArray<mozilla::AnimationEventDispatcher*, 16>
|
||||
mAnimationEventFlushObservers;
|
||||
|
||||
void BeginRefreshingImages(RequestTable& aEntries,
|
||||
mozilla::TimeStamp aDesired);
|
||||
|
|
|
@ -116,23 +116,6 @@ AnimationCollection<AnimationType>::GetOrCreateAnimationCollection(
|
|||
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>
|
||||
void
|
||||
AnimationCollection<AnimationType>::UpdateCheckGeneration(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -7,33 +7,25 @@
|
|||
#ifndef mozilla_css_AnimationCommon_h
|
||||
#define mozilla_css_AnimationCommon_h
|
||||
|
||||
#include <algorithm> // For <std::stable_sort>
|
||||
#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 EventInfo> class DelayedEventDispatcher;
|
||||
|
||||
namespace dom {
|
||||
class Element;
|
||||
}
|
||||
|
||||
template <class AnimationType, class AnimationEventType>
|
||||
template <class AnimationType>
|
||||
class CommonAnimationManager {
|
||||
public:
|
||||
explicit CommonAnimationManager(nsPresContext *aPresContext)
|
||||
|
@ -75,18 +67,6 @@ public:
|
|||
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:
|
||||
virtual ~CommonAnimationManager()
|
||||
{
|
||||
|
@ -107,8 +87,6 @@ protected:
|
|||
|
||||
LinkedList<AnimationCollection<AnimationType>> mElementCollections;
|
||||
nsPresContext *mPresContext; // weak (non-null from ctor to Disconnect)
|
||||
|
||||
mozilla::DelayedEventDispatcher<AnimationEventType> mEventDispatcher;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -179,122 +157,6 @@ private:
|
|||
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
|
||||
// doesn't have a target effect.
|
||||
template <typename PhaseType>
|
||||
|
@ -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<TimeDuration> currentTime = aAnimation.GetCurrentTime();
|
||||
if (currentTime.IsNull()) {
|
||||
return PhaseType::Idle;
|
||||
|
|
|
@ -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<CSSAnimation>
|
||||
|
|
|
@ -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<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 {
|
||||
|
||||
class CSSAnimation final : public Animation
|
||||
|
@ -311,18 +275,15 @@ struct AnimationTypeTraits<dom::CSSAnimation>
|
|||
} /* namespace mozilla */
|
||||
|
||||
class nsAnimationManager final
|
||||
: public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation,
|
||||
mozilla::AnimationEventInfo>
|
||||
: public mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>
|
||||
{
|
||||
public:
|
||||
explicit nsAnimationManager(nsPresContext *aPresContext)
|
||||
: mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation,
|
||||
mozilla::AnimationEventInfo>(aPresContext)
|
||||
: mozilla::CommonAnimationManager<mozilla::dom::CSSAnimation>(aPresContext)
|
||||
{
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(nsAnimationManager)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(nsAnimationManager)
|
||||
NS_INLINE_DECL_REFCOUNTING(nsAnimationManager)
|
||||
|
||||
typedef mozilla::AnimationCollection<mozilla::dom::CSSAnimation>
|
||||
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<nsAnimationManager> 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).
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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?
|
||||
|
||||
|
|
|
@ -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<TransitionEventInfo, 3> events;
|
||||
AutoTArray<AnimationEventInfo, 3> events;
|
||||
|
||||
auto appendTransitionEvent = [&](EventMessage aMessage,
|
||||
const StickyTimeDuration& aElapsedTime,
|
||||
|
@ -258,9 +259,9 @@ CSSTransition::QueueEvents(const StickyTimeDuration& aActiveTime)
|
|||
if (aMessage == eTransitionCancel) {
|
||||
elapsedTime = nsRFPService::ReduceTimePrecisionAsSecs(elapsedTime);
|
||||
}
|
||||
events.AppendElement(TransitionEventInfo(mOwningElement.Target(),
|
||||
events.AppendElement(AnimationEventInfo(TransitionProperty(),
|
||||
mOwningElement.Target(),
|
||||
aMessage,
|
||||
TransitionProperty(),
|
||||
elapsedTime,
|
||||
aTimeStamp,
|
||||
this));
|
||||
|
@ -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,
|
||||
|
|
|
@ -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<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
|
||||
|
||||
class nsTransitionManager final
|
||||
: public mozilla::CommonAnimationManager<mozilla::dom::CSSTransition,
|
||||
mozilla::TransitionEventInfo>
|
||||
: public mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>
|
||||
{
|
||||
public:
|
||||
explicit nsTransitionManager(nsPresContext *aPresContext)
|
||||
: mozilla::CommonAnimationManager<mozilla::dom::CSSTransition,
|
||||
mozilla::TransitionEventInfo>(aPresContext)
|
||||
: mozilla::CommonAnimationManager<mozilla::dom::CSSTransition>(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<mozilla::dom::CSSTransition>
|
||||
CSSTransitionCollection;
|
||||
|
@ -406,12 +363,6 @@ public:
|
|||
return mInAnimationOnlyStyleUpdate;
|
||||
}
|
||||
|
||||
void DispatchEvents()
|
||||
{
|
||||
RefPtr<nsTransitionManager> kungFuDeathGrip(this);
|
||||
mEventDispatcher.DispatchEvents(mPresContext);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~nsTransitionManager() {}
|
||||
|
||||
|
|
|
@ -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<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.
|
||||
pub scroll_area_response: Rect<i32>,
|
||||
|
||||
|
@ -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<N: LayoutNode>(requested_node: N, layout_root
|
|||
}
|
||||
}
|
||||
|
||||
pub fn process_node_overflow_request<N: LayoutNode>(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<N: LayoutNode>(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<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,
|
||||
}
|
||||
StyleResponse(data.map(|d| d.styles.primary().clone()))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Arc<ComputedValues>> {
|
||||
window_from_node(self).style_query(
|
||||
self.upcast::<Node>().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::<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
|
||||
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::<Node>().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::<Node>().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::<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
|
||||
box_.clone_overflow_x() == overflow_x::computed_value::T::Hidden ||
|
||||
box_.clone_overflow_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::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<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> {
|
||||
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<servo_arc::Arc<ComputedValues>> {
|
||||
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",
|
||||
});
|
||||
|
|
|
@ -113,13 +113,12 @@ pub enum ReflowGoal {
|
|||
TickAnimations,
|
||||
ContentBoxQuery(TrustedNodeAddress),
|
||||
ContentBoxesQuery(TrustedNodeAddress),
|
||||
NodeOverflowQuery(TrustedNodeAddress),
|
||||
NodeScrollRootIdQuery(TrustedNodeAddress),
|
||||
NodeGeometryQuery(TrustedNodeAddress),
|
||||
NodeScrollGeometryQuery(TrustedNodeAddress),
|
||||
ResolvedStyleQuery(TrustedNodeAddress, Option<PseudoElement>, PropertyId),
|
||||
OffsetParentQuery(TrustedNodeAddress),
|
||||
MarginStyleQuery(TrustedNodeAddress),
|
||||
StyleQuery(TrustedNodeAddress),
|
||||
TextIndexQuery(TrustedNodeAddress, Point2D<f32>),
|
||||
NodesFromPointQuery(Point2D<f32>, 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 |
|
||||
|
|
|
@ -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<UntrustedNodeAddress>;
|
||||
|
@ -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<Arc<ComputedValues>>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TextIndexResponse(pub Option<usize>);
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
env:
|
||||
RUST_BACKTRACE: '1'
|
||||
RUSTFLAGS: -Dwarnings
|
||||
CARGO_INCREMENTAL: "0"
|
||||
SCCACHE_IDLE_TIMEOUT: "600"
|
||||
|
@ -96,6 +97,10 @@ mac-rel-intermittent:
|
|||
- ./etc/ci/check_intermittents.sh --log-raw intermittents.log
|
||||
|
||||
linux-dev:
|
||||
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
|
||||
|
@ -112,6 +117,10 @@ linux-dev:
|
|||
- bash ./etc/ci/check_no_panic.sh
|
||||
|
||||
linux-rel-wpt:
|
||||
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
|
||||
|
@ -121,6 +130,10 @@ linux-rel-wpt:
|
|||
- ./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:
|
||||
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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче