зеркало из https://github.com/mozilla/gecko-dev.git
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
This commit is contained in:
Родитель
64e925cdb0
Коммит
6b574c5928
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
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
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
[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]
|
||||
|
|
|
@ -1,52 +1,46 @@
|
|||
implementation-status: backlog
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1587973
|
||||
[observe.html]
|
||||
expected: TIMEOUT
|
||||
expected:
|
||||
if (os == "android") and webrender: ["TIMEOUT", "OK"]
|
||||
[guard]
|
||||
expected: NOTRUN
|
||||
|
||||
[test6: iframe notifications]
|
||||
expected:
|
||||
if os == "android": [FAIL, PASS]
|
||||
[PASS, FAIL]
|
||||
|
||||
[test8: simple content-box observation]
|
||||
expected:
|
||||
if os == "linux": [FAIL, PASS]
|
||||
if os == "android": FAIL
|
||||
|
||||
[test13: an observation is fired after the change of writing mode when box's specified size comes from physical size properties.]
|
||||
expected:
|
||||
if os == "win": [FAIL, PASS]
|
||||
|
||||
|
||||
[test14: observe the same target but using a different box should override the previous one]
|
||||
expected:
|
||||
if os == "linux": [FAIL, PASS]
|
||||
if os == "win": [FAIL, PASS]
|
||||
|
||||
[test18: an observation is fired when device-pixel-content-box is being observed]
|
||||
expected: FAIL
|
||||
|
||||
[test8: simple content-box observation]
|
||||
expected:
|
||||
if os == "win": [FAIL, PASS]
|
||||
if os == "linux": [PASS, FAIL]
|
||||
|
||||
[test10: simple border-box observation]
|
||||
expected:
|
||||
if os == "linux": [FAIL, PASS]
|
||||
if os == "win": [FAIL, PASS]
|
||||
|
||||
[test17: Box sizing snd Resize Observer notifications]
|
||||
expected:
|
||||
if os == "linux": [FAIL, PASS]
|
||||
if os == "win": [FAIL, PASS]
|
||||
if (os == "android") and webrender: ["NOTRUN", "PASS"]
|
||||
|
||||
[test5: observe img]
|
||||
expected:
|
||||
if os == "win": [PASS, FAIL]
|
||||
|
||||
[test6: iframe notifications]
|
||||
expected:
|
||||
if os == "android": [FAIL, PASS]
|
||||
|
||||
[test8: simple content-box observation]
|
||||
expected:
|
||||
if os == "linux": [FAIL, PASS]
|
||||
if os == "win": [FAIL, PASS]
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1723619
|
||||
|
||||
[test9: simple content-box observation but keep border-box size unchanged]
|
||||
expected:
|
||||
if os == "win": [FAIL, PASS]
|
||||
|
||||
[test10: simple border-box observation]
|
||||
expected:
|
||||
if os == "linux": [PASS, FAIL]
|
||||
if os == "win": [FAIL, PASS]
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1723619
|
||||
|
||||
[test13: an observation is fired after the change of writing mode when box's specified size comes from physical size properties.]
|
||||
expected:
|
||||
if os == "win": [FAIL, PASS]
|
||||
|
||||
[test14: observe the same target but using a different box should override the previous one]
|
||||
expected:
|
||||
if os == "linux": [FAIL, PASS]
|
||||
if os == "win": [PASS, FAIL]
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1723619
|
||||
|
||||
[test17: Box sizing snd Resize Observer notifications]
|
||||
expected:
|
||||
if os == "linux": [FAIL, PASS]
|
||||
if os == "win": [FAIL, PASS]
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1723619
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
implementation-status: backlog
|
||||
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1587973
|
||||
[svg.html]
|
||||
expected: TIMEOUT
|
||||
|
||||
[test15: observe svg:text content and border box]
|
||||
expected:
|
||||
if os == "mac": FAIL
|
||||
|
||||
[guard]
|
||||
expected: NOTRUN
|
||||
|
||||
[test16: observe g:rect content, border and device-pixel-content boxes]
|
||||
expected: FAIL
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
// Use a linewidth of 2. Because the rectangle is drawn at 0,0 with
|
||||
// its dimensions being the same as canvas dimensions, linewidth as it
|
||||
// is drawn on the canvas will be 1.
|
||||
ctx.lineWidth = "2";
|
||||
ctx.lineWidth = window.devicePixelRatio * 2;
|
||||
ctx.strokeStyle = "green";
|
||||
ctx.rect(0, 0, snappedSize.inlineSize, snappedSize.blockSize);
|
||||
ctx.stroke();
|
||||
|
|
|
@ -878,6 +878,34 @@ function test18() {
|
|||
"target device-pixel-content-box block size");
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
return helper.start(() => t.remove());
|
||||
}
|
||||
|
||||
function test19() {
|
||||
// zoom is not a standard css property, so we should check it first. If the
|
||||
// browser doesn't support it, we skip this test.
|
||||
if (!CSS.supports("zoom", "3")) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let t = createAndAppendElement("div");
|
||||
t.style.height = "100px";
|
||||
t.style.width = "50px";
|
||||
|
||||
let helper = new ResizeTestHelper(
|
||||
"test19: an observation is fired when device-pixel-content-box is being " +
|
||||
"observed and zoom change",
|
||||
[
|
||||
{
|
||||
setup: observer => {
|
||||
observer.observe(t, {box: "device-pixel-content-box"});
|
||||
},
|
||||
notify: entries => {
|
||||
// No need to test again (see test18), so skip this event loop.
|
||||
}
|
||||
},
|
||||
{
|
||||
setup: observer => {
|
||||
document.body.style.zoom = 3;
|
||||
|
@ -931,6 +959,7 @@ test0()
|
|||
.then(() => test16())
|
||||
.then(() => test17())
|
||||
.then(() => test18())
|
||||
.then(() => test19())
|
||||
.then(() => guard.done());
|
||||
|
||||
</script>
|
||||
|
|
|
@ -505,7 +505,6 @@ function test16() {
|
|||
setup: observer => {
|
||||
observer.observe(target, {box: "device-pixel-content-box"});
|
||||
target.setAttribute('width', 50);
|
||||
document.body.style.zoom = 0.1;
|
||||
},
|
||||
notify: (entries, observer) => {
|
||||
assert_equals(entries.length, 1);
|
||||
|
@ -515,16 +514,49 @@ function test16() {
|
|||
assert_equals(entries[0].contentBoxSize[0].blockSize, 20);
|
||||
assert_equals(entries[0].borderBoxSize[0].inlineSize, 50);
|
||||
assert_equals(entries[0].borderBoxSize[0].blockSize, 20);
|
||||
assert_equals(entries[0].devicePixelContentBoxSize[0].inlineSize, 5);
|
||||
assert_equals(entries[0].devicePixelContentBoxSize[0].blockSize, 2);
|
||||
assert_equals(entries[0].devicePixelContentBoxSize[0].inlineSize, 50);
|
||||
assert_equals(entries[0].devicePixelContentBoxSize[0].blockSize, 20);
|
||||
return true; // Delay next step
|
||||
}
|
||||
},
|
||||
{
|
||||
setup: observer => {
|
||||
observer.observe(target, {box: "device-pixel-content-box"});
|
||||
target.setAttribute('height', 30);
|
||||
},
|
||||
notify: (entries, observer) => {
|
||||
assert_equals(entries.length, 1);
|
||||
assert_equals(entries[0].contentRect.width, 50);
|
||||
assert_equals(entries[0].contentRect.height, 30);
|
||||
assert_equals(entries[0].contentBoxSize[0].inlineSize, 50);
|
||||
assert_equals(entries[0].contentBoxSize[0].blockSize, 30);
|
||||
assert_equals(entries[0].borderBoxSize[0].inlineSize, 50);
|
||||
assert_equals(entries[0].borderBoxSize[0].blockSize, 30);
|
||||
assert_equals(entries[0].devicePixelContentBoxSize[0].inlineSize, 50);
|
||||
assert_equals(entries[0].devicePixelContentBoxSize[0].blockSize, 30);
|
||||
}
|
||||
}
|
||||
]);
|
||||
return helper.start();
|
||||
}
|
||||
|
||||
function test17() {
|
||||
// zoom is not a standard css property, so we should check it first. If the
|
||||
// browser doesn't support it, we skip this test.
|
||||
if (!CSS.supports("zoom", "0.1")) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let target = document.querySelector('#g_rect');
|
||||
let helper = new ResizeTestHelper(
|
||||
"test17: observe g:rect content, border and device-pixel-content boxes with zoom",
|
||||
[
|
||||
{
|
||||
setup: observer => {
|
||||
observer.observe(target, {box: "device-pixel-content-box"});
|
||||
target.setAttribute('width', 50);
|
||||
target.setAttribute('height', 30);
|
||||
document.body.style.zoom = 0.1;
|
||||
},
|
||||
notify: (entries, observer) => {
|
||||
assert_equals(entries.length, 1);
|
||||
assert_equals(entries[0].contentRect.width, 50);
|
||||
|
@ -581,6 +613,7 @@ test0()
|
|||
.then(() => { return test14(); })
|
||||
.then(() => { return test15(); })
|
||||
.then(() => { return test16(); })
|
||||
.then(() => { return test17(); })
|
||||
.then(() => { guard.done(); });
|
||||
|
||||
</script>
|
||||
|
|
Загрузка…
Ссылка в новой задаче