Bug 1588622 - [remote] Implement "layoutViewport" and "contentSize" for Page.getLayoutMetrics. r=remote-protocol-reviewers,maja_zf

Differential Revision: https://phabricator.services.mozilla.com/D56911

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Henrik Skupin 2019-12-12 18:27:17 +00:00
Родитель 18526d61b5
Коммит 947e858c46
6 изменённых файлов: 281 добавлений и 50 удалений

Просмотреть файл

@ -91,15 +91,6 @@ class Page extends ContentProcessDomain {
}
}
_viewportRect() {
return new DOMRect(
this.content.pageXOffset,
this.content.pageYOffset,
this.content.innerWidth,
this.content.innerHeight
);
}
async navigate({ url, referrer, transitionType, frameId } = {}) {
if (frameId && frameId != this.content.windowUtils.outerWindowID) {
throw new UnsupportedError("frameId not supported");
@ -306,6 +297,44 @@ class Page extends ContentProcessDomain {
break;
}
}
_contentSize() {
const docEl = this.content.document.documentElement;
return {
x: 0,
y: 0,
width: docEl.scrollWidth,
height: docEl.scrollHeight,
};
}
_getScrollbarSize() {
const scrollbarHeight = {};
const scrollbarWidth = {};
this.content.windowUtils.getScrollbarSize(
false,
scrollbarWidth,
scrollbarHeight
);
return {
width: scrollbarWidth.value,
height: scrollbarHeight.value,
};
}
_layoutViewport() {
const scrollbarSize = this._getScrollbarSize();
return {
pageX: this.content.pageXOffset,
pageY: this.content.pageYOffset,
clientWidth: this.content.innerWidth - scrollbarSize.width,
clientHeight: this.content.innerHeight - scrollbarSize.height,
};
}
}
function transitionToLoadFlag(transitionType) {

Просмотреть файл

@ -72,31 +72,37 @@ class Page extends Domain {
const { browsingContext, window } = this.session.target;
const scale = window.devicePixelRatio;
const rect = await this.executeInChild("_viewportRect");
const rect = await this.executeInChild("_layoutViewport");
let canvasWidth = rect.width * scale;
let canvasHeight = rect.height * scale;
let canvasWidth = rect.clientWidth * scale;
let canvasHeight = rect.clientHeight * scale;
// Cap the screenshot size based on maximum allowed canvas sizes.
// Using higher dimensions would trigger exceptions in Gecko.
//
// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas#Maximum_canvas_size
if (canvasWidth > MAX_CANVAS_DIMENSION) {
rect.width = Math.floor(MAX_CANVAS_DIMENSION / scale);
canvasWidth = rect.width * scale;
rect.clientWidth = Math.floor(MAX_CANVAS_DIMENSION / scale);
canvasWidth = rect.clientWidth * scale;
}
if (canvasHeight > MAX_CANVAS_DIMENSION) {
rect.height = Math.floor(MAX_CANVAS_DIMENSION / scale);
canvasHeight = rect.height * scale;
rect.clientHeight = Math.floor(MAX_CANVAS_DIMENSION / scale);
canvasHeight = rect.clientHeight * scale;
}
// If the area is larger, reduce the height to keep the full width.
if (canvasWidth * canvasHeight > MAX_CANVAS_AREA) {
rect.height = Math.floor(MAX_CANVAS_AREA / (canvasWidth * scale));
canvasHeight = rect.height * scale;
rect.clientHeight = Math.floor(MAX_CANVAS_AREA / (canvasWidth * scale));
canvasHeight = rect.clientHeight * scale;
}
const captureRect = new DOMRect(
rect.pageX,
rect.pageY,
rect.clientWidth,
rect.clientHeight
);
const snapshot = await browsingContext.currentWindowGlobal.drawSnapshot(
rect,
captureRect,
scale,
"rgb(255,255,255)"
);
@ -165,6 +171,59 @@ class Page extends Domain {
TabManager.selectTab(tab);
}
/**
* Return metrics relating to the layouting of the page.
*
* The returned object contains the following entries:
*
* layoutViewport:
* {number} pageX
* Horizontal offset relative to the document (CSS pixels)
* {number} pageY
* Vertical offset relative to the document (CSS pixels)
* {number} clientWidth
* Width (CSS pixels), excludes scrollbar if present
* {number} clientHeight
* Height (CSS pixels), excludes scrollbar if present
*
* visualViewport:
* {number} offsetX
* Horizontal offset relative to the layout viewport (CSS pixels)
* {number} offsetY
* Vertical offset relative to the layout viewport (CSS pixels)
* {number} pageX
* Horizontal offset relative to the document (CSS pixels)
* {number} pageY
* Vertical offset relative to the document (CSS pixels)
* {number} clientWidth
* Width (CSS pixels), excludes scrollbar if present
* {number} clientHeight
* Height (CSS pixels), excludes scrollbar if present
* {number} scale
* Scale relative to the ideal viewport (size at width=device-width)
* {number} zoom
* Page zoom factor (CSS to device independent pixels ratio)
*
* contentSize:
* {number} x
* X coordinate
* {number} y
* Y coordinate
* {number} width
* Width of scrollable area
* {number} height
* Height of scrollable area
*
* @return {Promise}
* @resolves {layoutViewport, visualViewport, contentSize}
*/
async getLayoutMetrics() {
return {
layoutViewport: await this.executeInChild("_layoutViewport"),
contentSize: await this.executeInChild("_contentSize"),
};
}
/**
* Interact with the currently opened JavaScript dialog (alert, confirm,
* prompt) for this page. This will always close the dialog, either accepting

Просмотреть файл

@ -10,6 +10,7 @@ support-files =
[browser_createIsolatedWorld.js]
[browser_frameNavigated.js]
[browser_frameNavigated_iframe.js]
[browser_getLayoutMetrics.js]
[browser_javascriptDialog_alert.js]
[browser_javascriptDialog_beforeunload.js]
[browser_javascriptDialog_confirm.js]

Просмотреть файл

@ -11,18 +11,14 @@ add_task(async function documentSmallerThanViewport({ Page }) {
ok(!!data, "Screenshot data is not empty");
const scale = await getDevicePixelRatio();
const viewportRect = await getViewportRect();
const viewport = await getViewportSize();
const { mimeType, width, height } = await getImageDetails(data);
is(mimeType, "image/png", "Screenshot has correct MIME type");
is(
width,
(viewportRect.width - viewportRect.left) * scale,
"Image has expected width"
);
is(width, (viewport.width - viewport.x) * scale, "Image has expected width");
is(
height,
(viewportRect.height - viewportRect.top) * scale,
(viewport.height - viewport.y) * scale,
"Image has expected height"
);
});
@ -35,18 +31,19 @@ add_task(async function documentLargerThanViewport({ Page }) {
ok(!!data, "Screenshot data is not empty");
const scale = await getDevicePixelRatio();
const viewportRect = await getViewportRect();
const scrollbarSize = await getScrollbarSize();
const viewport = await getViewportSize();
const { mimeType, width, height } = await getImageDetails(data);
is(mimeType, "image/png", "Screenshot has correct MIME type");
is(
width,
(viewportRect.width - viewportRect.left) * scale,
(viewport.width - viewport.x - scrollbarSize.width) * scale,
"Image has expected width"
);
is(
height,
(viewportRect.height - viewportRect.top) * scale,
(viewport.height - viewport.y - scrollbarSize.height) * scale,
"Image has expected height"
);
});
@ -71,12 +68,12 @@ add_task(async function asJPEGFormat({ Page }) {
ok(!!data, "Screenshot data is not empty");
const scale = await getDevicePixelRatio();
const viewportRect = await getViewportRect();
const viewport = await getViewportSize();
const { mimeType, height, width } = await getImageDetails(data);
is(mimeType, "image/jpeg", "Screenshot has correct MIME type");
is(width, (viewportRect.width - viewportRect.left) * scale);
is(height, (viewportRect.height - viewportRect.top) * scale);
is(width, (viewport.width - viewport.x) * scale);
is(height, (viewport.height - viewport.y) * scale);
});
add_task(async function asJPEGFormatAndQuality({ Page }) {
@ -120,17 +117,17 @@ add_task(async function asJPEGFormatAndQuality({ Page }) {
);
const scale = await getDevicePixelRatio();
const viewportRect = await getViewportRect();
const viewport = await getViewportSize();
// Images are all of the same dimension
is(infoDefault.width, (viewportRect.width - viewportRect.left) * scale);
is(infoDefault.height, (viewportRect.height - viewportRect.top) * scale);
is(infoDefault.width, (viewport.width - viewport.x) * scale);
is(infoDefault.height, (viewport.height - viewport.y) * scale);
is(info100.width, (viewportRect.width - viewportRect.left) * scale);
is(info100.height, (viewportRect.height - viewportRect.top) * scale);
is(info100.width, (viewport.width - viewport.x) * scale);
is(info100.height, (viewport.height - viewport.y) * scale);
is(info10.width, (viewportRect.width - viewportRect.left) * scale);
is(info10.height, (viewportRect.height - viewportRect.top) * scale);
is(info10.width, (viewport.width - viewport.x) * scale);
is(info10.height, (viewport.height - viewport.y) * scale);
// Images of different quality result in different content sizes
ok(
@ -198,14 +195,3 @@ function getMimeType(image) {
throw new Error("Unknown MIME type");
}
}
async function getViewportRect() {
return ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
return {
left: content.pageXOffset,
top: content.pageYOffset,
width: content.innerWidth,
height: content.innerHeight,
};
});
}

Просмотреть файл

@ -0,0 +1,115 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function documentSmallerThanViewport({ Page }) {
await loadURL(toDataURL("<div>Hello world"));
const { contentSize, layoutViewport } = await Page.getLayoutMetrics();
await checkContentSize(contentSize);
await checkLayoutViewport(layoutViewport);
is(
contentSize.x,
layoutViewport.pageX,
"X position of content is equal to layout viewport"
);
is(
contentSize.y,
layoutViewport.pageY,
"Y position of content is equal to layout viewport"
);
ok(
contentSize.width <= layoutViewport.clientWidth,
"Width of content is smaller than the layout viewport"
);
ok(
contentSize.height <= layoutViewport.clientHeight,
"Height of content is smaller than the layout viewport"
);
});
add_task(async function documentLargerThanViewport({ Page }) {
await loadURL(toDataURL("<div style='margin: 150vh 0 0 150vw'>Hello world"));
const { contentSize, layoutViewport } = await Page.getLayoutMetrics();
await checkContentSize(contentSize);
await checkLayoutViewport(layoutViewport, { scrollbars: true });
is(
contentSize.x,
layoutViewport.pageX,
"X position of content is equal to layout viewport"
);
is(
contentSize.y,
layoutViewport.pageY,
"Y position of content is equal to layout viewport"
);
ok(
contentSize.width > layoutViewport.clientWidth,
"Width of content is larger than the layout viewport"
);
ok(
contentSize.height > layoutViewport.clientHeight,
"Height of content is larger than the layout viewport"
);
});
add_task(async function documentLargerThanViewportScrolledXY({ Page }) {
await loadURL(toDataURL("<div style='margin: 150vh 0 0 150vw'>Hello world"));
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
content.scrollTo(50, 100);
});
const { contentSize, layoutViewport } = await Page.getLayoutMetrics();
await checkContentSize(contentSize);
await checkLayoutViewport(layoutViewport, { scrollbars: true });
is(
layoutViewport.pageX,
contentSize.x + 50,
"X position of content is equal to layout viewport"
);
is(
layoutViewport.pageY,
contentSize.y + 100,
"Y position of content is equal to layout viewport"
);
ok(
contentSize.width > layoutViewport.clientWidth,
"Width of content is larger than the layout viewport"
);
ok(
contentSize.height > layoutViewport.clientHeight,
"Height of content is larger than the layout viewport"
);
});
async function checkContentSize(rect) {
const expected = await getContentSize();
is(rect.x, expected.x, "Expected x position returned");
is(rect.y, expected.y, "Expected y position returned");
is(rect.width, expected.width, "Expected width returned");
is(rect.height, expected.height, "Expected height returned");
}
async function checkLayoutViewport(viewport, options = {}) {
const { scrollbars = false } = options;
const expected = await getViewportSize();
if (scrollbars) {
const { width, height } = await getScrollbarSize();
expected.width -= width;
expected.height -= height;
}
is(viewport.pageX, expected.x, "Expected x position returned");
is(viewport.pageY, expected.y, "Expected y position returned");
is(viewport.clientWidth, expected.width, "Expected width returned");
is(viewport.clientHeight, expected.height, "Expected height returned");
}

Просмотреть файл

@ -9,3 +9,44 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/remote/test/browser/head.js",
this
);
async function getContentSize() {
return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const docEl = content.document.documentElement;
return {
x: 0,
y: 0,
width: docEl.scrollWidth,
height: docEl.scrollHeight,
};
});
}
async function getViewportSize() {
return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
return {
x: content.pageXOffset,
y: content.pageYOffset,
width: content.innerWidth,
height: content.innerHeight,
};
});
}
async function getScrollbarSize() {
return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const scrollbarHeight = {};
const scrollbarWidth = {};
content.windowUtils.getScrollbarSize(
false,
scrollbarWidth,
scrollbarHeight
);
return {
width: scrollbarWidth.value,
height: scrollbarHeight.value,
};
});
}