|
Packit |
f0b94e |
/* Any copyright is dedicated to the Public Domain.
|
|
Packit |
f0b94e |
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
"use strict";
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Dirties style and layout on the current browser window.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* @param {Number} Optional factor by which to modify the DOM. Useful for
|
|
Packit |
f0b94e |
* when multiple calls to dirtyTheDOM may occur, and you need them
|
|
Packit |
f0b94e |
* to dirty the DOM differently from one another. If you only need
|
|
Packit |
f0b94e |
* to dirty the DOM once, this can be omitted.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
function dirtyStyleAndLayout(factor = 1) {
|
|
Packit |
f0b94e |
gNavToolbox.style.padding = factor + "px";
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Dirties style of the current browser window, but NOT layout.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
function dirtyStyle() {
|
|
Packit |
f0b94e |
gNavToolbox.style.color = "red";
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const gWindowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
Packit |
f0b94e |
.getInterface(Ci.nsIDOMWindowUtils);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Asserts that no style or layout flushes are required by the
|
|
Packit |
f0b94e |
* current window.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
function assertNoFlushesRequired() {
|
|
Packit |
f0b94e |
Assert.ok(!gWindowUtils.needsFlush(Ci.nsIDOMWindowUtils.FLUSH_STYLE),
|
|
Packit |
f0b94e |
"No style flushes are required.");
|
|
Packit |
f0b94e |
Assert.ok(!gWindowUtils.needsFlush(Ci.nsIDOMWindowUtils.FLUSH_LAYOUT),
|
|
Packit |
f0b94e |
"No layout flushes are required.");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Asserts that the DOM has been dirtied, and so style and layout flushes
|
|
Packit |
f0b94e |
* are required.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
function assertFlushesRequired() {
|
|
Packit |
f0b94e |
Assert.ok(gWindowUtils.needsFlush(Ci.nsIDOMWindowUtils.FLUSH_STYLE),
|
|
Packit |
f0b94e |
"Style flush required.");
|
|
Packit |
f0b94e |
Assert.ok(gWindowUtils.needsFlush(Ci.nsIDOMWindowUtils.FLUSH_LAYOUT),
|
|
Packit |
f0b94e |
"Layout flush required.");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Removes style changes from dirtyTheDOM() from the browser window,
|
|
Packit |
f0b94e |
* and resolves once the refresh driver ticks.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
async function cleanTheDOM() {
|
|
Packit |
f0b94e |
gNavToolbox.style.padding = "";
|
|
Packit |
f0b94e |
gNavToolbox.style.color = "";
|
|
Packit |
f0b94e |
await window.promiseDocumentFlushed(() => {});
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
add_task(async function setup() {
|
|
Packit |
f0b94e |
registerCleanupFunction(cleanTheDOM);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Tests that if the DOM is dirty, that promiseDocumentFlushed will
|
|
Packit |
f0b94e |
* resolve once layout and style have been flushed.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
add_task(async function test_basic() {
|
|
Packit |
f0b94e |
dirtyStyleAndLayout();
|
|
Packit |
f0b94e |
assertFlushesRequired();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await window.promiseDocumentFlushed(() => {});
|
|
Packit |
f0b94e |
assertNoFlushesRequired();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
dirtyStyle();
|
|
Packit |
f0b94e |
assertFlushesRequired();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await window.promiseDocumentFlushed(() => {});
|
|
Packit |
f0b94e |
assertNoFlushesRequired();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// The DOM should be clean already, but we'll do this anyway to isolate
|
|
Packit |
f0b94e |
// failures from other tests.
|
|
Packit |
f0b94e |
await cleanTheDOM();
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Test that values returned by the callback passed to promiseDocumentFlushed
|
|
Packit |
f0b94e |
* get passed down through the Promise resolution.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
add_task(async function test_can_get_results_from_callback() {
|
|
Packit |
f0b94e |
const NEW_PADDING = "2px";
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
gNavToolbox.style.padding = NEW_PADDING;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
assertFlushesRequired();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let paddings = await window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
let style = window.getComputedStyle(gNavToolbox);
|
|
Packit |
f0b94e |
return {
|
|
Packit |
f0b94e |
left: style.paddingLeft,
|
|
Packit |
f0b94e |
right: style.paddingRight,
|
|
Packit |
f0b94e |
top: style.paddingTop,
|
|
Packit |
f0b94e |
bottom: style.paddingBottom,
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (let prop in paddings) {
|
|
Packit |
f0b94e |
Assert.equal(paddings[prop], NEW_PADDING,
|
|
Packit |
f0b94e |
"Got expected padding");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await cleanTheDOM();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
gNavToolbox.style.padding = NEW_PADDING;
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
assertFlushesRequired();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let rect = await window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
let observer = {
|
|
Packit |
f0b94e |
reflow() {
|
|
Packit |
f0b94e |
Assert.ok(false, "A reflow should not have occurred.");
|
|
Packit |
f0b94e |
},
|
|
Packit |
f0b94e |
reflowInterruptible() {},
|
|
Packit |
f0b94e |
QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
|
|
Packit |
f0b94e |
Ci.nsISupportsWeakReference])
|
|
Packit |
f0b94e |
};
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
Packit |
f0b94e |
.getInterface(Ci.nsIWebNavigation)
|
|
Packit |
f0b94e |
.QueryInterface(Ci.nsIDocShell);
|
|
Packit |
f0b94e |
docShell.addWeakReflowObserver(observer);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let toolboxRect = gNavToolbox.getBoundingClientRect();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
docShell.removeWeakReflowObserver(observer);
|
|
Packit |
f0b94e |
return toolboxRect;
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
// The actual values of this rect aren't super important for
|
|
Packit |
f0b94e |
// the purposes of this test - we just want to know that a valid
|
|
Packit |
f0b94e |
// rect was returned, so checking for properties being greater than
|
|
Packit |
f0b94e |
// 0 is sufficient.
|
|
Packit |
f0b94e |
for (let property of ["width", "height"]) {
|
|
Packit |
f0b94e |
Assert.ok(rect[property] > 0, `Rect property ${property} > 0 (${rect[property]})`);
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await cleanTheDOM();
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Test that if promiseDocumentFlushed is requested on a window
|
|
Packit |
f0b94e |
* that closes before it gets a chance to do a refresh driver
|
|
Packit |
f0b94e |
* tick, the promiseDocumentFlushed Promise is still resolved, and
|
|
Packit |
f0b94e |
* the callback is still called.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
add_task(async function test_resolved_in_window_close() {
|
|
Packit |
f0b94e |
let win = await BrowserTestUtils.openNewBrowserWindow();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await win.promiseDocumentFlushed(() => {});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
Packit |
f0b94e |
.getInterface(Ci.nsIDocShell);
|
|
Packit |
f0b94e |
docShell.contentViewer.pausePainting();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
win.gNavToolbox.style.padding = "5px";
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
const EXPECTED = 1234;
|
|
Packit |
f0b94e |
let promise = win.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
// Despite the window not painting before closing, this
|
|
Packit |
f0b94e |
// callback should be fired when the window gets torn
|
|
Packit |
f0b94e |
// down and should stil be able to return a result.
|
|
Packit |
f0b94e |
return EXPECTED;
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await BrowserTestUtils.closeWindow(win);
|
|
Packit |
f0b94e |
Assert.equal(await promise, EXPECTED);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Test that re-entering promiseDocumentFlushed is not possible
|
|
Packit |
f0b94e |
* from within a promiseDocumentFlushed callback. Doing so will
|
|
Packit |
f0b94e |
* result in the outer Promise rejecting with NS_ERROR_FAILURE.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
add_task(async function test_reentrancy() {
|
|
Packit |
f0b94e |
dirtyStyleAndLayout();
|
|
Packit |
f0b94e |
assertFlushesRequired();
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let promise = window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
return window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
Assert.ok(false, "Should never run this.");
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Assert.rejects(promise, ex => ex.result == Cr.NS_ERROR_FAILURE);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
/**
|
|
Packit |
f0b94e |
* Tests the expected execution order of a series of promiseDocumentFlushed
|
|
Packit |
f0b94e |
* calls, their callbacks, and the resolutions of their Promises.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* When multiple promiseDocumentFlushed callbacks are queued, the callbacks
|
|
Packit |
f0b94e |
* should always been run first before any of the Promises are resolved.
|
|
Packit |
f0b94e |
*
|
|
Packit |
f0b94e |
* The callbacks should run in the order that they were queued in via
|
|
Packit |
f0b94e |
* promiseDocumentFlushed. The Promise resolutions should similarly run
|
|
Packit |
f0b94e |
* in the order that promiseDocumentFlushed was called in.
|
|
Packit |
f0b94e |
*/
|
|
Packit |
f0b94e |
add_task(async function test_execution_order() {
|
|
Packit |
f0b94e |
let result = [];
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
dirtyStyleAndLayout(1);
|
|
Packit |
f0b94e |
let promise1 = window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
result.push(0);
|
|
Packit |
f0b94e |
}).then(() => {
|
|
Packit |
f0b94e |
result.push(2);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let promise2 = window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
result.push(1);
|
|
Packit |
f0b94e |
}).then(() => {
|
|
Packit |
f0b94e |
result.push(3);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Promise.all([promise1, promise2]);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.equal(result.length, 4,
|
|
Packit |
f0b94e |
"Should have run all callbacks and Promises.");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let promise3 = window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
result.push(4);
|
|
Packit |
f0b94e |
}).then(() => {
|
|
Packit |
f0b94e |
result.push(6);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
let promise4 = window.promiseDocumentFlushed(() => {
|
|
Packit |
f0b94e |
result.push(5);
|
|
Packit |
f0b94e |
}).then(() => {
|
|
Packit |
f0b94e |
result.push(7);
|
|
Packit |
f0b94e |
});
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
await Promise.all([promise3, promise4]);
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
Assert.equal(result.length, 8,
|
|
Packit |
f0b94e |
"Should have run all callbacks and Promises.");
|
|
Packit |
f0b94e |
|
|
Packit |
f0b94e |
for (let i = 0; i < result.length; ++i) {
|
|
Packit |
f0b94e |
Assert.equal(result[i], i,
|
|
Packit |
f0b94e |
"Callbacks and Promises should have run in the expected order.");
|
|
Packit |
f0b94e |
}
|
|
Packit |
f0b94e |
});
|