|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 "use strict"; |
|
5 |
|
6 const EXPECTED_REFLOWS = [ |
|
7 // handleEvent flushes layout to get the tabstrip width after a resize. |
|
8 "handleEvent@chrome://browser/content/tabbrowser.xml|", |
|
9 |
|
10 // Loading a tab causes a reflow. |
|
11 "loadTabs@chrome://browser/content/tabbrowser.xml|" + |
|
12 "loadOneOrMoreURIs@chrome://browser/content/browser.js|" + |
|
13 "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", |
|
14 |
|
15 // Selecting the address bar causes a reflow. |
|
16 "select@chrome://global/content/bindings/textbox.xml|" + |
|
17 "focusAndSelectUrlBar@chrome://browser/content/browser.js|" + |
|
18 "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", |
|
19 |
|
20 // Focusing the content area causes a reflow. |
|
21 "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", |
|
22 |
|
23 // Sometimes sessionstore collects data during this test, which causes a sync reflow |
|
24 // (https://bugzilla.mozilla.org/show_bug.cgi?id=892154 will fix this) |
|
25 "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm", |
|
26 ]; |
|
27 |
|
28 if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") { |
|
29 // TabsInTitlebar._update causes a reflow on OS X and Windows trying to do calculations |
|
30 // since layout info is already dirty. This doesn't seem to happen before |
|
31 // MozAfterPaint on Linux. |
|
32 EXPECTED_REFLOWS.push("rect@chrome://browser/content/browser.js|" + |
|
33 "TabsInTitlebar._update@chrome://browser/content/browser.js|" + |
|
34 "updateAppearance@chrome://browser/content/browser.js|" + |
|
35 "handleEvent@chrome://browser/content/tabbrowser.xml|"); |
|
36 } |
|
37 |
|
38 if (Services.appinfo.OS == "Darwin") { |
|
39 // _onOverflow causes a reflow getting widths. |
|
40 EXPECTED_REFLOWS.push("OverflowableToolbar.prototype._onOverflow@resource:///modules/CustomizableUI.jsm|" + |
|
41 "OverflowableToolbar.prototype.init@resource:///modules/CustomizableUI.jsm|" + |
|
42 "OverflowableToolbar.prototype.observe@resource:///modules/CustomizableUI.jsm|" + |
|
43 "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|"); |
|
44 // Same as above since in packaged builds there are no function names and the resource URI includes "app" |
|
45 EXPECTED_REFLOWS.push("@resource://app/modules/CustomizableUI.jsm|" + |
|
46 "@resource://app/modules/CustomizableUI.jsm|" + |
|
47 "@resource://app/modules/CustomizableUI.jsm|" + |
|
48 "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|"); |
|
49 } |
|
50 |
|
51 /* |
|
52 * This test ensures that there are no unexpected |
|
53 * uninterruptible reflows when opening new windows. |
|
54 */ |
|
55 function test() { |
|
56 waitForExplicitFinish(); |
|
57 |
|
58 // Add a reflow observer and open a new window |
|
59 let win = OpenBrowserWindow(); |
|
60 let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor) |
|
61 .getInterface(Ci.nsIWebNavigation) |
|
62 .QueryInterface(Ci.nsIDocShell); |
|
63 docShell.addWeakReflowObserver(observer); |
|
64 |
|
65 // Wait until the mozafterpaint event occurs. |
|
66 waitForMozAfterPaint(win, function paintListener() { |
|
67 // Remove reflow observer and clean up. |
|
68 docShell.removeWeakReflowObserver(observer); |
|
69 win.close(); |
|
70 |
|
71 finish(); |
|
72 }); |
|
73 } |
|
74 |
|
75 let observer = { |
|
76 reflow: function (start, end) { |
|
77 // Gather information about the current code path. |
|
78 let stack = new Error().stack; |
|
79 let path = stack.split("\n").slice(1).map(line => { |
|
80 return line.replace(/:\d+:\d+$/, ""); |
|
81 }).join("|"); |
|
82 let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|"); |
|
83 |
|
84 // Stack trace is empty. Reflow was triggered by native code. |
|
85 if (path === "") { |
|
86 return; |
|
87 } |
|
88 |
|
89 // Check if this is an expected reflow. |
|
90 for (let expectedStack of EXPECTED_REFLOWS) { |
|
91 if (path.startsWith(expectedStack) || |
|
92 // Accept an empty function name for gBrowserInit._delayedStartup or TabsInTitlebar._update to workaround bug 906578. |
|
93 path.startsWith(expectedStack.replace(/(^|\|)(gBrowserInit\._delayedStartup|TabsInTitlebar\._update)@/, "$1@"))) { |
|
94 ok(true, "expected uninterruptible reflow '" + expectedStack + "'"); |
|
95 return; |
|
96 } |
|
97 } |
|
98 |
|
99 ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'"); |
|
100 }, |
|
101 |
|
102 reflowInterruptible: function (start, end) { |
|
103 // We're not interested in interruptible reflows. |
|
104 }, |
|
105 |
|
106 QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver, |
|
107 Ci.nsISupportsWeakReference]) |
|
108 }; |
|
109 |
|
110 function waitForMozAfterPaint(win, callback) { |
|
111 win.addEventListener("MozAfterPaint", function onEnd(event) { |
|
112 if (event.target != win) |
|
113 return; |
|
114 win.removeEventListener("MozAfterPaint", onEnd); |
|
115 executeSoon(callback); |
|
116 }); |
|
117 } |