1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/base/content/test/general/browser_tabopen_reflows.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,129 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +XPCOMUtils.defineLazyGetter(this, "docShell", () => { 1.8 + return window.QueryInterface(Ci.nsIInterfaceRequestor) 1.9 + .getInterface(Ci.nsIWebNavigation) 1.10 + .QueryInterface(Ci.nsIDocShell); 1.11 +}); 1.12 + 1.13 +const EXPECTED_REFLOWS = [ 1.14 + // tabbrowser.adjustTabstrip() call after tabopen animation has finished 1.15 + "adjustTabstrip@chrome://browser/content/tabbrowser.xml|" + 1.16 + "_handleNewTab@chrome://browser/content/tabbrowser.xml|" + 1.17 + "onxbltransitionend@chrome://browser/content/tabbrowser.xml|", 1.18 + 1.19 + // switching focus in updateCurrentBrowser() causes reflows 1.20 + "updateCurrentBrowser@chrome://browser/content/tabbrowser.xml|" + 1.21 + "onselect@chrome://browser/content/browser.xul|", 1.22 + 1.23 + // switching focus in openLinkIn() causes reflows 1.24 + "openLinkIn@chrome://browser/content/utilityOverlay.js|" + 1.25 + "openUILinkIn@chrome://browser/content/utilityOverlay.js|" + 1.26 + "BrowserOpenTab@chrome://browser/content/browser.js|", 1.27 + 1.28 + // accessing element.scrollPosition in _fillTrailingGap() flushes layout 1.29 + "get_scrollPosition@chrome://global/content/bindings/scrollbox.xml|" + 1.30 + "_fillTrailingGap@chrome://browser/content/tabbrowser.xml|" + 1.31 + "_handleNewTab@chrome://browser/content/tabbrowser.xml|" + 1.32 + "onxbltransitionend@chrome://browser/content/tabbrowser.xml|", 1.33 + 1.34 + // The TabView iframe causes reflows in the parent document. 1.35 + "iQClass_height@chrome://browser/content/tabview.js|" + 1.36 + "GroupItem_getContentBounds@chrome://browser/content/tabview.js|" + 1.37 + "GroupItem_shouldStack@chrome://browser/content/tabview.js|" + 1.38 + "GroupItem_arrange@chrome://browser/content/tabview.js|" + 1.39 + "GroupItem_add@chrome://browser/content/tabview.js|" + 1.40 + "GroupItems_newTab@chrome://browser/content/tabview.js|" + 1.41 + "TabItem__reconnect@chrome://browser/content/tabview.js|" + 1.42 + "TabItem@chrome://browser/content/tabview.js|" + 1.43 + "TabItems_link@chrome://browser/content/tabview.js|" + 1.44 + "TabItems_init/this._eventListeners.open@chrome://browser/content/tabview.js|", 1.45 + 1.46 + // SessionStore.getWindowDimensions() 1.47 + "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm|" + 1.48 + "ssi_updateWindowFeatures/<@resource:///modules/sessionstore/SessionStore.jsm|" + 1.49 + "ssi_updateWindowFeatures@resource:///modules/sessionstore/SessionStore.jsm|" + 1.50 + "ssi_collectWindowData@resource:///modules/sessionstore/SessionStore.jsm|", 1.51 + 1.52 + // tabPreviews.capture() 1.53 + "tabPreviews_capture@chrome://browser/content/browser.js|" + 1.54 + "tabPreviews_handleEvent/<@chrome://browser/content/browser.js|", 1.55 + 1.56 + // tabPreviews.capture() 1.57 + "tabPreviews_capture@chrome://browser/content/browser.js|" + 1.58 + "@chrome://browser/content/browser.js|" 1.59 +]; 1.60 + 1.61 +const PREF_PRELOAD = "browser.newtab.preload"; 1.62 +const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource"; 1.63 + 1.64 +/* 1.65 + * This test ensures that there are no unexpected 1.66 + * uninterruptible reflows when opening new tabs. 1.67 + */ 1.68 +function test() { 1.69 + waitForExplicitFinish(); 1.70 + 1.71 + Services.prefs.setBoolPref(PREF_PRELOAD, false); 1.72 + Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}"); 1.73 + registerCleanupFunction(() => { 1.74 + Services.prefs.clearUserPref(PREF_PRELOAD); 1.75 + Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE); 1.76 + }); 1.77 + 1.78 + // Add a reflow observer and open a new tab. 1.79 + docShell.addWeakReflowObserver(observer); 1.80 + BrowserOpenTab(); 1.81 + 1.82 + // Wait until the tabopen animation has finished. 1.83 + waitForTransitionEnd(function () { 1.84 + // Remove reflow observer and clean up. 1.85 + docShell.removeWeakReflowObserver(observer); 1.86 + gBrowser.removeCurrentTab(); 1.87 + 1.88 + finish(); 1.89 + }); 1.90 +} 1.91 + 1.92 +let observer = { 1.93 + reflow: function (start, end) { 1.94 + // Gather information about the current code path. 1.95 + let path = (new Error().stack).split("\n").slice(1).map(line => { 1.96 + return line.replace(/:\d+:\d+$/, ""); 1.97 + }).join("|"); 1.98 + let pathWithLineNumbers = (new Error().stack).split("\n").slice(1).join("|"); 1.99 + 1.100 + // Stack trace is empty. Reflow was triggered by native code. 1.101 + if (path === "") { 1.102 + return; 1.103 + } 1.104 + 1.105 + // Check if this is an expected reflow. 1.106 + for (let stack of EXPECTED_REFLOWS) { 1.107 + if (path.startsWith(stack)) { 1.108 + ok(true, "expected uninterruptible reflow '" + stack + "'"); 1.109 + return; 1.110 + } 1.111 + } 1.112 + 1.113 + ok(false, "unexpected uninterruptible reflow '" + pathWithLineNumbers + "'"); 1.114 + }, 1.115 + 1.116 + reflowInterruptible: function (start, end) { 1.117 + // We're not interested in interruptible reflows. 1.118 + }, 1.119 + 1.120 + QueryInterface: XPCOMUtils.generateQI([Ci.nsIReflowObserver, 1.121 + Ci.nsISupportsWeakReference]) 1.122 +}; 1.123 + 1.124 +function waitForTransitionEnd(callback) { 1.125 + let tab = gBrowser.selectedTab; 1.126 + tab.addEventListener("transitionend", function onEnd(event) { 1.127 + if (event.propertyName === "max-width") { 1.128 + tab.removeEventListener("transitionend", onEnd); 1.129 + executeSoon(callback); 1.130 + } 1.131 + }); 1.132 +}