зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1407366 - Part 4: Adding a test case for testing letterboxing. r=johannh
This patch adds a test for ensuring the letterboxing works as we expect. It will open a tab and resize its window into several different sizes and to see if the margins are correctly apply. And it will also check that no margin should apply to a tab with chrome privilege. --HG-- extra : rebase_source : 45e5c93b1845c9c9ca9b35b74f8a0b70c93a5bef
This commit is contained in:
Родитель
eed36e8544
Коммит
79972aef69
|
@ -11,6 +11,7 @@ support-files =
|
|||
head.js
|
||||
|
||||
[browser_block_mozAddonManager.js]
|
||||
[browser_dynamical_window_rounding.js]
|
||||
[browser_navigator.js]
|
||||
[browser_netInfo.js]
|
||||
[browser_performanceAPI.js]
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Bug 1407366 - A test case for reassuring the size of the content viewport is
|
||||
* rounded if the window is resized when letterboxing is enabled.
|
||||
*/
|
||||
|
||||
const TEST_PATH = "http://example.net/browser/browser/components/resistfingerprinting/test/browser/";
|
||||
|
||||
const DEFAULT_ROUNDED_WIDTH_STEP = 200;
|
||||
const DEFAULT_ROUNDED_HEIGHT_STEP = 100;
|
||||
|
||||
// A set of test cases which defines the width and the height of the outer window.
|
||||
const TEST_CASES = [
|
||||
{width: 1250, height: 1000},
|
||||
{width: 1500, height: 1050},
|
||||
{width: 1120, height: 760},
|
||||
{width: 800, height: 600},
|
||||
{width: 640, height: 400},
|
||||
{width: 500, height: 350},
|
||||
{width: 300, height: 170},
|
||||
];
|
||||
|
||||
function getPlatform() {
|
||||
const {OS} = Services.appinfo;
|
||||
if (OS == "WINNT") {
|
||||
return "win";
|
||||
} else if (OS == "Darwin") {
|
||||
return "mac";
|
||||
}
|
||||
return "linux";
|
||||
}
|
||||
|
||||
function handleOSFuzziness(aContent, aTarget) {
|
||||
/*
|
||||
* On Windows, we observed off-by-one pixel differences that
|
||||
* couldn't be expained. When manually setting the window size
|
||||
* to try to reproduce it; it did not occur.
|
||||
*/
|
||||
if (getPlatform() == "win") {
|
||||
return Math.abs(aContent - aTarget) <= 1;
|
||||
}
|
||||
return aContent == aTarget;
|
||||
}
|
||||
|
||||
function checkForDefaultSetting(
|
||||
aContentWidth, aContentHeight, aRealWidth, aRealHeight) {
|
||||
// The default behavior for rounding is to round window with 200x100 stepping.
|
||||
// So, we can get the rounded size by subtracting the remainder.
|
||||
let targetWidth = aRealWidth - (aRealWidth % DEFAULT_ROUNDED_WIDTH_STEP);
|
||||
let targetHeight = aRealHeight - (aRealHeight % DEFAULT_ROUNDED_HEIGHT_STEP);
|
||||
|
||||
// This platform-specific code is explained in the large comment below.
|
||||
if (getPlatform() != "linux") {
|
||||
ok(handleOSFuzziness(aContentWidth, targetWidth),
|
||||
`Default Dimensions: The content window width is correctly rounded into. ${aRealWidth}px -> ${aContentWidth}px should equal ${targetWidth}px`);
|
||||
|
||||
ok(handleOSFuzziness(aContentHeight, targetHeight),
|
||||
`Default Dimensions: The content window height is correctly rounded into. ${aRealHeight}px -> ${aContentHeight}px should equal ${targetHeight}px`);
|
||||
|
||||
// Using ok() above will cause Win/Mac to fail on even the first test, we don't need to repeat it, return true so waitForCondition ends
|
||||
return true;
|
||||
}
|
||||
// Returning true or false depending on if the test succeeded will cause Linux to repeat until it succeeds.
|
||||
return handleOSFuzziness(aContentWidth, targetWidth) && handleOSFuzziness(aContentHeight, targetHeight);
|
||||
}
|
||||
|
||||
async function test_dynamical_window_rounding(aWindow, aCheckFunc) {
|
||||
// We need to wait for the updating the margins for the newly opened tab, or
|
||||
// it will affect the following tests.
|
||||
let promiseForTheFirstRounding =
|
||||
TestUtils.topicObserved("test:letterboxing:update-margin-finish");
|
||||
|
||||
info("Open a content tab for testing.");
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
aWindow.gBrowser, TEST_PATH + "file_dummy.html");
|
||||
|
||||
info("Wait until the margins are applied for the opened tab.");
|
||||
await promiseForTheFirstRounding;
|
||||
|
||||
let getContainerSize = (aTab) => {
|
||||
let browserContainer = aWindow.gBrowser
|
||||
.getBrowserContainer(aTab.linkedBrowser);
|
||||
return {
|
||||
containerWidth: browserContainer.clientWidth,
|
||||
containerHeight: browserContainer.clientHeight,
|
||||
};
|
||||
};
|
||||
|
||||
for (let {width, height} of TEST_CASES) {
|
||||
let caseString = "Case " + width + "x" + height + ": ";
|
||||
// Create a promise for waiting for the margin update.
|
||||
let promiseRounding =
|
||||
TestUtils.topicObserved("test:letterboxing:update-margin-finish");
|
||||
|
||||
let {containerWidth, containerHeight} = getContainerSize(tab);
|
||||
|
||||
info(caseString + "Resize the window and wait until resize event happened (currently " +
|
||||
containerWidth + "x" + containerHeight + ")");
|
||||
await new Promise(resolve => {
|
||||
({containerWidth, containerHeight} = getContainerSize(tab));
|
||||
info(caseString + "Resizing (currently " + containerWidth + "x" + containerHeight + ")");
|
||||
|
||||
aWindow.onresize = () => {
|
||||
({containerWidth, containerHeight} = getContainerSize(tab));
|
||||
info(caseString + "Resized (currently " + containerWidth + "x" + containerHeight + ")");
|
||||
if (getPlatform() == "linux" && containerWidth != width) {
|
||||
/*
|
||||
* We observed frequent test failures that resulted from receiving an onresize
|
||||
* event where the browser was resized to an earlier requested dimension. This
|
||||
* resize event happens on Linux only, and is an artifact of the asynchronous
|
||||
* resizing. (See more discussion on 1407366#53)
|
||||
*
|
||||
* We cope with this problem in two ways.
|
||||
*
|
||||
* 1: If we detect that the browser was resized to the wrong value; we
|
||||
* redo the resize. (This is the lines of code immediately following this
|
||||
* comment)
|
||||
* 2: We repeat the test until it works using waitForCondition(). But we still
|
||||
* test Win/Mac more thoroughly: they do not loop in waitForCondition more
|
||||
* than once, and can fail the test on the first attempt (because their
|
||||
* check() functions use ok() while on Linux, we do not all ok() and instead
|
||||
* rely on waitForCondition to fail).
|
||||
*
|
||||
* The logging statements in this test, and RFPHelper.jsm, help narrow down and
|
||||
* illustrate the issue.
|
||||
*/
|
||||
info(caseString + "We hit the weird resize bug. Resize it again.");
|
||||
aWindow.resizeTo(width, height);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
aWindow.resizeTo(width, height);
|
||||
});
|
||||
|
||||
({containerWidth, containerHeight} = getContainerSize(tab));
|
||||
info(caseString + "Waiting until margin has been updated on browser element. (currently " +
|
||||
containerWidth + "x" + containerHeight + ")");
|
||||
await promiseRounding;
|
||||
|
||||
info(caseString + "Get innerWidth/Height from the content.");
|
||||
await BrowserTestUtils.waitForCondition(async () => {
|
||||
let {contentWidth, contentHeight} = await ContentTask.spawn(
|
||||
tab.linkedBrowser, null, () => {
|
||||
return {
|
||||
contentWidth: content.innerWidth,
|
||||
contentHeight: content.innerHeight,
|
||||
};
|
||||
});
|
||||
|
||||
info(caseString + "Check the result.");
|
||||
return aCheckFunc(contentWidth, contentHeight, containerWidth, containerHeight);
|
||||
}, "Default Dimensions: The content window width is correctly rounded into.");
|
||||
}
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
async function test_customize_width_and_height(aWindow) {
|
||||
const test_dimensions = `120x80, 200x143, 335x255, 600x312, 742x447, 813x558,
|
||||
990x672, 1200x733, 1470x858`;
|
||||
|
||||
await SpecialPowers.pushPrefEnv({"set":
|
||||
[
|
||||
["privacy.resistFingerprinting.letterboxing.dimensions", test_dimensions],
|
||||
],
|
||||
});
|
||||
|
||||
let dimensions_set = test_dimensions.split(",").map(item => {
|
||||
let sizes = item.split("x").map(size => parseInt(size, 10));
|
||||
|
||||
return {
|
||||
width: sizes[0],
|
||||
height: sizes[1],
|
||||
};
|
||||
});
|
||||
|
||||
let checkDimension =
|
||||
(aContentWidth, aContentHeight, aRealWidth, aRealHeight) => {
|
||||
let matchingArea = aRealWidth * aRealHeight;
|
||||
let minWaste = Number.MAX_SAFE_INTEGER;
|
||||
let targetDimensions = undefined;
|
||||
|
||||
// Find the dimensions which waste the least content area.
|
||||
for (let dim of dimensions_set) {
|
||||
if (dim.width > aRealWidth || dim.height > aRealHeight) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let waste = matchingArea - dim.width * dim.height;
|
||||
|
||||
if (waste >= 0 && waste < minWaste) {
|
||||
targetDimensions = dim;
|
||||
minWaste = waste;
|
||||
}
|
||||
}
|
||||
|
||||
// This platform-specific code is explained in the large comment above.
|
||||
if (getPlatform() != "linux") {
|
||||
ok(handleOSFuzziness(aContentWidth, targetDimensions.width),
|
||||
`Custom Dimension: The content window width is correctly rounded into. ${aRealWidth}px -> ${aContentWidth}px should equal ${targetDimensions.width}`);
|
||||
|
||||
ok(handleOSFuzziness(aContentHeight, targetDimensions.height),
|
||||
`Custom Dimension: The content window height is correctly rounded into. ${aRealHeight}px -> ${aContentHeight}px should equal ${targetDimensions.height}`);
|
||||
|
||||
// Using ok() above will cause Win/Mac to fail on even the first test, we don't need to repeat it, return true so waitForCondition ends
|
||||
return true;
|
||||
}
|
||||
// Returning true or false depending on if the test succeeded will cause Linux to repeat until it succeeds.
|
||||
return handleOSFuzziness(aContentWidth, targetDimensions.width) && handleOSFuzziness(aContentHeight, targetDimensions.height);
|
||||
};
|
||||
|
||||
await test_dynamical_window_rounding(aWindow, checkDimension);
|
||||
|
||||
await SpecialPowers.popPrefEnv();
|
||||
}
|
||||
|
||||
async function test_no_rounding_for_chrome(aWindow) {
|
||||
// First, resize the window to a size with is not rounded.
|
||||
await new Promise(resolve => {
|
||||
aWindow.onresize = () => resolve();
|
||||
aWindow.resizeTo(700, 450);
|
||||
});
|
||||
|
||||
// open a chrome privilege tab, like about:config.
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(
|
||||
aWindow.gBrowser, "about:config");
|
||||
|
||||
// Check that the browser element should not have a margin.
|
||||
is(tab.linkedBrowser.style.margin, "", "There is no margin around chrome tab.");
|
||||
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
await SpecialPowers.pushPrefEnv({"set":
|
||||
[
|
||||
["privacy.resistFingerprinting.letterboxing", true],
|
||||
["privacy.resistFingerprinting.letterboxing.testing", true],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function do_tests() {
|
||||
// Store the original window size before testing.
|
||||
let originalOuterWidth = window.outerWidth;
|
||||
let originalOuterHeight = window.outerHeight;
|
||||
|
||||
info("Run test for the default window rounding.");
|
||||
await test_dynamical_window_rounding(window, checkForDefaultSetting);
|
||||
|
||||
info("Run test for the window rounding with customized dimensions.");
|
||||
await test_customize_width_and_height(window);
|
||||
|
||||
info("Run test for no margin around tab with the chrome privilege.");
|
||||
await test_no_rounding_for_chrome(window);
|
||||
|
||||
// Restore the original window size.
|
||||
window.outerWidth = originalOuterWidth;
|
||||
window.outerHeight = originalOuterHeight;
|
||||
|
||||
// Testing that whether the dynamical rounding works for new windows.
|
||||
let win = await BrowserTestUtils.openNewBrowserWindow();
|
||||
|
||||
info("Run test for the default window rounding in new window.");
|
||||
await test_dynamical_window_rounding(win, checkForDefaultSetting);
|
||||
|
||||
info("Run test for the window rounding with customized dimensions in new window.");
|
||||
await test_customize_width_and_height(win);
|
||||
|
||||
info("Run test for no margin around tab with the chrome privilege in new window.");
|
||||
await test_no_rounding_for_chrome(win);
|
||||
|
||||
await BrowserTestUtils.closeWindow(win);
|
||||
});
|
|
@ -1442,6 +1442,9 @@ pref("privacy.firstparty.isolate.restrict_opener_access", true);
|
|||
// If you do set it, to work around some broken website, please file a bug with
|
||||
// information so we can understand why it is needed.
|
||||
pref("privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts", true);
|
||||
// The log level for browser console messages logged in RFPHelper.jsm
|
||||
// Change to 'All' and restart to see the messages
|
||||
pref("privacy.resistFingerprinting.jsmloglevel", "Warn");
|
||||
// A subset of Resist Fingerprinting protections focused specifically on timers for testing
|
||||
// This affects the Animation API, the performance APIs, Date.getTime, Event.timestamp,
|
||||
// File.lastModified, audioContext.currentTime, canvas.captureStream.currentTime
|
||||
|
|
|
@ -16,12 +16,26 @@ const kTopicHttpOnModifyRequest = "http-on-modify-request";
|
|||
const kPrefLetterboxing = "privacy.resistFingerprinting.letterboxing";
|
||||
const kPrefLetterboxingDimensions =
|
||||
"privacy.resistFingerprinting.letterboxing.dimensions";
|
||||
const kPrefLetterboxingTesting =
|
||||
"privacy.resistFingerprinting.letterboxing.testing";
|
||||
const kTopicDOMWindowOpened = "domwindowopened";
|
||||
const kEventLetterboxingSizeUpdate = "Letterboxing:ContentSizeUpdated";
|
||||
|
||||
const kDefaultWidthStepping = 200;
|
||||
const kDefaultHeightStepping = 100;
|
||||
|
||||
var logConsole;
|
||||
function log(msg) {
|
||||
if (!logConsole) {
|
||||
logConsole = console.createInstance({
|
||||
prefix: "RFPHelper.jsm",
|
||||
maxLogLevelPref: "privacy.resistFingerprinting.jsmloglevel",
|
||||
});
|
||||
}
|
||||
|
||||
logConsole.log(msg);
|
||||
}
|
||||
|
||||
class _RFPHelper {
|
||||
// ============================================================================
|
||||
// Shared Setup
|
||||
|
@ -41,6 +55,8 @@ class _RFPHelper {
|
|||
Services.prefs.addObserver(kPrefLetterboxing, this);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "_letterboxingDimensions",
|
||||
kPrefLetterboxingDimensions, "", null, this._parseLetterboxingDimensions);
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "_isLetterboxingTesting",
|
||||
kPrefLetterboxingTesting, false);
|
||||
|
||||
// Add RFP and Letterboxing observers if prefs are enabled
|
||||
this._handleResistFingerprintingChanged();
|
||||
|
@ -326,6 +342,8 @@ class _RFPHelper {
|
|||
* content viewport.
|
||||
*/
|
||||
async _roundContentView(aBrowser) {
|
||||
let logId = Math.random();
|
||||
log("_roundContentView[" + logId + "]");
|
||||
let win = aBrowser.ownerGlobal;
|
||||
let browserContainer = aBrowser.getTabBrowser()
|
||||
.getBrowserContainer(aBrowser);
|
||||
|
@ -345,14 +363,21 @@ class _RFPHelper {
|
|||
};
|
||||
});
|
||||
|
||||
log("_roundContentView[" + logId + "] contentWidth=" + contentWidth + " contentHeight=" + contentHeight +
|
||||
" containerWidth=" + containerWidth + " containerHeight=" + containerHeight + " ");
|
||||
|
||||
let calcMargins = (aWidth, aHeight) => {
|
||||
let result;
|
||||
log("_roundContentView[" + logId + "] calcMargins(" + aWidth + ", " + aHeight + ")");
|
||||
// If the set is empty, we will round the content with the default
|
||||
// stepping size.
|
||||
if (!this._letterboxingDimensions.length) {
|
||||
return {
|
||||
result = {
|
||||
width: (aWidth % kDefaultWidthStepping) / 2,
|
||||
height: (aHeight % kDefaultHeightStepping) / 2,
|
||||
};
|
||||
log("_roundContentView[" + logId + "] calcMargins(" + aWidth + ", " + aHeight + ") = " + result.width + " x " + result.height);
|
||||
return result;
|
||||
}
|
||||
|
||||
let matchingArea = aWidth * aHeight;
|
||||
|
@ -375,7 +400,6 @@ class _RFPHelper {
|
|||
}
|
||||
}
|
||||
|
||||
let result;
|
||||
// If we cannot find any dimensions match to the real content window, this
|
||||
// means the content area is smaller the smallest size in the set. In this
|
||||
// case, we won't apply any margins.
|
||||
|
@ -391,6 +415,7 @@ class _RFPHelper {
|
|||
};
|
||||
}
|
||||
|
||||
log("_roundContentView[" + logId + "] calcMargins(" + aWidth + ", " + aHeight + ") = " + result.width + " x " + result.height);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
@ -401,10 +426,16 @@ class _RFPHelper {
|
|||
|
||||
// If the size of the content is already quantized, we do nothing.
|
||||
if (aBrowser.style.margin == `${margins.height}px ${margins.width}px`) {
|
||||
log("_roundContentView[" + logId + "] is_rounded == true");
|
||||
if (this._isLetterboxingTesting) {
|
||||
log("_roundContentView[" + logId + "] is_rounded == true test:letterboxing:update-margin-finish");
|
||||
Services.obs.notifyObservers(null, "test:letterboxing:update-margin-finish");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
win.requestAnimationFrame(() => {
|
||||
log("_roundContentView[" + logId + "] setting margins to " + margins.width + " x " + margins.height);
|
||||
// One cannot (easily) control the color of a margin unfortunately.
|
||||
// An initial attempt to use a border instead of a margin resulted
|
||||
// in offset event dispatching; so for now we use a colorless margin.
|
||||
|
|
Загрузка…
Ссылка в новой задаче