Skip to content

Commit

Permalink
Bug 1791375 - Switch ResizeObserver APIs from gfx::Size to LogicalPix…
Browse files Browse the repository at this point in the history
…elSize. r=emilio

This is in preparation of supporting multiple fragments, since we want
to track the logical sizes, and fragments might potentially have
different writing modes (but not sure if it can happen in practice).

This patch should have no observable effect.

Differential Revision: https://phabricator.services.mozilla.com/D157639
  • Loading branch information
Loirooriol committed Sep 21, 2022
1 parent 2188862 commit ac52d72
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 81 deletions.
139 changes: 68 additions & 71 deletions dom/base/ResizeObserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,22 @@ static nsSize GetContentRectSize(const nsIFrame& aFrame) {
*
* https://www.w3.org/TR/resize-observer-1/#calculate-box-size
*/
static gfx::Size CalculateBoxSize(Element* aTarget,
ResizeObserverBoxOptions aBox) {
gfx::Size size;
static LogicalPixelSize CalculateBoxSize(Element* aTarget,
ResizeObserverBoxOptions aBox) {
nsIFrame* frame = aTarget->GetPrimaryFrame();

if (!frame) {
return size;
return LogicalPixelSize();
}

if (frame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
// Per the spec, this target's SVG size is always its bounding box size no
// matter what box option you choose, because SVG elements do not use
// standard CSS box model.
const gfxRect bbox = SVGUtils::GetBBox(frame);
size.width = static_cast<float>(bbox.width);
size.height = static_cast<float>(bbox.height);
gfx::Size size(static_cast<float>(bbox.width),
static_cast<float>(bbox.height));
const WritingMode wm = frame->GetWritingMode();
if (aBox == ResizeObserverBoxOptions::Device_pixel_content_box) {
// Per spec, we calculate the inline/block sizes to target’s bounding box
// {inline|block} length, in integral device pixels, so we round the final
Expand All @@ -97,9 +97,9 @@ static gfx::Size CalculateBoxSize(Element* aTarget,
const LayoutDeviceIntSize snappedSize =
RoundedToInt(CSSSize::FromUnknownSize(size) *
frame->PresContext()->CSSToDevPixelScale());
return gfx::Size(snappedSize.ToUnknownSize());
return LogicalPixelSize(wm, gfx::Size(snappedSize.ToUnknownSize()));
}
return size;
return LogicalPixelSize(wm, size);
}

// Per the spec, non-replaced inline Elements will always have an empty
Expand All @@ -108,47 +108,50 @@ static gfx::Size CalculateBoxSize(Element* aTarget,
// always return false. (So its observation won't be fired.)
if (!frame->IsFrameOfType(nsIFrame::eReplaced) &&
frame->IsFrameOfType(nsIFrame::eLineParticipant)) {
return size;
return LogicalPixelSize();
}

switch (aBox) {
case ResizeObserverBoxOptions::Border_box:
return CSSPixel::FromAppUnits(frame->GetSize()).ToUnknownSize();
case ResizeObserverBoxOptions::Device_pixel_content_box: {
// Simply converting from app units to device units is insufficient - we
// need to take subpixel snapping into account. Subpixel snapping happens
// with respect to the reference frame, so do the dev pixel conversion
// with our rectangle positioned relative to the reference frame, then
// get the size from there.
const auto* referenceFrame = nsLayoutUtils::GetReferenceFrame(frame);
// GetOffsetToCrossDoc version handles <iframe>s in addition to normal
// cases. We don't expect this to tight loop for additional checks to
// matter.
const auto offset = frame->GetOffsetToCrossDoc(referenceFrame);
const auto contentSize = GetContentRectSize(*frame);
// Casting to double here is deliberate to minimize rounding error in
// upcoming operations.
const auto appUnitsPerDevPixel =
static_cast<double>(frame->PresContext()->AppUnitsPerDevPixel());
// Calculation here is a greatly simplified version of
// `NSRectToSnappedRect` as 1) we're not actually drawing (i.e. no draw
// target), and 2) transform does not need to be taken into account.
gfx::Rect rect{gfx::Float(offset.X() / appUnitsPerDevPixel),
gfx::Float(offset.Y() / appUnitsPerDevPixel),
gfx::Float(contentSize.Width() / appUnitsPerDevPixel),
gfx::Float(contentSize.Height() / appUnitsPerDevPixel)};
gfx::Point tl = rect.TopLeft().Round();
gfx::Point br = rect.BottomRight().Round();

rect.SizeTo(gfx::Size(br.x - tl.x, br.y - tl.y));
rect.NudgeToIntegers();
return rect.Size().ToUnknownSize();
auto GetFrameSize = [&](nsIFrame* aFrame) {
switch (aBox) {
case ResizeObserverBoxOptions::Border_box:
return CSSPixel::FromAppUnits(frame->GetSize()).ToUnknownSize();
case ResizeObserverBoxOptions::Device_pixel_content_box: {
// Simply converting from app units to device units is insufficient - we
// need to take subpixel snapping into account. Subpixel snapping
// happens with respect to the reference frame, so do the dev pixel
// conversion with our rectangle positioned relative to the reference
// frame, then get the size from there.
const auto* referenceFrame = nsLayoutUtils::GetReferenceFrame(frame);
// GetOffsetToCrossDoc version handles <iframe>s in addition to normal
// cases. We don't expect this to tight loop for additional checks to
// matter.
const auto offset = frame->GetOffsetToCrossDoc(referenceFrame);
const auto contentSize = GetContentRectSize(*frame);
// Casting to double here is deliberate to minimize rounding error in
// upcoming operations.
const auto appUnitsPerDevPixel =
static_cast<double>(frame->PresContext()->AppUnitsPerDevPixel());
// Calculation here is a greatly simplified version of
// `NSRectToSnappedRect` as 1) we're not actually drawing (i.e. no draw
// target), and 2) transform does not need to be taken into account.
gfx::Rect rect{gfx::Float(offset.X() / appUnitsPerDevPixel),
gfx::Float(offset.Y() / appUnitsPerDevPixel),
gfx::Float(contentSize.Width() / appUnitsPerDevPixel),
gfx::Float(contentSize.Height() / appUnitsPerDevPixel)};
gfx::Point tl = rect.TopLeft().Round();
gfx::Point br = rect.BottomRight().Round();

rect.SizeTo(gfx::Size(br.x - tl.x, br.y - tl.y));
rect.NudgeToIntegers();
return rect.Size().ToUnknownSize();
}
case ResizeObserverBoxOptions::Content_box:
default:
break;
}
case ResizeObserverBoxOptions::Content_box:
default:
break;
}
return CSSPixel::FromAppUnits(GetContentRectSize(*frame)).ToUnknownSize();
return CSSPixel::FromAppUnits(GetContentRectSize(*frame)).ToUnknownSize();
};
return LogicalPixelSize(frame->GetWritingMode(), GetFrameSize(frame));
}

NS_IMPL_CYCLE_COLLECTION_CLASS(ResizeObservation)
Expand Down Expand Up @@ -189,23 +192,18 @@ void ResizeObservation::Unlink(RemoveFromObserver aRemoveFromObserver) {
}

bool ResizeObservation::IsActive() const {
nsIFrame* frame = mTarget->GetPrimaryFrame();

// As detailed in the css-contain specification, if the target is hidden by
// `content-visibility` it should not call its ResizeObservation callbacks.
nsIFrame* frame = mTarget->GetPrimaryFrame();
if (frame && frame->IsHiddenByContentVisibilityOnAnyAncestor()) {
return false;
}

const WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
const LogicalPixelSize size(wm, CalculateBoxSize(mTarget, mObservedBox));
return mLastReportedSize != size;
return mLastReportedSize != CalculateBoxSize(mTarget, mObservedBox);
}

void ResizeObservation::UpdateLastReportedSize(const gfx::Size& aSize) {
nsIFrame* frame = mTarget->GetPrimaryFrame();
WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
mLastReportedSize = {wm, aSize};
void ResizeObservation::UpdateLastReportedSize(const LogicalPixelSize& aSize) {
mLastReportedSize = aSize;
}

// Only needed for refcounted objects.
Expand Down Expand Up @@ -310,7 +308,8 @@ void ResizeObserver::Observe(Element& aTarget,
// See https://github.com/w3c/csswg-drafts/issues/3664 about doing this in
// the general case, then we won't need this hack for the last remembered
// size, and will have consistency with IntersectionObserver.
observation->UpdateLastReportedSize(gfx::Size(-1, -1));
observation->UpdateLastReportedSize(
LogicalPixelSize(WritingMode(), gfx::Size(-1, -1)));
MOZ_ASSERT(observation->IsActive());
}
mObservationList.insertBack(observation);
Expand Down Expand Up @@ -382,11 +381,11 @@ uint32_t ResizeObserver::BroadcastActiveObservations() {
for (auto& observation : mActiveTargets) {
Element* target = observation->Target();

gfx::Size borderBoxSize =
LogicalPixelSize borderBoxSize =
CalculateBoxSize(target, ResizeObserverBoxOptions::Border_box);
gfx::Size contentBoxSize =
LogicalPixelSize contentBoxSize =
CalculateBoxSize(target, ResizeObserverBoxOptions::Content_box);
gfx::Size devicePixelContentBoxSize = CalculateBoxSize(
LogicalPixelSize devicePixelContentBoxSize = CalculateBoxSize(
target, ResizeObserverBoxOptions::Device_pixel_content_box);
RefPtr<ResizeObserverEntry> entry =
new ResizeObserverEntry(mOwner, *target, borderBoxSize, contentBoxSize,
Expand Down Expand Up @@ -479,34 +478,32 @@ void ResizeObserverEntry::GetDevicePixelContentBoxSize(
aRetVal.AppendElement(mDevicePixelContentBoxSize);
}

void ResizeObserverEntry::SetBorderBoxSize(const gfx::Size& aSize) {
nsIFrame* frame = mTarget->GetPrimaryFrame();
const WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
mBorderBoxSize = new ResizeObserverSize(mOwner, aSize, wm);
void ResizeObserverEntry::SetBorderBoxSize(const LogicalPixelSize& aSize) {
mBorderBoxSize = new ResizeObserverSize(mOwner, aSize);
}

void ResizeObserverEntry::SetContentRectAndSize(const gfx::Size& aSize) {
void ResizeObserverEntry::SetContentRectAndSize(const LogicalPixelSize& aSize) {
nsIFrame* frame = mTarget->GetPrimaryFrame();

// 1. 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.
const WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
gfx::Size sizeForRect = aSize.PhysicalSize(wm);
nsRect rect(nsPoint(padding.left, padding.top),
CSSPixel::ToAppUnits(CSSSize::FromUnknownSize(aSize)));
CSSPixel::ToAppUnits(CSSSize::FromUnknownSize(sizeForRect)));
RefPtr<DOMRect> contentRect = new DOMRect(mOwner);
contentRect->SetLayoutRect(rect);
mContentRect = std::move(contentRect);

// 2. Update mContentBoxSize.
const WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
mContentBoxSize = new ResizeObserverSize(mOwner, aSize, wm);
mContentBoxSize = new ResizeObserverSize(mOwner, aSize);
}

void ResizeObserverEntry::SetDevicePixelContentSize(const gfx::Size& aSize) {
nsIFrame* frame = mTarget->GetPrimaryFrame();
const WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
mDevicePixelContentBoxSize = new ResizeObserverSize(mOwner, aSize, wm);
void ResizeObserverEntry::SetDevicePixelContentSize(
const LogicalPixelSize& aSize) {
mDevicePixelContentBoxSize = new ResizeObserverSize(mOwner, aSize);
}

static void LastRememberedSizeCallback(
Expand Down
28 changes: 18 additions & 10 deletions dom/base/ResizeObserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ class LogicalPixelSize {
}
}

gfx::Size PhysicalSize(WritingMode aWM) const {
if (!aWM.IsVertical()) {
return mSize;
}
gfx::Size result(mSize);
std::swap(result.width, result.height);
return result;
}

bool operator==(const LogicalPixelSize& aOther) const {
return mSize == aOther.mSize;
}
Expand Down Expand Up @@ -86,7 +95,7 @@ class ResizeObservation final : public LinkedListElement<ResizeObservation> {
/**
* Update current mLastReportedSize with size from aSize.
*/
void UpdateLastReportedSize(const gfx::Size& aSize);
void UpdateLastReportedSize(const LogicalPixelSize& aSize);

enum class RemoveFromObserver : bool { No, Yes };
void Unlink(RemoveFromObserver);
Expand Down Expand Up @@ -220,9 +229,9 @@ class ResizeObserverEntry final : public nsISupports, public nsWrapperCache {
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ResizeObserverEntry)

ResizeObserverEntry(nsISupports* aOwner, Element& aTarget,
const gfx::Size& aBorderBoxSize,
const gfx::Size& aContentBoxSize,
const gfx::Size& aDevicePixelContentBoxSize)
const LogicalPixelSize& aBorderBoxSize,
const LogicalPixelSize& aContentBoxSize,
const LogicalPixelSize& aDevicePixelContentBoxSize)
: mOwner(aOwner), mTarget(&aTarget) {
MOZ_ASSERT(mOwner, "Need a non-null owner");
MOZ_ASSERT(mTarget, "Need a non-null target element");
Expand Down Expand Up @@ -260,11 +269,11 @@ class ResizeObserverEntry final : public nsISupports, public nsWrapperCache {
~ResizeObserverEntry() = default;

// Set borderBoxSize.
void SetBorderBoxSize(const gfx::Size& aSize);
void SetBorderBoxSize(const LogicalPixelSize& aSize);
// Set contentRect and contentBoxSize.
void SetContentRectAndSize(const gfx::Size& aSize);
void SetContentRectAndSize(const LogicalPixelSize& aSize);
// Set devicePixelContentBoxSize.
void SetDevicePixelContentSize(const gfx::Size& aSize);
void SetDevicePixelContentSize(const LogicalPixelSize& aSize);

nsCOMPtr<nsISupports> mOwner;
nsCOMPtr<Element> mTarget;
Expand All @@ -280,9 +289,8 @@ class ResizeObserverSize final : public nsISupports, public nsWrapperCache {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ResizeObserverSize)

ResizeObserverSize(nsISupports* aOwner, const gfx::Size& aSize,
const WritingMode aWM)
: mOwner(aOwner), mSize(aWM, aSize) {
ResizeObserverSize(nsISupports* aOwner, const LogicalPixelSize& aSize)
: mOwner(aOwner), mSize(aSize) {
MOZ_ASSERT(mOwner, "Need a non-null owner");
}

Expand Down

0 comments on commit ac52d72

Please sign in to comment.