summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2021-06-19 23:00:12 +0000
committerMoonchild <moonchild@palemoon.org>2021-06-19 23:00:12 +0000
commit014fdd0521150e795a37c62b7e1ade7f3a8b2396 (patch)
tree6f0861de75cb7e6f1f82d8173e2ff889b0917ada
parent2c626f604620140462d52d5865d4beb0a9d11ae3 (diff)
parentcfdfbcf059525690b8e8fb5ad2bf089fd9c607f4 (diff)
downloaduxp-014fdd0521150e795a37c62b7e1ade7f3a8b2396.tar.gz
Merge branch '1783'
-rw-r--r--dom/base/ResizeObserver.cpp200
-rw-r--r--dom/base/ResizeObserver.h118
-rw-r--r--dom/base/nsContentUtils.cpp14
-rw-r--r--dom/base/nsContentUtils.h8
-rw-r--r--dom/bindings/Bindings.conf5
-rw-r--r--dom/webidl/ResizeObserver.webidl25
6 files changed, 248 insertions, 122 deletions
diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp
index 37d940c2b3..f168b87c55 100644
--- a/dom/base/ResizeObserver.cpp
+++ b/dom/base/ResizeObserver.cpp
@@ -6,13 +6,88 @@
#include "mozilla/dom/ResizeObserver.h"
#include "mozilla/dom/DOMRect.h"
-#include "nsContentUtils.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)
@@ -68,6 +143,7 @@ ResizeObserver::Constructor(const GlobalObject& aGlobal,
void
ResizeObserver::Observe(Element* aTarget,
+ const ResizeObserverOptions& aOptions,
ErrorResult& aRv)
{
if (!aTarget) {
@@ -78,7 +154,9 @@ ResizeObserver::Observe(Element* aTarget,
RefPtr<ResizeObservation> observation;
if (!mObservationMap.Get(aTarget, getter_AddRefs(observation))) {
- observation = new ResizeObservation(this, aTarget);
+ 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);
@@ -128,8 +206,7 @@ ResizeObserver::GatherActiveObservations(uint32_t aDepth)
for (auto observation : mObservationList) {
if (observation->IsActive()) {
- uint32_t targetDepth =
- nsContentUtils::GetNodeDepth(observation->Target());
+ uint32_t targetDepth = GetNodeDepth(observation->Target());
if (targetDepth > aDepth) {
mActiveTargets.AppendElement(observation);
@@ -161,11 +238,14 @@ ResizeObserver::BroadcastActiveObservations()
Sequence<OwningNonNull<ResizeObserverEntry>> entries;
for (auto observation : mActiveTargets) {
- RefPtr<ResizeObserverEntry> entry =
- new ResizeObserverEntry(this, observation->Target());
+ Element* target = observation->Target();
+ RefPtr<ResizeObserverEntry> entry = new ResizeObserverEntry(this, target);
- nsRect rect = observation->GetTargetRect();
- entry->SetContentRect(rect);
+ 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.
@@ -174,10 +254,16 @@ ResizeObserver::BroadcastActiveObservations()
// Sync the broadcast size of observation so the next size inspection
// will be based on the updated size from last delivered observations.
- observation->UpdateBroadcastSize(rect);
+ switch (observation->BoxOptions()) {
+ case ResizeObserverBoxOptions::Border_box:
+ observation->UpdateLastReportedSize(borderBoxSize);
+ break;
+ case ResizeObserverBoxOptions::Content_box:
+ default:
+ observation->UpdateLastReportedSize(contentBoxSize);
+ }
- uint32_t targetDepth =
- nsContentUtils::GetNodeDepth(observation->Target());
+ uint32_t targetDepth = GetNodeDepth(observation->Target());
if (targetDepth < shallowestTargetDepth) {
shallowestTargetDepth = targetDepth;
@@ -201,8 +287,11 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverEntry)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverEntry)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverEntry,
- mTarget, mContentRect,
- mOwner)
+ mOwner,
+ mTarget,
+ mContentRect,
+ mBorderBoxSize,
+ mContentBoxSize)
already_AddRefed<ResizeObserverEntry>
ResizeObserverEntry::Constructor(const GlobalObject& aGlobal,
@@ -215,28 +304,42 @@ ResizeObserverEntry::Constructor(const GlobalObject& aGlobal,
}
void
-ResizeObserverEntry::SetContentRect(nsRect aRect)
-{
- RefPtr<DOMRect> contentRect = new DOMRect(mTarget);
+ResizeObserverEntry::SetBorderBoxSize(const nsSize& aSize) {
nsIFrame* frame = mTarget->GetPrimaryFrame();
+ WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
+ mBorderBoxSize = new ResizeObserverSize(this, aSize, wm);
+}
- if (frame) {
- nsMargin padding = frame->GetUsedPadding();
-
- // Per the spec, we need to include padding in contentRect of
- // ResizeObserverEntry.
- aRect.x = padding.left;
- aRect.y = padding.top;
- }
+void
+ResizeObserverEntry::SetContentRectAndSize(const nsSize& aSize) {
+ nsIFrame* frame = mTarget->GetPrimaryFrame();
- contentRect->SetLayoutRect(aRect);
+ // 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)
@@ -248,52 +351,21 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObservation)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObservation,
mTarget, mOwner)
-already_AddRefed<ResizeObservation>
-ResizeObservation::Constructor(const GlobalObject& aGlobal,
- Element* aTarget,
- ErrorResult& aRv)
-{
- RefPtr<ResizeObservation> observation =
- new ResizeObservation(aGlobal.GetAsSupports(), aTarget);
- return observation.forget();
-}
-
bool
ResizeObservation::IsActive() const
{
- nsRect rect = GetTargetRect();
- return (rect.width != mBroadcastWidth || rect.height != mBroadcastHeight);
+ 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::UpdateBroadcastSize(nsRect aRect)
-{
- mBroadcastWidth = aRect.width;
- mBroadcastHeight = aRect.height;
-}
-
-nsRect
-ResizeObservation::GetTargetRect() const
-{
- nsRect rect;
+ResizeObservation::UpdateLastReportedSize(const nsSize& aSize) {
nsIFrame* frame = mTarget->GetPrimaryFrame();
-
- if (frame) {
- if (mTarget->IsSVGElement()) {
- gfxRect bbox = nsSVGUtils::GetBBox(frame);
- rect.width = NSFloatPixelsToAppUnits(bbox.width, AppUnitsPerCSSPixel());
- rect.height = NSFloatPixelsToAppUnits(bbox.height, AppUnitsPerCSSPixel());
- } else {
- // Per the spec, non-replaced inline Elements will always have an empty
- // content rect.
- if (frame->IsFrameOfType(nsIFrame::eReplaced) ||
- !frame->IsFrameOfType(nsIFrame::eLineParticipant)) {
- rect = frame->GetContentRectRelativeToSelf();
- }
- }
- }
-
- return rect;
+ mLastReportedWM = frame ? frame->GetWritingMode() : WritingMode();
+ mLastReportedSize = LogicalSize(mLastReportedWM, aSize);
}
ResizeObservation::~ResizeObservation()
diff --git a/dom/base/ResizeObserver.h b/dom/base/ResizeObserver.h
index 2f56c580f4..92a7ee08cc 100644
--- a/dom/base/ResizeObserver.h
+++ b/dom/base/ResizeObserver.h
@@ -6,7 +6,10 @@
#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 {
@@ -47,7 +50,7 @@ public:
return mOwner;
}
- void Observe(Element* aTarget, ErrorResult& aRv);
+ void Observe(Element* aTarget, const ResizeObserverOptions& aOptions, ErrorResult& aRv);
void Unobserve(Element* aTarget, ErrorResult& aRv);
@@ -77,9 +80,10 @@ public:
/*
* Deliver the callback function in JavaScript for all active observations
* and pass the sequence of ResizeObserverEntry so JavaScript can access them.
- * The broadcast size of observations will be updated and mActiveTargets will
- * be cleared. It also returns the shallowest depth of elements from active
- * observations or UINT32_MAX if there is no any active observations.
+ * 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();
@@ -92,6 +96,10 @@ protected:
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
@@ -153,14 +161,67 @@ public:
return mContentRect;
}
- void SetContentRect(nsRect aRect);
+ /**
+ * 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;
};
/**
@@ -177,11 +238,15 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ResizeObservation)
- ResizeObservation(nsISupports* aOwner, Element* aTarget)
+ ResizeObservation(nsISupports* aOwner,
+ Element* aTarget,
+ ResizeObserverBoxOptions aBox,
+ const WritingMode aWM)
: mOwner(aOwner)
, mTarget(aTarget)
- , mBroadcastWidth(0)
- , mBroadcastHeight(0)
+ , mObservedBox(aBox)
+ , mLastReportedSize(aWM)
+ , mLastReportedWM(aWM)
{
MOZ_ASSERT(mOwner, "Need a non-null owner");
MOZ_ASSERT(mTarget, "Need a non-null target element");
@@ -208,32 +273,21 @@ public:
return mTarget;
}
- nscoord BroadcastWidth() const
- {
- return mBroadcastWidth;
- }
-
- nscoord BroadcastHeight() const
- {
- return mBroadcastHeight;
+ ResizeObserverBoxOptions BoxOptions() const
+ {
+ return mObservedBox;
}
/*
- * Returns whether the observed target element size differs from current
- * BroadcastWidth and BroadcastHeight
+ * Returns whether the observed target element size differs from the saved
+ * mLastReportedSize.
*/
bool IsActive() const;
/*
- * Update current BroadcastWidth and BroadcastHeight with size from aRect.
+ * Update current mLastReportedSize with size from aSize.
*/
- void UpdateBroadcastSize(nsRect aRect);
-
- /*
- * Returns the target's rect in the form of nsRect.
- * If the target is SVG, width and height are determined from bounding box.
- */
- nsRect GetTargetRect() const;
+ void UpdateLastReportedSize(const nsSize& aSize);
protected:
~ResizeObservation();
@@ -241,10 +295,14 @@ protected:
nsCOMPtr<nsISupports> mOwner;
nsCOMPtr<Element> mTarget;
- // Broadcast width and broadcast height are the latest recorded size
- // of observed target.
- nscoord mBroadcastWidth;
- nscoord mBroadcastHeight;
+ 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
diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
index 95f176ca8f..fdffd95531 100644
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9781,17 +9781,3 @@ nsContentUtils::GetClosestNonNativeAnonymousAncestor(Element* aElement)
}
return e;
}
-
-/* static */ uint32_t
-nsContentUtils::GetNodeDepth(nsINode* aNode)
-{
- uint32_t depth = 1;
-
- MOZ_ASSERT(aNode, "Node shouldn't be null");
-
- while ((aNode = aNode->GetParentNode())) {
- ++depth;
- }
-
- return depth;
-} \ No newline at end of file
diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
index 83ee417403..b3bc22f286 100644
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2756,14 +2756,6 @@ public:
static bool
IsCustomElementsEnabled() { return sIsCustomElementsEnabled; }
- /**
- * 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.
- */
- static uint32_t GetNodeDepth(nsINode* aNode);
-
private:
static bool InitializeEventTable();
diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf
index afa019c464..af3c7f70f0 100644
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -731,6 +731,11 @@ DOMInterfaces = {
'headerFile': 'mozilla/dom/ResizeObserver.h',
},
+'ResizeObserverSize': {
+ 'nativeType': 'mozilla::dom::ResizeObserverSize',
+ 'headerFile': 'mozilla/dom/ResizeObserver.h',
+},
+
'Response': {
'binaryNames': { 'headers': 'headers_' },
},
diff --git a/dom/webidl/ResizeObserver.webidl b/dom/webidl/ResizeObserver.webidl
index 98700f53c6..d764af7cbd 100644
--- a/dom/webidl/ResizeObserver.webidl
+++ b/dom/webidl/ResizeObserver.webidl
@@ -7,12 +7,21 @@
* https://wicg.github.io/ResizeObserver/
*/
+enum ResizeObserverBoxOptions {
+ "border-box",
+ "content-box"
+};
+
+dictionary ResizeObserverOptions {
+ ResizeObserverBoxOptions box = "content-box";
+};
+
[Constructor(ResizeObserverCallback callback),
Exposed=Window,
Pref="layout.css.resizeobserver.enabled"]
interface ResizeObserver {
[Throws]
- void observe(Element? target);
+ void observe(Element? target, optional ResizeObserverOptions options);
[Throws]
void unobserve(Element? target);
void disconnect();
@@ -21,19 +30,23 @@ interface ResizeObserver {
callback ResizeObserverCallback = void (sequence<ResizeObserverEntry> entries, ResizeObserver observer);
[Constructor(Element? target),
- ChromeOnly,
Pref="layout.css.resizeobserver.enabled"]
interface ResizeObserverEntry {
readonly attribute Element target;
readonly attribute DOMRectReadOnly? contentRect;
+ readonly attribute ResizeObserverSize borderBoxSize;
+ readonly attribute ResizeObserverSize contentBoxSize;
};
-[Constructor(Element? target),
- ChromeOnly,
+[Pref="layout.css.resizeobserver.enabled"]
+interface ResizeObserverSize {
+ readonly attribute unrestricted double inlineSize;
+ readonly attribute unrestricted double blockSize;
+};
+
+[ChromeOnly,
Pref="layout.css.resizeobserver.enabled"]
interface ResizeObservation {
readonly attribute Element target;
- readonly attribute long broadcastWidth;
- readonly attribute long broadcastHeight;
boolean isActive();
};