summaryrefslogtreecommitdiff
path: root/dom/base
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base')
-rw-r--r--dom/base/ResizeObserver.cpp163
-rw-r--r--dom/base/ResizeObserver.h118
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