summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoonchild <moonchild@palemoon.org>2021-06-19 15:38:14 +0000
committerMoonchild <moonchild@palemoon.org>2021-06-19 15:38:14 +0000
commit6f7dd6fd98a6972169eb4134d3a5423e6423d632 (patch)
treec51de46cdba7475f241e6e13986ebb1376333027
parent22a75217e1ab6203c9952597182c154e44b2637a (diff)
downloaduxp-6f7dd6fd98a6972169eb4134d3a5423e6423d632.tar.gz
Issue #1783 - Part 2: Update ResizeObserver and resizeObserverSize.
This adds the later spec revision's contentBoxSize and borderBoxSize, and the inlineSize and blockSize concepts. The older spec contentRect remains functional as well for backwards compatibility with the earlier spec (that was IMHO perfectly fine as it was...)
-rw-r--r--dom/base/ResizeObserver.cpp163
-rw-r--r--dom/base/ResizeObserver.h118
-rw-r--r--dom/bindings/Bindings.conf5
-rw-r--r--dom/webidl/ResizeObserver.webidl24
4 files changed, 228 insertions, 82 deletions
diff --git a/dom/base/ResizeObserver.cpp b/dom/base/ResizeObserver.cpp
index c3302efee3..67467b0fc3 100644
--- a/dom/base/ResizeObserver.cpp
+++ b/dom/base/ResizeObserver.cpp
@@ -29,7 +29,8 @@ namespace dom {
*
* https://drafts.csswg.org/resize-observer/#calculate-depth-for-node-h
*/
-static uint32_t GetNodeDepth(nsINode* aNode) {
+static uint32_t
+GetNodeDepth(nsINode* aNode) {
uint32_t depth = 1;
MOZ_ASSERT(aNode, "Node shouldn't be null");
@@ -43,6 +44,50 @@ static uint32_t GetNodeDepth(nsINode* aNode) {
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)
@@ -98,6 +143,7 @@ ResizeObserver::Constructor(const GlobalObject& aGlobal,
void
ResizeObserver::Observe(Element* aTarget,
+ const ResizeObserverOptions& aOptions,
ErrorResult& aRv)
{
if (!aTarget) {
@@ -108,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);
@@ -190,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);
+
+ nsSize borderBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Border_box);
+ entry->SetBorderBoxSize(borderBoxSize);
- nsRect rect = observation->GetTargetRect();
- entry->SetContentRect(rect);
+ nsSize contentBoxSize = GetTargetSize(target, ResizeObserverBoxOptions::Content_box);
+ entry->SetContentRectAndSize(contentBoxSize);
if (!entries.AppendElement(entry.forget(), fallible)) {
// Out of memory.
@@ -203,7 +254,14 @@ 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 = GetNodeDepth(observation->Target());
@@ -229,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,
@@ -243,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)
@@ -281,48 +356,40 @@ ResizeObservation::Constructor(const GlobalObject& aGlobal,
Element* aTarget,
ErrorResult& aRv)
{
+ ResizeObserverOptions options;
+ options.mBox = ResizeObserverBoxOptions::Content_box;
+ nsIFrame* frame = aTarget->GetPrimaryFrame();
+ WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
RefPtr<ResizeObservation> observation =
- new ResizeObservation(aGlobal.GetAsSupports(), aTarget);
+ new ResizeObservation(aGlobal.GetAsSupports(), aTarget, options.mBox, wm);
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::UpdateLastReportedSize(const nsSize& aSize) {
+ nsIFrame* frame = mTarget->GetPrimaryFrame();
+ mLastReportedWM = frame ? frame->GetWritingMode() : WritingMode();
+ mLastReportedSize = LogicalSize(mLastReportedWM, aSize);
+}
+
+/*
+void
ResizeObservation::UpdateBroadcastSize(nsRect aRect)
{
mBroadcastWidth = aRect.width;
mBroadcastHeight = aRect.height;
}
-
-nsRect
-ResizeObservation::GetTargetRect() const
-{
- nsRect rect;
- 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;
-}
+*/
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/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..c3ae93a810 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,11 +30,18 @@ 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;
+};
+
+[Pref="layout.css.resizeobserver.enabled"]
+interface ResizeObserverSize {
+ readonly attribute unrestricted double inlineSize;
+ readonly attribute unrestricted double blockSize;
};
[Constructor(Element? target),
@@ -33,7 +49,7 @@ interface ResizeObserverEntry {
Pref="layout.css.resizeobserver.enabled"]
interface ResizeObservation {
readonly attribute Element target;
- readonly attribute long broadcastWidth;
- readonly attribute long broadcastHeight;
+/* readonly attribute long broadcastWidth;
+ readonly attribute long broadcastHeight; */
boolean isActive();
};