Blame dom/base/test/browser_promiseDocumentFlushed.js

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
});