diff options
-rw-r--r-- | dom/base/ResizeObserver.cpp | 752 | ||||
-rw-r--r-- | dom/base/ResizeObserver.h | 624 | ||||
-rw-r--r-- | dom/base/ResizeObserverController.h | 6 |
3 files changed, 691 insertions, 691 deletions
diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp index f168b87c55..51fd603217 100644 --- a/dom/base/ResizeObserver.cpp +++ b/dom/base/ResizeObserver.cpp @@ -1,376 +1,376 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/dom/ResizeObserver.h"
-
-#include "mozilla/dom/DOMRect.h"
-#include "nsIContentInlines.h"
-#include "nsIFrame.h"
-#include "nsSVGUtils.h"
-
-namespace mozilla {
-namespace dom {
-
-/**
- * Returns the length of the parent-traversal path (in terms of the number of
- * nodes) to an unparented/root node from aNode. An unparented/root node is
- * considered to have a depth of 1, its children have a depth of 2, etc.
- * aNode is expected to be non-null.
- * Note: The shadow root is not part of the calculation because the caller,
- * ResizeObserver, doesn't observe the shadow root, and only needs relative
- * depths among all the observed targets. In other words, we calculate the
- * depth of the flattened tree.
- *
- * Note: these is a spec issue about how to handle shadow DOM case. We
- * may need to update this function later.
- *
- *
- * https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h
- */
-static uint32_t
-GetNodeDepth(nsINode* aNode) {
- uint32_t depth = 1;
-
- MOZ_ASSERT(aNode, "Node shouldn't be null");
-
- // Use GetFlattenedTreeParentNode to bypass the shadow root and cross the
- // shadow boundary to calculate the node depth without the shadow root.
- while ((aNode = aNode->GetFlattenedTreeParentNode())) {
- ++depth;
- }
-
- return depth;
-}
-
-/**
- * Returns |aTarget|'s size in the form of an nsSize.
- * If the target is an SVG, width and height are determined from the bounding box.
- */
-static nsSize
-GetTargetSize(Element* aTarget, ResizeObserverBoxOptions aBox) {
- nsSize size;
- nsIFrame* frame = aTarget->GetPrimaryFrame();
-
- if (!frame) {
- return size;
- }
-
- if (aTarget->IsSVGElement()) {
- // Per the spec, an SVG size is always its bounding box size, no matter what
- // box option you choose, because SVG elements do not use the standard CSS box
- // model.
- gfxRect bbox = nsSVGUtils::GetBBox(frame);
- size.width = NSFloatPixelsToAppUnits(bbox.width, AppUnitsPerCSSPixel());
- size.height = NSFloatPixelsToAppUnits(bbox.height, AppUnitsPerCSSPixel());
- } else {
- // Per the spec, non-replaced inline Elements will always have an empty
- // content rect. We therefore always use the same empty size for
- // non-replaced inline elements here, and their IsActive() will
- // always return false. (So its observation won't be fired.)
- if (!frame->IsFrameOfType(nsIFrame::eReplaced) &&
- frame->IsFrameOfType(nsIFrame::eLineParticipant)) {
- return size;
- }
-
- switch (aBox) {
- case ResizeObserverBoxOptions::Border_box:
- // GetSize() includes the content area, borders, and padding.
- size = frame->GetSize();
- break;
- case ResizeObserverBoxOptions::Content_box:
- default:
- size = frame->GetContentRectRelativeToSelf().Size();
- }
- }
-
- return size;
-}
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserver)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserver)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserver)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(ResizeObserver)
-
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResizeObserver)
- NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObserver)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservationMap)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObserver)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservationMap)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-already_AddRefed<ResizeObserver>
-ResizeObserver::Constructor(const GlobalObject& aGlobal,
- ResizeObserverCallback& aCb,
- ErrorResult& aRv)
-{
- nsCOMPtr<nsPIDOMWindowInner> window =
- do_QueryInterface(aGlobal.GetAsSupports());
-
- if (!window) {
- aRv.Throw(NS_ERROR_FAILURE);
- return nullptr;
- }
-
- nsCOMPtr<nsIDocument> document = window->GetExtantDoc();
-
- if (!document) {
- aRv.Throw(NS_ERROR_FAILURE);
- return nullptr;
- }
-
- RefPtr<ResizeObserver> observer = new ResizeObserver(window.forget(), aCb);
- document->AddResizeObserver(observer);
-
- return observer.forget();
-}
-
-void
-ResizeObserver::Observe(Element* aTarget,
- const ResizeObserverOptions& aOptions,
- ErrorResult& aRv)
-{
- if (!aTarget) {
- aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
- return;
- }
-
- RefPtr<ResizeObservation> observation;
-
- if (!mObservationMap.Get(aTarget, getter_AddRefs(observation))) {
- nsIFrame* frame = aTarget->GetPrimaryFrame();
- WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
- observation = new ResizeObservation(aTarget->OwnerDoc(), aTarget, aOptions.mBox, wm);
-
- mObservationMap.Put(aTarget, observation);
- mObservationList.insertBack(observation);
-
- // Per the spec, we need to trigger notification in event loop that
- // contains ResizeObserver observe call even when resize/reflow does
- // not happen.
- aTarget->OwnerDoc()->ScheduleResizeObserversNotification();
- }
-}
-
-void
-ResizeObserver::Unobserve(Element* aTarget,
- ErrorResult& aRv)
-{
- if (!aTarget) {
- aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
- return;
- }
-
- RefPtr<ResizeObservation> observation;
-
- if (mObservationMap.Get(aTarget, getter_AddRefs(observation))) {
- mObservationMap.Remove(aTarget);
-
- MOZ_ASSERT(!mObservationList.isEmpty(),
- "If ResizeObservation found for an element, observation list "
- "must be not empty.");
-
- observation->remove();
- }
-}
-
-void
-ResizeObserver::Disconnect()
-{
- mObservationMap.Clear();
- mObservationList.clear();
- mActiveTargets.Clear();
-}
-
-void
-ResizeObserver::GatherActiveObservations(uint32_t aDepth)
-{
- mActiveTargets.Clear();
- mHasSkippedTargets = false;
-
- for (auto observation : mObservationList) {
- if (observation->IsActive()) {
- uint32_t targetDepth = GetNodeDepth(observation->Target());
-
- if (targetDepth > aDepth) {
- mActiveTargets.AppendElement(observation);
- } else {
- mHasSkippedTargets = true;
- }
- }
- }
-}
-
-bool
-ResizeObserver::HasActiveObservations() const
-{
- return !mActiveTargets.IsEmpty();
-}
-
-bool
-ResizeObserver::HasSkippedObservations() const
-{
- return mHasSkippedTargets;
-}
-
-uint32_t
-ResizeObserver::BroadcastActiveObservations()
-{
- uint32_t shallowestTargetDepth = UINT32_MAX;
-
- if (HasActiveObservations()) {
- Sequence<OwningNonNull<ResizeObserverEntry>> entries;
-
- for (auto observation : mActiveTargets) {
- Element* target = observation->Target();
- RefPtr<ResizeObserverEntry> entry = new ResizeObserverEntry(this, target);
-
- nsSize borderBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Border_box);
- entry->SetBorderBoxSize(borderBoxSize);
-
- nsSize contentBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Content_box);
- entry->SetContentRectAndSize(contentBoxSize);
-
- if (!entries.AppendElement(entry.forget(), fallible)) {
- // Out of memory.
- break;
- }
-
- // Sync the broadcast size of observation so the next size inspection
- // will be based on the updated size from last delivered observations.
- switch (observation->BoxOptions()) {
- case ResizeObserverBoxOptions::Border_box:
- observation->UpdateLastReportedSize(borderBoxSize);
- break;
- case ResizeObserverBoxOptions::Content_box:
- default:
- observation->UpdateLastReportedSize(contentBoxSize);
- }
-
- uint32_t targetDepth = GetNodeDepth(observation->Target());
-
- if (targetDepth < shallowestTargetDepth) {
- shallowestTargetDepth = targetDepth;
- }
- }
-
- mCallback->Call(this, entries, *this);
- mActiveTargets.Clear();
- mHasSkippedTargets = false;
- }
-
- return shallowestTargetDepth;
-}
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverEntry)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverEntry)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverEntry)
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverEntry,
- mOwner,
- mTarget,
- mContentRect,
- mBorderBoxSize,
- mContentBoxSize)
-
-already_AddRefed<ResizeObserverEntry>
-ResizeObserverEntry::Constructor(const GlobalObject& aGlobal,
- Element* aTarget,
- ErrorResult& aRv)
-{
- RefPtr<ResizeObserverEntry> observerEntry =
- new ResizeObserverEntry(aGlobal.GetAsSupports(), aTarget);
- return observerEntry.forget();
-}
-
-void
-ResizeObserverEntry::SetBorderBoxSize(const nsSize& aSize) {
- nsIFrame* frame = mTarget->GetPrimaryFrame();
- WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
- mBorderBoxSize = new ResizeObserverSize(this, aSize, wm);
-}
-
-void
-ResizeObserverEntry::SetContentRectAndSize(const nsSize& aSize) {
- nsIFrame* frame = mTarget->GetPrimaryFrame();
-
- // Update mContentRect.
- nsMargin padding = frame ? frame->GetUsedPadding(): nsMargin();
- // Per the spec, we need to use the top-left padding offset as the origin of
- // our contentRect.
- nsRect rect(nsPoint(padding.left, padding.top), aSize);
- RefPtr<DOMRect> contentRect = new DOMRect(mTarget);
- contentRect->SetLayoutRect(rect);
- mContentRect = contentRect.forget();
-
- // Update mContentBoxSize.
- WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
- mContentBoxSize = new ResizeObserverSize(this, aSize, wm);
-}
-
-ResizeObserverEntry::~ResizeObserverEntry()
-{
-}
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize, mOwner)
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize)
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverSize)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObservation)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObservation)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObservation)
-
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObservation,
- mTarget, mOwner)
-
-bool
-ResizeObservation::IsActive() const
-{
- nsIFrame* frame = mTarget->GetPrimaryFrame();
- WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
- LogicalSize size(wm, GetTargetSize(mTarget, mObservedBox));
- return mLastReportedSize.ISize(mLastReportedWM) != size.ISize(wm) ||
- mLastReportedSize.BSize(mLastReportedWM) != size.BSize(wm);
-}
-
-void
-ResizeObservation::UpdateLastReportedSize(const nsSize& aSize) {
- nsIFrame* frame = mTarget->GetPrimaryFrame();
- mLastReportedWM = frame ? frame->GetWritingMode() : WritingMode();
- mLastReportedSize = LogicalSize(mLastReportedWM, aSize);
-}
-
-ResizeObservation::~ResizeObservation()
-{
-}
-
-} // namespace dom
-} // namespace mozilla
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/dom/ResizeObserver.h" + +#include "mozilla/dom/DOMRect.h" +#include "nsIContentInlines.h" +#include "nsIFrame.h" +#include "nsSVGUtils.h" + +namespace mozilla { +namespace dom { + +/** + * Returns the length of the parent-traversal path (in terms of the number of + * nodes) to an unparented/root node from aNode. An unparented/root node is + * considered to have a depth of 1, its children have a depth of 2, etc. + * aNode is expected to be non-null. + * Note: The shadow root is not part of the calculation because the caller, + * ResizeObserver, doesn't observe the shadow root, and only needs relative + * depths among all the observed targets. In other words, we calculate the + * depth of the flattened tree. + * + * Note: these is a spec issue about how to handle shadow DOM case. We + * may need to update this function later. + * + * + * https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h + */ +static uint32_t +GetNodeDepth(nsINode* aNode) { + uint32_t depth = 1; + + MOZ_ASSERT(aNode, "Node shouldn't be null"); + + // Use GetFlattenedTreeParentNode to bypass the shadow root and cross the + // shadow boundary to calculate the node depth without the shadow root. + while ((aNode = aNode->GetFlattenedTreeParentNode())) { + ++depth; + } + + return depth; +} + +/** + * Returns |aTarget|'s size in the form of an nsSize. + * If the target is an SVG, width and height are determined from the bounding box. + */ +static nsSize +GetTargetSize(Element* aTarget, ResizeObserverBoxOptions aBox) { + nsSize size; + nsIFrame* frame = aTarget->GetPrimaryFrame(); + + if (!frame) { + return size; + } + + if (aTarget->IsSVGElement()) { + // Per the spec, an SVG size is always its bounding box size, no matter what + // box option you choose, because SVG elements do not use the standard CSS box + // model. + gfxRect bbox = nsSVGUtils::GetBBox(frame); + size.width = NSFloatPixelsToAppUnits(bbox.width, AppUnitsPerCSSPixel()); + size.height = NSFloatPixelsToAppUnits(bbox.height, AppUnitsPerCSSPixel()); + } else { + // Per the spec, non-replaced inline Elements will always have an empty + // content rect. We therefore always use the same empty size for + // non-replaced inline elements here, and their IsActive() will + // always return false. (So its observation won't be fired.) + if (!frame->IsFrameOfType(nsIFrame::eReplaced) && + frame->IsFrameOfType(nsIFrame::eLineParticipant)) { + return size; + } + + switch (aBox) { + case ResizeObserverBoxOptions::Border_box: + // GetSize() includes the content area, borders, and padding. + size = frame->GetSize(); + break; + case ResizeObserverBoxOptions::Content_box: + default: + size = frame->GetContentRectRelativeToSelf().Size(); + } + } + + return size; +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserver) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserver) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserver) + +NS_IMPL_CYCLE_COLLECTION_CLASS(ResizeObserver) + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ResizeObserver) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ResizeObserver) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservationMap) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ResizeObserver) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservationMap) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +already_AddRefed<ResizeObserver> +ResizeObserver::Constructor(const GlobalObject& aGlobal, + ResizeObserverCallback& aCb, + ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> window = + do_QueryInterface(aGlobal.GetAsSupports()); + + if (!window) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + nsCOMPtr<nsIDocument> document = window->GetExtantDoc(); + + if (!document) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + RefPtr<ResizeObserver> observer = new ResizeObserver(window.forget(), aCb); + document->AddResizeObserver(observer); + + return observer.forget(); +} + +void +ResizeObserver::Observe(Element* aTarget, + const ResizeObserverOptions& aOptions, + ErrorResult& aRv) +{ + if (!aTarget) { + aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + RefPtr<ResizeObservation> observation; + + if (!mObservationMap.Get(aTarget, getter_AddRefs(observation))) { + nsIFrame* frame = aTarget->GetPrimaryFrame(); + WritingMode wm = frame ? frame->GetWritingMode() : WritingMode(); + observation = new ResizeObservation(aTarget->OwnerDoc(), aTarget, aOptions.mBox, wm); + + mObservationMap.Put(aTarget, observation); + mObservationList.insertBack(observation); + + // Per the spec, we need to trigger notification in event loop that + // contains ResizeObserver observe call even when resize/reflow does + // not happen. + aTarget->OwnerDoc()->ScheduleResizeObserversNotification(); + } +} + +void +ResizeObserver::Unobserve(Element* aTarget, + ErrorResult& aRv) +{ + if (!aTarget) { + aRv.Throw(NS_ERROR_DOM_NOT_FOUND_ERR); + return; + } + + RefPtr<ResizeObservation> observation; + + if (mObservationMap.Get(aTarget, getter_AddRefs(observation))) { + mObservationMap.Remove(aTarget); + + MOZ_ASSERT(!mObservationList.isEmpty(), + "If ResizeObservation found for an element, observation list " + "must be not empty."); + + observation->remove(); + } +} + +void +ResizeObserver::Disconnect() +{ + mObservationMap.Clear(); + mObservationList.clear(); + mActiveTargets.Clear(); +} + +void +ResizeObserver::GatherActiveObservations(uint32_t aDepth) +{ + mActiveTargets.Clear(); + mHasSkippedTargets = false; + + for (auto observation : mObservationList) { + if (observation->IsActive()) { + uint32_t targetDepth = GetNodeDepth(observation->Target()); + + if (targetDepth > aDepth) { + mActiveTargets.AppendElement(observation); + } else { + mHasSkippedTargets = true; + } + } + } +} + +bool +ResizeObserver::HasActiveObservations() const +{ + return !mActiveTargets.IsEmpty(); +} + +bool +ResizeObserver::HasSkippedObservations() const +{ + return mHasSkippedTargets; +} + +uint32_t +ResizeObserver::BroadcastActiveObservations() +{ + uint32_t shallowestTargetDepth = UINT32_MAX; + + if (HasActiveObservations()) { + Sequence<OwningNonNull<ResizeObserverEntry>> entries; + + for (auto observation : mActiveTargets) { + Element* target = observation->Target(); + RefPtr<ResizeObserverEntry> entry = new ResizeObserverEntry(this, target); + + nsSize borderBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Border_box); + entry->SetBorderBoxSize(borderBoxSize); + + nsSize contentBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Content_box); + entry->SetContentRectAndSize(contentBoxSize); + + if (!entries.AppendElement(entry.forget(), fallible)) { + // Out of memory. + break; + } + + // Sync the broadcast size of observation so the next size inspection + // will be based on the updated size from last delivered observations. + switch (observation->BoxOptions()) { + case ResizeObserverBoxOptions::Border_box: + observation->UpdateLastReportedSize(borderBoxSize); + break; + case ResizeObserverBoxOptions::Content_box: + default: + observation->UpdateLastReportedSize(contentBoxSize); + } + + uint32_t targetDepth = GetNodeDepth(observation->Target()); + + if (targetDepth < shallowestTargetDepth) { + shallowestTargetDepth = targetDepth; + } + } + + mCallback->Call(this, entries, *this); + mActiveTargets.Clear(); + mHasSkippedTargets = false; + } + + return shallowestTargetDepth; +} + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverEntry) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverEntry) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverEntry) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverEntry, + mOwner, + mTarget, + mContentRect, + mBorderBoxSize, + mContentBoxSize) + +already_AddRefed<ResizeObserverEntry> +ResizeObserverEntry::Constructor(const GlobalObject& aGlobal, + Element* aTarget, + ErrorResult& aRv) +{ + RefPtr<ResizeObserverEntry> observerEntry = + new ResizeObserverEntry(aGlobal.GetAsSupports(), aTarget); + return observerEntry.forget(); +} + +void +ResizeObserverEntry::SetBorderBoxSize(const nsSize& aSize) { + nsIFrame* frame = mTarget->GetPrimaryFrame(); + WritingMode wm = frame ? frame->GetWritingMode() : WritingMode(); + mBorderBoxSize = new ResizeObserverSize(this, aSize, wm); +} + +void +ResizeObserverEntry::SetContentRectAndSize(const nsSize& aSize) { + nsIFrame* frame = mTarget->GetPrimaryFrame(); + + // Update mContentRect. + nsMargin padding = frame ? frame->GetUsedPadding(): nsMargin(); + // Per the spec, we need to use the top-left padding offset as the origin of + // our contentRect. + nsRect rect(nsPoint(padding.left, padding.top), aSize); + RefPtr<DOMRect> contentRect = new DOMRect(mTarget); + contentRect->SetLayoutRect(rect); + mContentRect = contentRect.forget(); + + // Update mContentBoxSize. + WritingMode wm = frame ? frame->GetWritingMode() : WritingMode(); + mContentBoxSize = new ResizeObserverSize(this, aSize, wm); +} + +ResizeObserverEntry::~ResizeObserverEntry() +{ +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize, mOwner) +NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverSize) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObservation) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObservation) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObservation) + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObservation, + mTarget, mOwner) + +bool +ResizeObservation::IsActive() const +{ + nsIFrame* frame = mTarget->GetPrimaryFrame(); + WritingMode wm = frame ? frame->GetWritingMode() : WritingMode(); + LogicalSize size(wm, GetTargetSize(mTarget, mObservedBox)); + return mLastReportedSize.ISize(mLastReportedWM) != size.ISize(wm) || + mLastReportedSize.BSize(mLastReportedWM) != size.BSize(wm); +} + +void +ResizeObservation::UpdateLastReportedSize(const nsSize& aSize) { + nsIFrame* frame = mTarget->GetPrimaryFrame(); + mLastReportedWM = frame ? frame->GetWritingMode() : WritingMode(); + mLastReportedSize = LogicalSize(mLastReportedWM, aSize); +} + +ResizeObservation::~ResizeObservation() +{ +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/base/ResizeObserver.h b/dom/base/ResizeObserver.h index 92a7ee08cc..56675693ce 100644 --- a/dom/base/ResizeObserver.h +++ b/dom/base/ResizeObserver.h @@ -1,312 +1,312 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_dom_ResizeObserver_h
-#define mozilla_dom_ResizeObserver_h
-
-#include "mozilla/AppUnits.h"
-#include "mozilla/WritingModes.h"
-#include "mozilla/dom/ResizeObserverBinding.h"
-#include "nsCoord.h"
-
-namespace mozilla {
-namespace dom {
-
-/**
- * ResizeObserver interfaces and algorithms are based on
- * https://wicg.github.io/ResizeObserver/#api
- */
-class ResizeObserver final
- : public nsISupports
- , public nsWrapperCache
-{
-public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserver)
-
- ResizeObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner,
- ResizeObserverCallback& aCb)
- : mOwner(aOwner)
- , mCallback(&aCb)
- {
- MOZ_ASSERT(mOwner, "Need a non-null owner window");
- }
-
- static already_AddRefed<ResizeObserver>
- Constructor(const GlobalObject& aGlobal,
- ResizeObserverCallback& aCb,
- ErrorResult& aRv);
-
- JSObject* WrapObject(JSContext* aCx,
- JS::Handle<JSObject*> aGivenProto) override
- {
- return ResizeObserverBinding::Wrap(aCx, this, aGivenProto);
- }
-
- nsISupports* GetParentObject() const
- {
- return mOwner;
- }
-
- void Observe(Element* aTarget, const ResizeObserverOptions& aOptions, ErrorResult& aRv);
-
- void Unobserve(Element* aTarget, ErrorResult& aRv);
-
- void Disconnect();
-
- /*
- * Gather all observations which have an observed target with size changed
- * since last BroadcastActiveObservations() in this ResizeObserver.
- * An observation will be skipped if the depth of its observed target is less
- * or equal than aDepth. All gathered observations will be added to
- * mActiveTargets.
- */
- void GatherActiveObservations(uint32_t aDepth);
-
- /*
- * Returns whether this ResizeObserver has any active observations
- * since last GatherActiveObservations().
- */
- bool HasActiveObservations() const;
-
- /*
- * Returns whether this ResizeObserver has any skipped observations
- * since last GatherActiveObservations().
- */
- bool HasSkippedObservations() const;
-
- /*
- * Deliver the callback function in JavaScript for all active observations
- * and pass the sequence of ResizeObserverEntry so JavaScript can access them.
- * The active observations' mLastReportedSize fields will be updated, and
- * mActiveTargets will be cleared. It also returns the shallowest depth of
- * elements from active observations or numeric_limits<uint32_t>::max() if
- * there are not any active observations.
- */
- uint32_t BroadcastActiveObservations();
-
-protected:
- ~ResizeObserver()
- {
- mObservationList.clear();
- }
-
- nsCOMPtr<nsPIDOMWindowInner> mOwner;
- RefPtr<ResizeObserverCallback> mCallback;
- nsTArray<RefPtr<ResizeObservation>> mActiveTargets;
- // The spec uses a list to store the skipped targets. However, it seems what
- // we want is to check if there are any skipped targets (i.e. existence).
- // Therefore, we use a boolean value to represent the existence of skipped
- // targets.
- bool mHasSkippedTargets;
-
- // Combination of HashTable and LinkedList so we can iterate through
- // the elements of HashTable in order of insertion time.
- // Will be nice if we have our own data structure for this in the future.
- nsRefPtrHashtable<nsPtrHashKey<Element>, ResizeObservation> mObservationMap;
- LinkedList<ResizeObservation> mObservationList;
-};
-
-/**
- * ResizeObserverEntry is the entry that contains the information for observed
- * elements. This object is the one that visible to JavaScript in callback
- * function that is fired by ResizeObserver.
- */
-class ResizeObserverEntry final
- : public nsISupports
- , public nsWrapperCache
-{
-public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverEntry)
-
- ResizeObserverEntry(nsISupports* aOwner, Element* aTarget)
- : mOwner(aOwner)
- , mTarget(aTarget)
- {
- MOZ_ASSERT(mOwner, "Need a non-null owner");
- MOZ_ASSERT(mTarget, "Need a non-null target element");
- }
-
- static already_AddRefed<ResizeObserverEntry>
- Constructor(const GlobalObject& aGlobal,
- Element* aTarget,
- ErrorResult& aRv);
-
- JSObject* WrapObject(JSContext* aCx,
- JS::Handle<JSObject*> aGivenProto) override
- {
- return ResizeObserverEntryBinding::Wrap(aCx, this,
- aGivenProto);
- }
-
- nsISupports* GetParentObject() const
- {
- return mOwner;
- }
-
- Element* Target() const
- {
- return mTarget;
- }
-
- /*
- * Returns the DOMRectReadOnly of target's content rect so it can be
- * accessed from JavaScript in callback function of ResizeObserver.
- */
- DOMRectReadOnly* GetContentRect() const
- {
- return mContentRect;
- }
-
- /**
- * Returns target's logical border-box size and content-box size as
- * ResizeObserverSize.
- */
- ResizeObserverSize* BorderBoxSize() const {
- return mBorderBoxSize;
- }
- ResizeObserverSize* ContentBoxSize() const {
- return mContentBoxSize;
- }
-
- // Set borderBoxSize.
- void SetBorderBoxSize(const nsSize& aSize);
- // Set contentRect and contentBoxSize.
- void SetContentRectAndSize(const nsSize& aSize);
-
-protected:
- ~ResizeObserverEntry();
-
- nsCOMPtr<nsISupports> mOwner;
- nsCOMPtr<Element> mTarget;
-
- RefPtr<DOMRectReadOnly> mContentRect;
- RefPtr<ResizeObserverSize> mBorderBoxSize;
- RefPtr<ResizeObserverSize> mContentBoxSize;
-};
-
-class ResizeObserverSize final : public nsISupports, public nsWrapperCache {
-public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverSize)
-
- // Note: the unit of |aSize| is app unit, and we convert it into css pixel in
- // the public JS APIs.
- ResizeObserverSize(nsISupports* aOwner, const nsSize& aSize,
- const WritingMode aWM)
- : mOwner(aOwner), mSize(aWM, aSize), mWM(aWM) {
- MOZ_ASSERT(mOwner, "Need a non-null owner");
- }
-
- nsISupports* GetParentObject() const { return mOwner; }
-
- JSObject* WrapObject(JSContext* aCx,
- JS::Handle<JSObject*> aGivenProto) override {
- return ResizeObserverSizeBinding::Wrap(aCx, this, aGivenProto);
- }
-
- double InlineSize() const {
- return NSAppUnitsToDoublePixels(mSize.ISize(mWM), AppUnitsPerCSSPixel());
- }
-
- double BlockSize() const {
- return NSAppUnitsToDoublePixels(mSize.BSize(mWM), AppUnitsPerCSSPixel());
- }
-
-protected:
- ~ResizeObserverSize() = default;
-
- nsCOMPtr<nsISupports> mOwner;
- const LogicalSize mSize;
- const WritingMode mWM;
-};
-
-/**
- * We use ResizeObservation to store and sync the size information of one
- * observed element so we can decide whether an observation should be fired
- * or not.
- */
-class ResizeObservation final
- : public nsISupports
- , public nsWrapperCache
- , public LinkedListElement<ResizeObservation>
-{
-public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObservation)
-
- ResizeObservation(nsISupports* aOwner,
- Element* aTarget,
- ResizeObserverBoxOptions aBox,
- const WritingMode aWM)
- : mOwner(aOwner)
- , mTarget(aTarget)
- , mObservedBox(aBox)
- , mLastReportedSize(aWM)
- , mLastReportedWM(aWM)
- {
- MOZ_ASSERT(mOwner, "Need a non-null owner");
- MOZ_ASSERT(mTarget, "Need a non-null target element");
- }
-
- static already_AddRefed<ResizeObservation>
- Constructor(const GlobalObject& aGlobal,
- Element* aTarget,
- ErrorResult& aRv);
-
- JSObject* WrapObject(JSContext* aCx,
- JS::Handle<JSObject*> aGivenProto) override
- {
- return ResizeObservationBinding::Wrap(aCx, this, aGivenProto);
- }
-
- nsISupports* GetParentObject() const
- {
- return mOwner;
- }
-
- Element* Target() const
- {
- return mTarget;
- }
-
- ResizeObserverBoxOptions BoxOptions() const
- {
- return mObservedBox;
- }
-
- /*
- * Returns whether the observed target element size differs from the saved
- * mLastReportedSize.
- */
- bool IsActive() const;
-
- /*
- * Update current mLastReportedSize with size from aSize.
- */
- void UpdateLastReportedSize(const nsSize& aSize);
-
-protected:
- ~ResizeObservation();
-
- nsCOMPtr<nsISupports> mOwner;
- nsCOMPtr<Element> mTarget;
-
- const ResizeObserverBoxOptions mObservedBox;
-
- // The latest recorded size of observed target.
- // Per the spec, observation.lastReportedSize should be entry.borderBoxSize
- // or entry.contentBoxSize (i.e. logical size), instead of entry.contentRect
- // (i.e. physical rect), so we store this as LogicalSize.
- LogicalSize mLastReportedSize;
- WritingMode mLastReportedWM;
-};
-
-} // namespace dom
-} // namespace mozilla
-
-#endif // mozilla_dom_ResizeObserver_h
-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_ResizeObserver_h +#define mozilla_dom_ResizeObserver_h + +#include "mozilla/AppUnits.h" +#include "mozilla/WritingModes.h" +#include "mozilla/dom/ResizeObserverBinding.h" +#include "nsCoord.h" + +namespace mozilla { +namespace dom { + +/** + * ResizeObserver interfaces and algorithms are based on + * https://wicg.github.io/ResizeObserver/#api + */ +class ResizeObserver final + : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserver) + + ResizeObserver(already_AddRefed<nsPIDOMWindowInner>&& aOwner, + ResizeObserverCallback& aCb) + : mOwner(aOwner) + , mCallback(&aCb) + { + MOZ_ASSERT(mOwner, "Need a non-null owner window"); + } + + static already_AddRefed<ResizeObserver> + Constructor(const GlobalObject& aGlobal, + ResizeObserverCallback& aCb, + ErrorResult& aRv); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override + { + return ResizeObserverBinding::Wrap(aCx, this, aGivenProto); + } + + nsISupports* GetParentObject() const + { + return mOwner; + } + + void Observe(Element* aTarget, const ResizeObserverOptions& aOptions, ErrorResult& aRv); + + void Unobserve(Element* aTarget, ErrorResult& aRv); + + void Disconnect(); + + /* + * Gather all observations which have an observed target with size changed + * since last BroadcastActiveObservations() in this ResizeObserver. + * An observation will be skipped if the depth of its observed target is less + * or equal than aDepth. All gathered observations will be added to + * mActiveTargets. + */ + void GatherActiveObservations(uint32_t aDepth); + + /* + * Returns whether this ResizeObserver has any active observations + * since last GatherActiveObservations(). + */ + bool HasActiveObservations() const; + + /* + * Returns whether this ResizeObserver has any skipped observations + * since last GatherActiveObservations(). + */ + bool HasSkippedObservations() const; + + /* + * Deliver the callback function in JavaScript for all active observations + * and pass the sequence of ResizeObserverEntry so JavaScript can access them. + * The active observations' mLastReportedSize fields will be updated, and + * mActiveTargets will be cleared. It also returns the shallowest depth of + * elements from active observations or numeric_limits<uint32_t>::max() if + * there are not any active observations. + */ + uint32_t BroadcastActiveObservations(); + +protected: + ~ResizeObserver() + { + mObservationList.clear(); + } + + nsCOMPtr<nsPIDOMWindowInner> mOwner; + RefPtr<ResizeObserverCallback> mCallback; + nsTArray<RefPtr<ResizeObservation>> mActiveTargets; + // The spec uses a list to store the skipped targets. However, it seems what + // we want is to check if there are any skipped targets (i.e. existence). + // Therefore, we use a boolean value to represent the existence of skipped + // targets. + bool mHasSkippedTargets; + + // Combination of HashTable and LinkedList so we can iterate through + // the elements of HashTable in order of insertion time. + // Will be nice if we have our own data structure for this in the future. + nsRefPtrHashtable<nsPtrHashKey<Element>, ResizeObservation> mObservationMap; + LinkedList<ResizeObservation> mObservationList; +}; + +/** + * ResizeObserverEntry is the entry that contains the information for observed + * elements. This object is the one that visible to JavaScript in callback + * function that is fired by ResizeObserver. + */ +class ResizeObserverEntry final + : public nsISupports + , public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverEntry) + + ResizeObserverEntry(nsISupports* aOwner, Element* aTarget) + : mOwner(aOwner) + , mTarget(aTarget) + { + MOZ_ASSERT(mOwner, "Need a non-null owner"); + MOZ_ASSERT(mTarget, "Need a non-null target element"); + } + + static already_AddRefed<ResizeObserverEntry> + Constructor(const GlobalObject& aGlobal, + Element* aTarget, + ErrorResult& aRv); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override + { + return ResizeObserverEntryBinding::Wrap(aCx, this, + aGivenProto); + } + + nsISupports* GetParentObject() const + { + return mOwner; + } + + Element* Target() const + { + return mTarget; + } + + /* + * Returns the DOMRectReadOnly of target's content rect so it can be + * accessed from JavaScript in callback function of ResizeObserver. + */ + DOMRectReadOnly* GetContentRect() const + { + return mContentRect; + } + + /** + * Returns target's logical border-box size and content-box size as + * ResizeObserverSize. + */ + ResizeObserverSize* BorderBoxSize() const { + return mBorderBoxSize; + } + ResizeObserverSize* ContentBoxSize() const { + return mContentBoxSize; + } + + // Set borderBoxSize. + void SetBorderBoxSize(const nsSize& aSize); + // Set contentRect and contentBoxSize. + void SetContentRectAndSize(const nsSize& aSize); + +protected: + ~ResizeObserverEntry(); + + nsCOMPtr<nsISupports> mOwner; + nsCOMPtr<Element> mTarget; + + RefPtr<DOMRectReadOnly> mContentRect; + RefPtr<ResizeObserverSize> mBorderBoxSize; + RefPtr<ResizeObserverSize> mContentBoxSize; +}; + +class ResizeObserverSize final : public nsISupports, public nsWrapperCache { +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObserverSize) + + // Note: the unit of |aSize| is app unit, and we convert it into css pixel in + // the public JS APIs. + ResizeObserverSize(nsISupports* aOwner, const nsSize& aSize, + const WritingMode aWM) + : mOwner(aOwner), mSize(aWM, aSize), mWM(aWM) { + MOZ_ASSERT(mOwner, "Need a non-null owner"); + } + + nsISupports* GetParentObject() const { return mOwner; } + + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override { + return ResizeObserverSizeBinding::Wrap(aCx, this, aGivenProto); + } + + double InlineSize() const { + return NSAppUnitsToDoublePixels(mSize.ISize(mWM), AppUnitsPerCSSPixel()); + } + + double BlockSize() const { + return NSAppUnitsToDoublePixels(mSize.BSize(mWM), AppUnitsPerCSSPixel()); + } + +protected: + ~ResizeObserverSize() = default; + + nsCOMPtr<nsISupports> mOwner; + const LogicalSize mSize; + const WritingMode mWM; +}; + +/** + * We use ResizeObservation to store and sync the size information of one + * observed element so we can decide whether an observation should be fired + * or not. + */ +class ResizeObservation final + : public nsISupports + , public nsWrapperCache + , public LinkedListElement<ResizeObservation> +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObservation) + + ResizeObservation(nsISupports* aOwner, + Element* aTarget, + ResizeObserverBoxOptions aBox, + const WritingMode aWM) + : mOwner(aOwner) + , mTarget(aTarget) + , mObservedBox(aBox) + , mLastReportedSize(aWM) + , mLastReportedWM(aWM) + { + MOZ_ASSERT(mOwner, "Need a non-null owner"); + MOZ_ASSERT(mTarget, "Need a non-null target element"); + } + + static already_AddRefed<ResizeObservation> + Constructor(const GlobalObject& aGlobal, + Element* aTarget, + ErrorResult& aRv); + + JSObject* WrapObject(JSContext* aCx, + JS::Handle<JSObject*> aGivenProto) override + { + return ResizeObservationBinding::Wrap(aCx, this, aGivenProto); + } + + nsISupports* GetParentObject() const + { + return mOwner; + } + + Element* Target() const + { + return mTarget; + } + + ResizeObserverBoxOptions BoxOptions() const + { + return mObservedBox; + } + + /* + * Returns whether the observed target element size differs from the saved + * mLastReportedSize. + */ + bool IsActive() const; + + /* + * Update current mLastReportedSize with size from aSize. + */ + void UpdateLastReportedSize(const nsSize& aSize); + +protected: + ~ResizeObservation(); + + nsCOMPtr<nsISupports> mOwner; + nsCOMPtr<Element> mTarget; + + const ResizeObserverBoxOptions mObservedBox; + + // The latest recorded size of observed target. + // Per the spec, observation.lastReportedSize should be entry.borderBoxSize + // or entry.contentBoxSize (i.e. logical size), instead of entry.contentRect + // (i.e. physical rect), so we store this as LogicalSize. + LogicalSize mLastReportedSize; + WritingMode mLastReportedWM; +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_ResizeObserver_h + diff --git a/dom/base/ResizeObserverController.h b/dom/base/ResizeObserverController.h index 01169112aa..346d5ab93f 100644 --- a/dom/base/ResizeObserverController.h +++ b/dom/base/ResizeObserverController.h @@ -41,9 +41,9 @@ public: void Disconnect(); - bool IsRegistered() const { return mRegistered; }
-
- void DetachFromOwner() { mOwner = nullptr; }
+ bool IsRegistered() const { return mRegistered; } + + void DetachFromOwner() { mOwner = nullptr; } protected: virtual ~ResizeObserverNotificationHelper(); |