Commit 6b574c59 authored by Boris Chiou's avatar Boris Chiou
Browse files

Bug 1587973 - Part 3: Support device-pixel-content-box for ResizeObserver. r=emilio

This patch implements device-pixel-content-box for ResizeObserver.
GetTargetSize() returns CSS pixels for {border|content}-box, or device
pixels for device-pixel-content-box. We round the device pixel to
integral based on the spec,
https://drafts.csswg.org/resize-observer/#calculate-box-size.
And then we compare the current calculated box sizes and the last updated one
in IsActive().

Besides, the current wpts only use zoom property to verify this, but zoom
property is non-standard and we doesn't supports it, so now we only test the
getter functions for device-pixel-content-box and the subpixel snapping
algorithm (e.g. devicepixel.html) for Gecko in wpts.

Differential Revision: https://phabricator.services.mozilla.com/D120776
parent 64e925cd
Loading
Loading
Loading
Loading
+53 −3
Original line number Diff line number Diff line
@@ -62,9 +62,19 @@ static gfx::Size GetTargetSize(Element* aTarget,
    // Per the spec, 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.
    gfxRect bbox = SVGUtils::GetBBox(frame);
    const gfxRect bbox = SVGUtils::GetBBox(frame);
    size.width = static_cast<float>(bbox.width);
    size.height = static_cast<float>(bbox.height);
    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
      // result.
      // https://drafts.csswg.org/resize-observer/#dom-resizeobserverboxoptions-device-pixel-content-box
      const LayoutDeviceIntSize snappedSize =
          RoundedToInt(CSSSize::FromUnknownSize(size) *
                       frame->PresContext()->CSSToDevPixelScale());
      size = gfx::Size(snappedSize.ToUnknownSize());
    }
  } else {
    // Per the spec, non-replaced inline Elements will always have an empty
    // content rect. Therefore, we always use the same trivially-empty size
@@ -80,6 +90,21 @@ static gfx::Size GetTargetSize(Element* aTarget,
        // GetSize() includes the content area, borders, and padding.
        size = CSSPixel::FromAppUnits(frame->GetSize()).ToUnknownSize();
        break;
      case ResizeObserverBoxOptions::Device_pixel_content_box: {
        // This is a implementation-dependent for subpixel snapping algorithm.
        // Gecko relys on LayoutDevicePixel to convert (and snap) the app units
        // into device pixels in painting and gfx code, so here we simply
        // convert it into dev pixels and round it.
        //
        // Note: This size must contain integer values.
        // https://drafts.csswg.org/resize-observer/#dom-resizeobserverboxoptions-device-pixel-content-box
        const LayoutDeviceIntSize snappedSize =
            LayoutDevicePixel::FromAppUnitsRounded(
                frame->GetContentRectRelativeToSelf().Size(),
                frame->PresContext()->AppUnitsPerDevPixel());
        size = gfx::Size(snappedSize.ToUnknownSize());
        break;
      }
      case ResizeObserverBoxOptions::Content_box:
      default:
        size =
@@ -295,8 +320,11 @@ uint32_t ResizeObserver::BroadcastActiveObservations() {
        GetTargetSize(target, ResizeObserverBoxOptions::Border_box);
    gfx::Size contentBoxSize =
        GetTargetSize(target, ResizeObserverBoxOptions::Content_box);
    gfx::Size devicePixelContentBoxSize = GetTargetSize(
        target, ResizeObserverBoxOptions::Device_pixel_content_box);
    RefPtr<ResizeObserverEntry> entry =
        new ResizeObserverEntry(this, *target, borderBoxSize, contentBoxSize);
        new ResizeObserverEntry(this, *target, borderBoxSize, contentBoxSize,
                                devicePixelContentBoxSize);

    if (!entries.AppendElement(entry.forget(), fallible)) {
      // Out of memory.
@@ -309,6 +337,9 @@ uint32_t ResizeObserver::BroadcastActiveObservations() {
      case ResizeObserverBoxOptions::Border_box:
        observation->UpdateLastReportedSize(borderBoxSize);
        break;
      case ResizeObserverBoxOptions::Device_pixel_content_box:
        observation->UpdateLastReportedSize(devicePixelContentBoxSize);
        break;
      case ResizeObserverBoxOptions::Content_box:
      default:
        observation->UpdateLastReportedSize(contentBoxSize);
@@ -332,7 +363,8 @@ uint32_t ResizeObserver::BroadcastActiveObservations() {

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverEntry, mOwner, mTarget,
                                      mContentRect, mBorderBoxSize,
                                      mContentBoxSize)
                                      mContentBoxSize,
                                      mDevicePixelContentBoxSize)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverEntry)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverEntry)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ResizeObserverEntry)
@@ -364,6 +396,18 @@ void ResizeObserverEntry::GetContentBoxSize(
  aRetVal.AppendElement(mContentBoxSize);
}

void ResizeObserverEntry::GetDevicePixelContentBoxSize(
    nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const {
  // In the resize-observer-1 spec, there will only be a single
  // ResizeObserverSize returned in the FrozenArray for now.
  //
  // Note: the usage of FrozenArray is to support elements that have multiple
  // fragments, which occur in multi-column scenarios.
  // https://drafts.csswg.org/resize-observer/#resize-observer-entry-interface
  aRetVal.Clear();
  aRetVal.AppendElement(mDevicePixelContentBoxSize);
}

void ResizeObserverEntry::SetBorderBoxSize(const gfx::Size& aSize) {
  nsIFrame* frame = mTarget->GetPrimaryFrame();
  const WritingMode wm = frame ? frame->GetWritingMode() : WritingMode();
@@ -388,6 +432,12 @@ void ResizeObserverEntry::SetContentRectAndSize(const gfx::Size& aSize) {
  mContentBoxSize = new ResizeObserverSize(this, aSize, wm);
}

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

NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ResizeObserverSize, mOwner)
NS_IMPL_CYCLE_COLLECTING_ADDREF(ResizeObserverSize)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ResizeObserverSize)
+10 −3
Original line number Diff line number Diff line
@@ -212,13 +212,15 @@ class ResizeObserverEntry final : public nsISupports, public nsWrapperCache {

  ResizeObserverEntry(nsISupports* aOwner, Element& aTarget,
                      const gfx::Size& aBorderBoxSize,
                      const gfx::Size& aContentBoxSize)
                      const gfx::Size& aContentBoxSize,
                      const gfx::Size& aDevicePixelContentBoxSize)
      : mOwner(aOwner), mTarget(&aTarget) {
    MOZ_ASSERT(mOwner, "Need a non-null owner");
    MOZ_ASSERT(mTarget, "Need a non-null target element");

    SetBorderBoxSize(aBorderBoxSize);
    SetContentRectAndSize(aContentBoxSize);
    SetDevicePixelContentSize(aDevicePixelContentBoxSize);
  }

  nsISupports* GetParentObject() const { return mOwner; }
@@ -237,11 +239,13 @@ class ResizeObserverEntry final : public nsISupports, public nsWrapperCache {
  DOMRectReadOnly* ContentRect() const { return mContentRect; }

  /**
   * Returns target's logical border-box size and content-box size as
   * ResizeObserverSize.
   * Returns target's logical border-box size, content-box size, and
   * device-pixel-content-box as an array of ResizeObserverSize.
   */
  void GetBorderBoxSize(nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const;
  void GetContentBoxSize(nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const;
  void GetDevicePixelContentBoxSize(
      nsTArray<RefPtr<ResizeObserverSize>>& aRetVal) const;

 private:
  ~ResizeObserverEntry() = default;
@@ -250,6 +254,8 @@ class ResizeObserverEntry final : public nsISupports, public nsWrapperCache {
  void SetBorderBoxSize(const gfx::Size& aSize);
  // Set contentRect and contentBoxSize.
  void SetContentRectAndSize(const gfx::Size& aSize);
  // Set devicePixelContentBoxSize.
  void SetDevicePixelContentSize(const gfx::Size& aSize);

  nsCOMPtr<nsISupports> mOwner;
  nsCOMPtr<Element> mTarget;
@@ -257,6 +263,7 @@ class ResizeObserverEntry final : public nsISupports, public nsWrapperCache {
  RefPtr<DOMRectReadOnly> mContentRect;
  RefPtr<ResizeObserverSize> mBorderBoxSize;
  RefPtr<ResizeObserverSize> mContentBoxSize;
  RefPtr<ResizeObserverSize> mDevicePixelContentBoxSize;
};

class ResizeObserverSize final : public nsISupports, public nsWrapperCache {
+4 −1
Original line number Diff line number Diff line
@@ -9,7 +9,8 @@

enum ResizeObserverBoxOptions {
    "border-box",
    "content-box"
    "content-box",
    "device-pixel-content-box"
};

dictionary ResizeObserverOptions {
@@ -42,6 +43,8 @@ interface ResizeObserverEntry {
    readonly attribute sequence<ResizeObserverSize> borderBoxSize;
    [Frozen, Cached, Pure]
    readonly attribute sequence<ResizeObserverSize> contentBoxSize;
    [Frozen, Cached, Pure]
    readonly attribute sequence<ResizeObserverSize> devicePixelContentBoxSize;
};

[Pref="layout.css.resizeobserver.enabled",
+2 −3
Original line number Diff line number Diff line
implementation-status: backlog
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1587973
[devicepixel.html]
  expected: FAIL
  fuzzy: maxDifference=0-2;totalPixels=0-1391
  bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1723618
+0 −6
Original line number Diff line number Diff line
[idlharness.window.html]
  [ResizeObserverEntry interface: entry must inherit property "devicePixelContentBoxSize" with the proper type]
    expected: FAIL

  [ResizeObserverEntry interface: attribute devicePixelContentBoxSize]
    expected: FAIL

  [ResizeObserverEntry must be primary interface of entry]
    expected:
      if (os == "linux") and debug and webrender and not fission: [PASS, FAIL]
Loading