summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/general/browser_windowopen_reflows.js
blob: 7dac8aad6e4c75ce30cca55d070a731aca3cfce6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const EXPECTED_REFLOWS = [
  // handleEvent flushes layout to get the tabstrip width after a resize.
  "handleEvent@chrome://browser/content/tabbrowser.xml|",

  // Loading a tab causes a reflow.
  "loadTabs@chrome://browser/content/tabbrowser.xml|" +
    "loadOneOrMoreURIs@chrome://browser/content/browser.js|" +
    "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|",

  // Selecting the address bar causes a reflow.
  "select@chrome://global/content/bindings/textbox.xml|" +
    "focusAndSelectUrlBar@chrome://browser/content/browser.js|" +
    "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|",

  // Focusing the content area causes a reflow.
  "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|",

  // Sometimes sessionstore collects data during this test, which causes a sync reflow
  // (https://bugzilla.mozilla.org/show_bug.cgi?id=892154 will fix this)
  "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm",
];

if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
  // TabsInTitlebar._update causes a reflow on OS X and Windows trying to do calculations
  // since layout info is already dirty. This doesn't seem to happen before
  // MozAfterPaint on Linux.
  EXPECTED_REFLOWS.push("TabsInTitlebar._update/rect@chrome://browser/content/browser-tabsintitlebar.js|" +
                          "TabsInTitlebar._update@chrome://browser/content/browser-tabsintitlebar.js|" +
                          "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js|" +
                          "handleEvent@chrome://browser/content/tabbrowser.xml|");
}

if (Services.appinfo.OS == "Darwin") {
  // _onOverflow causes a reflow getting widths.
  EXPECTED_REFLOWS.push("OverflowableToolbar.prototype._onOverflow@resource:///modules/CustomizableUI.jsm|" +
                        "OverflowableToolbar.prototype.init@resource:///modules/CustomizableUI.jsm|" +
                        "OverflowableToolbar.prototype.observe@resource:///modules/CustomizableUI.jsm|" +
                        "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|");
  // Same as above since in packaged builds there are no function names and the resource URI includes "app"
  EXPECTED_REFLOWS.push("@resource://app/modules/CustomizableUI.jsm|" +
                          "@resource://app/modules/CustomizableUI.jsm|" +
                          "@resource://app/modules/CustomizableUI.jsm|" +
                          "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|");
}

/*
 * This test ensures that there are no unexpected
 * uninterruptible reflows when opening new windows.
 */
function test() {
  waitForExplicitFinish();

  // Add a reflow observer and open a new window
  let win = OpenBrowserWindow();
  let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIWebNavigation)
                    .QueryInterface(Ci.nsIDocShell);
  docShell.addWeakReflowObserver(observer);

  // Wait until the mozafterpaint event occurs.
  waitForMozAfterPaint(win, function paintListener() {
    // Remove reflow observer and clean up.
    docShell.removeWeakReflowObserver(observer);
    win.close();

    finish();
  });
}

var observer = {
  reflow: function (start, end) {
    // Gather information about the current code path.
    let stack = new Error().stack;
    let path = stack.split("\n").slice(1).map(line => {
      return line.replace(/:\d+:\d+$/, "");
    }).join("|");
    let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|");

    // Stack trace is empty. Reflow was triggered by native code.
    if (path === "") {
      return;
    }

    // Check if this is an expected reflow.
    for (let expectedStack of EXPECTED_REFLOWS) {
      if (path.startsWith(expectedStack) ||
          // Accept an empty function name for gBrowserInit._delayedStartup or TabsInTitlebar._update to workaround bug 906578.
          path.startsWith(expectedStack.replace(/(^|\|)(gBrowserInit\._delayedStartup|TabsInTitlebar\._update)@/, "$1@"))) {
        ok(true, "expected uninterruptible reflow '" + expectedStack + "'");
        return;
      }
    }

    ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'");
  },

  reflowInterruptible: function (start, end) {
    // We're not interested in interruptible reflows.
  },

  QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver,
                                         Ci.nsISupportsWeakReference])
};

function waitForMozAfterPaint(win, callback) {
  win.addEventListener("MozAfterPaint", function onEnd(event) {
    if (event.target != win)
      return;
    win.removeEventListener("MozAfterPaint", onEnd);
    executeSoon(callback);
  });
}