1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/test/general/browser_windowopen_reflows.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,117 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +"use strict"; 1.8 + 1.9 +const EXPECTED_REFLOWS = [ 1.10 + // handleEvent flushes layout to get the tabstrip width after a resize. 1.11 + "handleEvent@chrome://browser/content/tabbrowser.xml|", 1.12 + 1.13 + // Loading a tab causes a reflow. 1.14 + "loadTabs@chrome://browser/content/tabbrowser.xml|" + 1.15 + "loadOneOrMoreURIs@chrome://browser/content/browser.js|" + 1.16 + "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", 1.17 + 1.18 + // Selecting the address bar causes a reflow. 1.19 + "select@chrome://global/content/bindings/textbox.xml|" + 1.20 + "focusAndSelectUrlBar@chrome://browser/content/browser.js|" + 1.21 + "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", 1.22 + 1.23 + // Focusing the content area causes a reflow. 1.24 + "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|", 1.25 + 1.26 + // Sometimes sessionstore collects data during this test, which causes a sync reflow 1.27 + // (https://bugzilla.mozilla.org/show_bug.cgi?id=892154 will fix this) 1.28 + "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm", 1.29 +]; 1.30 + 1.31 +if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") { 1.32 + // TabsInTitlebar._update causes a reflow on OS X and Windows trying to do calculations 1.33 + // since layout info is already dirty. This doesn't seem to happen before 1.34 + // MozAfterPaint on Linux. 1.35 + EXPECTED_REFLOWS.push("rect@chrome://browser/content/browser.js|" + 1.36 + "TabsInTitlebar._update@chrome://browser/content/browser.js|" + 1.37 + "updateAppearance@chrome://browser/content/browser.js|" + 1.38 + "handleEvent@chrome://browser/content/tabbrowser.xml|"); 1.39 +} 1.40 + 1.41 +if (Services.appinfo.OS == "Darwin") { 1.42 + // _onOverflow causes a reflow getting widths. 1.43 + EXPECTED_REFLOWS.push("OverflowableToolbar.prototype._onOverflow@resource:///modules/CustomizableUI.jsm|" + 1.44 + "OverflowableToolbar.prototype.init@resource:///modules/CustomizableUI.jsm|" + 1.45 + "OverflowableToolbar.prototype.observe@resource:///modules/CustomizableUI.jsm|" + 1.46 + "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|"); 1.47 + // Same as above since in packaged builds there are no function names and the resource URI includes "app" 1.48 + EXPECTED_REFLOWS.push("@resource://app/modules/CustomizableUI.jsm|" + 1.49 + "@resource://app/modules/CustomizableUI.jsm|" + 1.50 + "@resource://app/modules/CustomizableUI.jsm|" + 1.51 + "gBrowserInit._delayedStartup@chrome://browser/content/browser.js|"); 1.52 +} 1.53 + 1.54 +/* 1.55 + * This test ensures that there are no unexpected 1.56 + * uninterruptible reflows when opening new windows. 1.57 + */ 1.58 +function test() { 1.59 + waitForExplicitFinish(); 1.60 + 1.61 + // Add a reflow observer and open a new window 1.62 + let win = OpenBrowserWindow(); 1.63 + let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor) 1.64 + .getInterface(Ci.nsIWebNavigation) 1.65 + .QueryInterface(Ci.nsIDocShell); 1.66 + docShell.addWeakReflowObserver(observer); 1.67 + 1.68 + // Wait until the mozafterpaint event occurs. 1.69 + waitForMozAfterPaint(win, function paintListener() { 1.70 + // Remove reflow observer and clean up. 1.71 + docShell.removeWeakReflowObserver(observer); 1.72 + win.close(); 1.73 + 1.74 + finish(); 1.75 + }); 1.76 +} 1.77 + 1.78 +let observer = { 1.79 + reflow: function (start, end) { 1.80 + // Gather information about the current code path. 1.81 + let stack = new Error().stack; 1.82 + let path = stack.split("\n").slice(1).map(line => { 1.83 + return line.replace(/:\d+:\d+$/, ""); 1.84 + }).join("|"); 1.85 + let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|"); 1.86 + 1.87 + // Stack trace is empty. Reflow was triggered by native code. 1.88 + if (path === "") { 1.89 + return; 1.90 + } 1.91 + 1.92 + // Check if this is an expected reflow. 1.93 + for (let expectedStack of EXPECTED_REFLOWS) { 1.94 + if (path.startsWith(expectedStack) || 1.95 + // Accept an empty function name for gBrowserInit._delayedStartup or TabsInTitlebar._update to workaround bug 906578. 1.96 + path.startsWith(expectedStack.replace(/(^|\|)(gBrowserInit\._delayedStartup|TabsInTitlebar\._update)@/, "$1@"))) { 1.97 + ok(true, "expected uninterruptible reflow '" + expectedStack + "'"); 1.98 + return; 1.99 + } 1.100 + } 1.101 + 1.102 + ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'"); 1.103 + }, 1.104 + 1.105 + reflowInterruptible: function (start, end) { 1.106 + // We're not interested in interruptible reflows. 1.107 + }, 1.108 + 1.109 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver, 1.110 + Ci.nsISupportsWeakReference]) 1.111 +}; 1.112 + 1.113 +function waitForMozAfterPaint(win, callback) { 1.114 + win.addEventListener("MozAfterPaint", function onEnd(event) { 1.115 + if (event.target != win) 1.116 + return; 1.117 + win.removeEventListener("MozAfterPaint", onEnd); 1.118 + executeSoon(callback); 1.119 + }); 1.120 +}