зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
18526d61b5
Коммит
947e858c46
|
@ -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,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче