diff options
author | Moonchild <moonchild@palemoon.org> | 2021-06-19 15:38:14 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2021-06-19 15:38:14 +0000 |
commit | 6f7dd6fd98a6972169eb4134d3a5423e6423d632 (patch) | |
tree | c51de46cdba7475f241e6e13986ebb1376333027 /dom/base | |
parent | 22a75217e1ab6203c9952597182c154e44b2637a (diff) | |
download | uxp-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...)
Diffstat (limited to 'dom/base')
-rw-r--r-- | dom/base/ResizeObserver.cpp | 163 | ||||
-rw-r--r-- | dom/base/ResizeObserver.h | 118 |
2 files changed, 203 insertions, 78 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
|