From 1004ce069e7b51b8a7858160d4e0973b5c626494 Mon Sep 17 00:00:00 2001 From: Brandon Benvie Date: Fri, 13 Dec 2013 11:11:20 -0800 Subject: [PATCH 01/17] Bug 943681 - Convert to Promise.jsm in the webconsole. r=msucan --- browser/devtools/webconsole/hudservice.js | 4 +-- browser/devtools/webconsole/panel.js | 2 +- ...r_webconsole_bug_613642_maintain_scroll.js | 36 +++++++------------ browser/devtools/webconsole/test/head.js | 2 +- browser/devtools/webconsole/webconsole.js | 2 +- 5 files changed, 17 insertions(+), 29 deletions(-) diff --git a/browser/devtools/webconsole/hudservice.js b/browser/devtools/webconsole/hudservice.js index caca70d3502..fed0a290ab6 100644 --- a/browser/devtools/webconsole/hudservice.js +++ b/browser/devtools/webconsole/hudservice.js @@ -11,9 +11,9 @@ const {Cc, Ci, Cu} = require("chrome"); let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils; let Heritage = require("sdk/core/heritage"); -loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); loader.lazyGetter(this, "Telemetry", () => require("devtools/shared/telemetry")); loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/webconsole/webconsole").WebConsoleFrame); +loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm"); loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); @@ -110,6 +110,7 @@ HUD_SERVICE.prototype = function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow) { let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow); + this._browserConsoleID = hud.hudId; this.consoles.set(hud.hudId, hud); return hud.init(); }, @@ -259,7 +260,6 @@ HUD_SERVICE.prototype = connect().then(getTarget).then(openWindow).then((aWindow) => { this.openBrowserConsole(target, aWindow, aWindow) .then((aBrowserConsole) => { - this._browserConsoleID = aBrowserConsole.hudId; this._browserConsoleDefer.resolve(aBrowserConsole); this._browserConsoleDefer = null; }) diff --git a/browser/devtools/webconsole/panel.js b/browser/devtools/webconsole/panel.js index 48a71f576ec..466f84834de 100644 --- a/browser/devtools/webconsole/panel.js +++ b/browser/devtools/webconsole/panel.js @@ -6,7 +6,7 @@ const {Cc, Ci, Cu} = require("chrome"); -loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); +loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyGetter(this, "HUDService", () => require("devtools/webconsole/hudservice")); loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); diff --git a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js index 13f01855626..63da224c9a4 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js @@ -95,37 +95,25 @@ function testGen() { testNext(); }; EventUtils.synthesizeKey("VK_END", {}); - yield; + yield undefined; let oldScrollTop = scrollBox.scrollTop; content.console.log("test message 151"); - waitForMessages({ - webconsole: hud, - messages: [{ - text: "test message 151", - category: CATEGORY_WEBDEV, - severity: SEVERITY_LOG, - }], - }).then(() => { - scrollBox.onscroll = () => { - if (scrollBox.scrollTop == oldScrollTop) { - // Wait for scroll to change. - return; - } - scrollBox.onscroll = null; - isnot(scrollBox.scrollTop, oldScrollTop, "scroll location updated (moved to bottom again)"); - testNext(); - }; - }); + scrollBox.onscroll = () => { + dump("\n\nSCROLLED\n\n"); + if (scrollBox.scrollTop == oldScrollTop) { + // Wait for scroll to change. + return; + } + scrollBox.onscroll = null; + isnot(scrollBox.scrollTop, oldScrollTop, "scroll location updated (moved to bottom again)"); + hud = testDriver = null; + finishTest(); + }; yield undefined; - - hud = testDriver = null; - finishTest(); - - yield undefined; } function test() { diff --git a/browser/devtools/webconsole/test/head.js b/browser/devtools/webconsole/test/head.js index c60e972e605..744401b971d 100644 --- a/browser/devtools/webconsole/test/head.js +++ b/browser/devtools/webconsole/test/head.js @@ -8,7 +8,7 @@ let WebConsoleUtils, gDevTools, TargetFactory, console, promise, require; (() => { gDevTools = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}).gDevTools; console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console; - promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise; + promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; let tools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools; let utils = tools.require("devtools/toolkit/webconsole/utils"); diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 5e477227fc9..46673010ec1 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -14,7 +14,7 @@ loader.lazyServiceGetter(this, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); -loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); +loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup); From ee52a77207089e101eca911422344238e864b405 Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Thu, 26 Sep 2013 15:17:22 -0700 Subject: [PATCH 02/17] Bug 919777: Change the width to be fill_parent for titles in TwoLinePageRow. [r=lucasr] --- mobile/android/base/resources/layout/two_line_page_row.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/base/resources/layout/two_line_page_row.xml b/mobile/android/base/resources/layout/two_line_page_row.xml index 65ec3844fd3..94125a24e9c 100644 --- a/mobile/android/base/resources/layout/two_line_page_row.xml +++ b/mobile/android/base/resources/layout/two_line_page_row.xml @@ -20,7 +20,7 @@ From d6c33a86319004efd685d65e72f4b39e5b44371b Mon Sep 17 00:00:00 2001 From: Jared Wein Date: Fri, 13 Dec 2013 14:27:56 -0500 Subject: [PATCH 03/17] Bug 931891 - [Australis] Backed out bug 887515 and the followups (bug 896291, 909662, 896896). r=ttaubert --- browser/base/content/browser.js | 36 ++---- browser/base/content/browser.xul | 3 +- browser/base/content/tabbrowser.xml | 45 ++------ browser/base/content/test/general/browser.ini | 2 - .../content/test/general/browser_bug887515.js | 75 ------------ ...wser_bug896291_closeMaxSessionStoreTabs.js | 108 ------------------ .../sessionstore/nsISessionStore.idl | 16 +-- .../RecentlyClosedTabsAndWindowsMenuUtils.jsm | 2 +- .../sessionstore/src/SessionStore.jsm | 42 ------- .../sessionstore/test/browser_345898.js | 4 - .../tabview/test/browser_tabview_bug608037.js | 2 +- .../tabview/test/browser_tabview_bug624847.js | 4 +- .../tabview/test/browser_tabview_bug628270.js | 2 +- .../tabview/test/browser_tabview_bug706736.js | 2 +- browser/components/tabview/test/head.js | 2 +- .../locales/en-US/chrome/browser/browser.dtd | 5 - 16 files changed, 27 insertions(+), 323 deletions(-) delete mode 100644 browser/base/content/test/general/browser_bug887515.js delete mode 100644 browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index b7adb8dde39..a247de742b1 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6101,33 +6101,16 @@ function undoCloseTab(aIndex) { if (gBrowser.tabs.length == 1 && isTabEmpty(gBrowser.selectedTab)) blankTabToRemove = gBrowser.selectedTab; - let numberOfTabsToUndoClose = 0; - let index = Number(aIndex); - - - if (isNaN(index)) { - index = 0; - numberOfTabsToUndoClose = SessionStore.getNumberOfTabsClosedLast(window); - } else { - if (0 > index || index >= SessionStore.getClosedTabCount(window)) - return null; - numberOfTabsToUndoClose = 1; - } - - let tab = null; - while (numberOfTabsToUndoClose > 0 && - numberOfTabsToUndoClose--) { + var tab = null; + if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) { TabView.prepareUndoCloseTab(blankTabToRemove); - tab = SessionStore.undoCloseTab(window, index); + tab = SessionStore.undoCloseTab(window, aIndex || 0); TabView.afterUndoCloseTab(); - if (blankTabToRemove) { + + if (blankTabToRemove) gBrowser.removeTab(blankTabToRemove); - blankTabToRemove = null; - } } - // Reset the number of tabs closed last time to the default. - SessionStore.setNumberOfTabsClosedLast(window, 1); return tab; } @@ -6950,13 +6933,8 @@ var TabContextMenu = { menuItem.disabled = disabled; // Session store - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - let closedTabCount = SessionStore.getNumberOfTabsClosedLast(window); - undoCloseTabElement.disabled = closedTabCount == 0; - // Change the label of "Undo Close Tab" to specify if it will undo a batch-close - // or a single close. - let visibleLabel = closedTabCount <= 1 ? "singletablabel" : "multipletablabel"; - undoCloseTabElement.setAttribute("label", undoCloseTabElement.getAttribute(visibleLabel)); + document.getElementById("context_undoCloseTab").disabled = + SessionStore.getClosedTabCount(window) == 0; // Only one of pin/unpin should be visible document.getElementById("context_pinTab").hidden = this.contextTab.pinned; diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index a32bd183a1d..a7b821703ce 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -115,8 +115,7 @@ oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/> @@ -1758,11 +1743,9 @@ = 0; --i) { + for (let i = tabs.length - 1; i >= 0; --i) { this.removeTab(tabs[i], {animate: true}); } - SessionStore.setNumberOfTabsClosedLast(window, numberOfTabsToClose); } ]]> @@ -1779,14 +1762,10 @@ let tabs = this.visibleTabs; this.selectedTab = aTab; - let closedTabs = 0; for (let i = tabs.length - 1; i >= 0; --i) { - if (tabs[i] != aTab && !tabs[i].pinned) { + if (tabs[i] != aTab && !tabs[i].pinned) this.removeTab(tabs[i], {animate: true}); - closedTabs++; - } } - SessionStore.setNumberOfTabsClosedLast(window, closedTabs); } ]]> @@ -1815,8 +1794,6 @@ var byMouse = aParams.byMouse; } - SessionStore.setNumberOfTabsClosedLast(window, 1); - // Handle requests for synchronously removing an already // asynchronously closing tab. if (!animate && diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index bbc5eb6bc9a..070a0d390bb 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -229,8 +229,6 @@ run-if = toolkit == "cocoa" [browser_bug839103.js] [browser_bug880101.js] [browser_bug882977.js] -[browser_bug887515.js] -[browser_bug896291_closeMaxSessionStoreTabs.js] [browser_bug902156.js] [browser_bug906190.js] [browser_canonizeURL.js] diff --git a/browser/base/content/test/general/browser_bug887515.js b/browser/base/content/test/general/browser_bug887515.js deleted file mode 100644 index 48796162f4b..00000000000 --- a/browser/base/content/test/general/browser_bug887515.js +++ /dev/null @@ -1,75 +0,0 @@ -function numClosedTabs() - SessionStore.getNumberOfTabsClosedLast(window); - -var originalTab; -var tab1Loaded = false; -var tab2Loaded = false; - -function verifyUndoMultipleClose() { - if (!tab1Loaded || !tab2Loaded) - return; - - gBrowser.removeAllTabsBut(originalTab); - updateTabContextMenu(); - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled."); - is(numClosedTabs(), 2, "There should be 2 closed tabs."); - is(gBrowser.tabs.length, 1, "There should only be 1 open tab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"), - "The label should be showing that the command will restore multiple tabs"); - undoCloseTab(); - - is(gBrowser.tabs.length, 3, "There should be 3 open tabs"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), - "The label should be showing that the command will restore a single tab"); - - gBrowser.removeTabsToTheEndFrom(originalTab); - updateTabContextMenu(); - ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled."); - is(numClosedTabs(), 2, "There should be 2 closed tabs."); - is(gBrowser.tabs.length, 1, "There should only be 1 open tab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"), - "The label should be showing that the command will restore multiple tabs"); - - finish(); -} - -function test() { - waitForExplicitFinish(); - - Services.prefs.setBoolPref("browser.tabs.animate", false); - registerCleanupFunction(function() { - Services.prefs.clearUserPref("browser.tabs.animate"); - originalTab.linkedBrowser.loadURI("about:blank"); - originalTab = null; - }); - - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), - "The label should be showing that the command will restore a single tab"); - - originalTab = gBrowser.selectedTab; - gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/"); - var tab1 = gBrowser.addTab("http://mochi.test:8888/"); - var tab2 = gBrowser.addTab("http://mochi.test:8888/"); - var browser1 = gBrowser.getBrowserForTab(tab1); - browser1.addEventListener("load", function onLoad1() { - browser1.removeEventListener("load", onLoad1, true); - tab1Loaded = true; - tab1 = null; - - verifyUndoMultipleClose(); - }, true); - var browser2 = gBrowser.getBrowserForTab(tab2); - browser2.addEventListener("load", function onLoad2() { - browser2.removeEventListener("load", onLoad2, true); - tab2Loaded = true; - tab2 = null; - - verifyUndoMultipleClose(); - }, true); -} diff --git a/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js b/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js deleted file mode 100644 index 98f0b35ace1..00000000000 --- a/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js +++ /dev/null @@ -1,108 +0,0 @@ -function numClosedTabs() - Cc["@mozilla.org/browser/sessionstore;1"]. - getService(Ci.nsISessionStore). - getNumberOfTabsClosedLast(window); - -let originalTab; -let maxTabsUndo; -let maxTabsUndoPlusOne; -let acceptRemoveAllTabsDialogListener; -let cancelRemoveAllTabsDialogListener; - -function test() { - waitForExplicitFinish(); - Services.prefs.setBoolPref("browser.tabs.animate", false); - - registerCleanupFunction(function() { - Services.prefs.clearUserPref("browser.tabs.animate"); - - originalTab.linkedBrowser.loadURI("about:blank"); - originalTab = null; - }); - - // Creating and throwing away this tab guarantees that the - // number of tabs closed in the previous tab-close operation is 1. - let throwaway_tab = gBrowser.addTab("http://mochi.test:8888/"); - gBrowser.removeTab(throwaway_tab); - - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), - "The label should be showing that the command will restore a single tab"); - - originalTab = gBrowser.selectedTab; - gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/"); - - maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo"); - maxTabsUndoPlusOne = maxTabsUndo + 1; - let numberOfTabsLoaded = 0; - for (let i = 0; i < maxTabsUndoPlusOne; i++) { - let tab = gBrowser.addTab("http://mochi.test:8888/"); - let browser = gBrowser.getBrowserForTab(tab); - browser.addEventListener("load", function onLoad() { - browser.removeEventListener("load", onLoad, true); - - if (++numberOfTabsLoaded == maxTabsUndoPlusOne) - verifyUndoMultipleClose(); - }, true); - } -} - -function verifyUndoMultipleClose() { - info("all tabs opened and loaded"); - cancelRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", cancelRemoveAllTabsDialog); - Services.wm.addListener(cancelRemoveAllTabsDialogListener); - gBrowser.removeAllTabsBut(originalTab); -} - -function cancelRemoveAllTabsDialog(domWindow) { - ok(true, "dialog appeared in response to multiple tab close action"); - domWindow.document.documentElement.cancelDialog(); - Services.wm.removeListener(cancelRemoveAllTabsDialogListener); - - acceptRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", acceptRemoveAllTabsDialog); - Services.wm.addListener(acceptRemoveAllTabsDialogListener); - waitForCondition(function () gBrowser.tabs.length == 1 + maxTabsUndoPlusOne, function verifyCancel() { - is(gBrowser.tabs.length, 1 + maxTabsUndoPlusOne, /* The '1 +' is for the original tab */ - "All tabs should still be open after the 'Cancel' option on the prompt is chosen"); - gBrowser.removeAllTabsBut(originalTab); - }, "Waited too long to find that no tabs were closed."); -} - -function acceptRemoveAllTabsDialog(domWindow) { - ok(true, "dialog appeared in response to multiple tab close action"); - domWindow.document.documentElement.acceptDialog(); - Services.wm.removeListener(acceptRemoveAllTabsDialogListener); - - waitForCondition(function () gBrowser.tabs.length == 1, function verifyAccept() { - is(gBrowser.tabs.length, 1, - "All other tabs should be closed after the 'OK' option on the prompt is chosen"); - finish(); - }, "Waited too long for the other tabs to be closed."); -} - -function WindowListener(aURL, aCallback) { - this.callback = aCallback; - this.url = aURL; -} -WindowListener.prototype = { - onOpenWindow: function(aXULWindow) { - var domWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - var self = this; - domWindow.addEventListener("load", function() { - domWindow.removeEventListener("load", arguments.callee, false); - - info("domWindow.document.location.href: " + domWindow.document.location.href); - if (domWindow.document.location.href != self.url) - return; - - // Allow other window load listeners to execute before passing to callback - executeSoon(function() { - self.callback(domWindow); - }); - }, false); - }, - onCloseWindow: function(aXULWindow) {}, - onWindowTitleChange: function(aXULWindow, aNewTitle) {} -} diff --git a/browser/components/sessionstore/nsISessionStore.idl b/browser/components/sessionstore/nsISessionStore.idl index d11c6fe4001..f8b9dca0923 100644 --- a/browser/components/sessionstore/nsISessionStore.idl +++ b/browser/components/sessionstore/nsISessionStore.idl @@ -25,7 +25,7 @@ interface nsIDOMNode; * |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|. */ -[scriptable, uuid(63a4d9f4-373f-11e3-a237-fa91a24410d2)] +[scriptable, uuid(0c99811f-6c5f-4a78-9c31-2d266d714175)] interface nsISessionStore : nsISupports { /** @@ -100,20 +100,6 @@ interface nsISessionStore : nsISupports nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab, [optional] in long aDelta); - /** - * Set the number of tabs that was closed during the last close-tabs - * operation. This helps us keep track of batch-close operations so - * we can restore multiple tabs at once. - */ - void setNumberOfTabsClosedLast(in nsIDOMWindow aWindow, in unsigned long aNumber); - - /** - * Get the number of tabs that was closed during the last close-tabs - * operation. This helps us keep track of batch-close operations so - * we can restore multiple tabs at once. - */ - unsigned long getNumberOfTabsClosedLast(in nsIDOMWindow aWindow); - /** * Get the number of restore-able tabs for a browser window */ diff --git a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm index 98a738444d1..fdc2dee7d6f 100644 --- a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm +++ b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm @@ -66,7 +66,7 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = { let restoreAllTabs = fragment.appendChild(doc.createElementNS(kNSXUL, aTagName)); restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName("menuRestoreAllTabs.label")); restoreAllTabs.setAttribute("oncommand", - "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab(0);"); + "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab();"); } return fragment; }, diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index 804541a7cab..278c1889b09 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -193,14 +193,6 @@ this.SessionStore = { return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta); }, - getNumberOfTabsClosedLast: function ss_getNumberOfTabsClosedLast(aWindow) { - return SessionStoreInternal.getNumberOfTabsClosedLast(aWindow); - }, - - setNumberOfTabsClosedLast: function ss_setNumberOfTabsClosedLast(aWindow, aNumber) { - return SessionStoreInternal.setNumberOfTabsClosedLast(aWindow, aNumber); - }, - getClosedTabCount: function ss_getClosedTabCount(aWindow) { return SessionStoreInternal.getClosedTabCount(aWindow); }, @@ -1616,35 +1608,6 @@ let SessionStoreInternal = { return newTab; }, - setNumberOfTabsClosedLast: function ssi_setNumberOfTabsClosedLast(aWindow, aNumber) { - if (this._disabledForMultiProcess) { - return; - } - - if (!("__SSi" in aWindow)) { - throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG); - } - - return NumberOfTabsClosedLastPerWindow.set(aWindow, aNumber); - }, - - /* Used to undo batch tab-close operations. Defaults to 1. */ - getNumberOfTabsClosedLast: function ssi_getNumberOfTabsClosedLast(aWindow) { - if (this._disabledForMultiProcess) { - return 0; - } - - if (!("__SSi" in aWindow)) { - throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG); - } - // Blank tabs cannot be undo-closed, so the number returned by - // the NumberOfTabsClosedLastPerWindow can be greater than the - // return value of getClosedTabCount. We won't restore blank - // tabs, so we return the minimum of these two values. - return Math.min(NumberOfTabsClosedLastPerWindow.get(aWindow) || 1, - this.getClosedTabCount(aWindow)); - }, - getClosedTabCount: function ssi_getClosedTabCount(aWindow) { if ("__SSi" in aWindow) { return this._windows[aWindow.__SSi]._closedTabs.length; @@ -4029,11 +3992,6 @@ let DirtyWindows = { } }; -// A map storing the number of tabs last closed per windoow. This only -// stores the most recent tab-close operation, and is used to undo -// batch tab-closing operations. -let NumberOfTabsClosedLastPerWindow = new WeakMap(); - // This is used to help meter the number of restoring tabs. This is the control // point for telling the next tab to restore. It gets attached to each gBrowser // via gBrowser.addTabsProgressListener diff --git a/browser/components/sessionstore/test/browser_345898.js b/browser/components/sessionstore/test/browser_345898.js index e5f3a1fe80b..c4dc7fbb432 100644 --- a/browser/components/sessionstore/test/browser_345898.js +++ b/browser/components/sessionstore/test/browser_345898.js @@ -40,8 +40,4 @@ function test() { "Invalid window for getWindowValue throws"); ok(test(function() ss.setWindowValue({}, "", "")), "Invalid window for setWindowValue throws"); - ok(test(function() ss.getNumberOfTabsClosedLast({})), - "Invalid window for getNumberOfTabsClosedLast throws"); - ok(test(function() ss.setNumberOfTabsClosedLast({}, 1)), - "Invalid window for setNumberOfTabsClosedLast throws"); } diff --git a/browser/components/tabview/test/browser_tabview_bug608037.js b/browser/components/tabview/test/browser_tabview_bug608037.js index a85e3aed99c..8e02d611fed 100644 --- a/browser/components/tabview/test/browser_tabview_bug608037.js +++ b/browser/components/tabview/test/browser_tabview_bug608037.js @@ -38,5 +38,5 @@ function onTabViewWindowLoaded() { gBrowser.removeTab(tabTwo); finish(); }); - }, 0); + }); } diff --git a/browser/components/tabview/test/browser_tabview_bug624847.js b/browser/components/tabview/test/browser_tabview_bug624847.js index e91430fd281..bc5cac4a0d4 100644 --- a/browser/components/tabview/test/browser_tabview_bug624847.js +++ b/browser/components/tabview/test/browser_tabview_bug624847.js @@ -64,7 +64,7 @@ function test() { createBlankTab(); afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab); - }, 0); + }); }); } @@ -94,7 +94,7 @@ function test() { gBrowser.removeTab(gBrowser.tabs[0]); afterAllTabsLoaded(finishTest); - }, 0); + }); }); } diff --git a/browser/components/tabview/test/browser_tabview_bug628270.js b/browser/components/tabview/test/browser_tabview_bug628270.js index c64c2e58416..4887c88a593 100644 --- a/browser/components/tabview/test/browser_tabview_bug628270.js +++ b/browser/components/tabview/test/browser_tabview_bug628270.js @@ -81,7 +81,7 @@ function test() { gBrowser.removeTab(gBrowser.tabs[1]); gBrowser.removeTab(gBrowser.tabs[1]); hideTabView(finishTest); - }, 0); + }); } waitForExplicitFinish(); diff --git a/browser/components/tabview/test/browser_tabview_bug706736.js b/browser/components/tabview/test/browser_tabview_bug706736.js index f47a7d3c5e8..60a1509d2d9 100644 --- a/browser/components/tabview/test/browser_tabview_bug706736.js +++ b/browser/components/tabview/test/browser_tabview_bug706736.js @@ -20,7 +20,7 @@ function test() { whenTabViewIsHidden(function() { win.gBrowser.removeTab(win.gBrowser.selectedTab); executeSoon(function() { - win.undoCloseTab(0); + win.undoCloseTab(); groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) { groupItemTwo.removeSubscriber("childAdded", onChildAdded); diff --git a/browser/components/tabview/test/head.js b/browser/components/tabview/test/head.js index 61eb5948c16..d5d9679c434 100644 --- a/browser/components/tabview/test/head.js +++ b/browser/components/tabview/test/head.js @@ -362,7 +362,7 @@ function newWindowWithState(state, callback) { function restoreTab(callback, index, win) { win = win || window; - let tab = win.undoCloseTab(index); + let tab = win.undoCloseTab(index || 0); let tabItem = tab._tabViewTabItem; let finalize = function () { diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 27b0d1375a8..0b5ff9e58b5 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -49,11 +49,6 @@ can reach it easily. --> - - From b212ee71eb89672d155fe4cac49eb735622f7b25 Mon Sep 17 00:00:00 2001 From: Victor Porof Date: Fri, 13 Dec 2013 22:28:04 +0200 Subject: [PATCH 04/17] Bug 948893 - Hovering certain bounds in a HTML source while paused in the debugger can sometimes incorrectly show a variable inspection popup, r=past --- browser/devtools/debugger/debugger-panes.js | 6 +++- browser/devtools/debugger/test/browser.ini | 2 ++ .../debugger/test/browser_dbg_parser-03.js | 34 +++++++++--------- .../debugger/test/browser_dbg_parser-04.js | 6 ++-- .../debugger/test/browser_dbg_parser-05.js | 6 ++-- .../browser_dbg_variables-view-popup-09.js | 33 +++++++++++++++++ .../debugger/test/doc_scope-variable-3.html | 23 ++++++++++++ browser/devtools/shared/Parser.jsm | 35 +++++++++++++------ 8 files changed, 111 insertions(+), 34 deletions(-) create mode 100644 browser/devtools/debugger/test/browser_dbg_variables-view-popup-09.js create mode 100644 browser/devtools/debugger/test/doc_scope-variable-3.html diff --git a/browser/devtools/debugger/debugger-panes.js b/browser/devtools/debugger/debugger-panes.js index 64bd229c771..352f6fdfd33 100644 --- a/browser/devtools/debugger/debugger-panes.js +++ b/browser/devtools/debugger/debugger-panes.js @@ -1357,7 +1357,11 @@ VariableBubbleView.prototype = { let scriptLine = hoveredLine - scriptLineOffset; let scriptColumn = hoveredColumn - scriptColumnOffset; - let identifierInfo = parsedSource.getIdentifierAt(scriptLine + 1, scriptColumn); + let identifierInfo = parsedSource.getIdentifierAt({ + line: scriptLine + 1, + column: scriptColumn, + scriptIndex: scriptInfo.index + }); // If the info is null, we're not hovering any identifier. if (!identifierInfo) { diff --git a/browser/devtools/debugger/test/browser.ini b/browser/devtools/debugger/test/browser.ini index 18c0e06dc62..47e9eeafdbf 100644 --- a/browser/devtools/debugger/test/browser.ini +++ b/browser/devtools/debugger/test/browser.ini @@ -52,6 +52,7 @@ support-files = doc_recursion-stack.html doc_scope-variable.html doc_scope-variable-2.html + doc_scope-variable-3.html doc_script-switching-01.html doc_script-switching-02.html doc_step-out.html @@ -222,6 +223,7 @@ support-files = [browser_dbg_variables-view-popup-06.js] [browser_dbg_variables-view-popup-07.js] [browser_dbg_variables-view-popup-08.js] +[browser_dbg_variables-view-popup-09.js] [browser_dbg_variables-view-reexpand-01.js] [browser_dbg_variables-view-reexpand-02.js] [browser_dbg_variables-view-webidl.js] diff --git a/browser/devtools/debugger/test/browser_dbg_parser-03.js b/browser/devtools/debugger/test/browser_dbg_parser-03.js index f206b8cce3b..a4c0fce9168 100644 --- a/browser/devtools/debugger/test/browser_dbg_parser-03.js +++ b/browser/devtools/debugger/test/browser_dbg_parser-03.js @@ -33,44 +33,44 @@ function test() { is(parsed.scriptCount, 3, "There should be 3 scripts parsed in the parent HTML source."); - is(parsed.getScriptInfo(0).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(0).toSource(), "({start:-1, length:-1, index:-1})", "There is no script at the beginning of the parent source."); - is(parsed.getScriptInfo(source.length - 1).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.length - 1).toSource(), "({start:-1, length:-1, index:-1})", "There is no script at the end of the parent source."); - is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:31, length:13})", + is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:31, length:13, index:0})", "The first script was located correctly."); - is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13})", + is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13, index:1})", "The second script was located correctly."); - is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:151, length:13})", + is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:151, length:13, index:2})", "The third script was located correctly."); - is(parsed.getScriptInfo(source.indexOf("let a") - 1).toSource(), "({start:31, length:13})", + is(parsed.getScriptInfo(source.indexOf("let a") - 1).toSource(), "({start:31, length:13, index:0})", "The left edge of the first script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let b") - 1).toSource(), "({start:85, length:13})", + is(parsed.getScriptInfo(source.indexOf("let b") - 1).toSource(), "({start:85, length:13, index:1})", "The left edge of the second script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let c") - 1).toSource(), "({start:151, length:13})", + is(parsed.getScriptInfo(source.indexOf("let c") - 1).toSource(), "({start:151, length:13, index:2})", "The left edge of the third script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let a") - 2).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let a") - 2).toSource(), "({start:-1, length:-1, index:-1})", "The left outside of the first script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let b") - 2).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let b") - 2).toSource(), "({start:-1, length:-1, index:-1})", "The left outside of the second script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let c") - 2).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let c") - 2).toSource(), "({start:-1, length:-1, index:-1})", "The left outside of the third script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let a") + 12).toSource(), "({start:31, length:13})", + is(parsed.getScriptInfo(source.indexOf("let a") + 12).toSource(), "({start:31, length:13, index:0})", "The right edge of the first script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let b") + 12).toSource(), "({start:85, length:13})", + is(parsed.getScriptInfo(source.indexOf("let b") + 12).toSource(), "({start:85, length:13, index:1})", "The right edge of the second script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let c") + 12).toSource(), "({start:151, length:13})", + is(parsed.getScriptInfo(source.indexOf("let c") + 12).toSource(), "({start:151, length:13, index:2})", "The right edge of the third script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let a") + 13).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let a") + 13).toSource(), "({start:-1, length:-1, index:-1})", "The right outside of the first script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let b") + 13).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let b") + 13).toSource(), "({start:-1, length:-1, index:-1})", "The right outside of the second script was interpreted correctly."); - is(parsed.getScriptInfo(source.indexOf("let c") + 13).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let c") + 13).toSource(), "({start:-1, length:-1, index:-1})", "The right outside of the third script was interpreted correctly."); finish(); diff --git a/browser/devtools/debugger/test/browser_dbg_parser-04.js b/browser/devtools/debugger/test/browser_dbg_parser-04.js index 82db773cdf7..2ef653bf367 100644 --- a/browser/devtools/debugger/test/browser_dbg_parser-04.js +++ b/browser/devtools/debugger/test/browser_dbg_parser-04.js @@ -43,11 +43,11 @@ function test() { is(parsed.scriptCount, 1, "There should be 1 script parsed in the parent HTML source."); - is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:-1, length:-1, index:-1})", "The first script shouldn't be considered valid."); - is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13})", + is(parsed.getScriptInfo(source.indexOf("let b")).toSource(), "({start:85, length:13, index:0})", "The second script was located correctly."); - is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:-1, length:-1})", + is(parsed.getScriptInfo(source.indexOf("let c")).toSource(), "({start:-1, length:-1, index:-1})", "The third script shouldn't be considered valid."); finish(); diff --git a/browser/devtools/debugger/test/browser_dbg_parser-05.js b/browser/devtools/debugger/test/browser_dbg_parser-05.js index ef598874522..4c1d935a269 100644 --- a/browser/devtools/debugger/test/browser_dbg_parser-05.js +++ b/browser/devtools/debugger/test/browser_dbg_parser-05.js @@ -32,11 +32,11 @@ function test() { is(parsed.scriptCount, 1, "There should be 1 script parsed in the parent source."); - is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:0, length:261})", + is(parsed.getScriptInfo(source.indexOf("let a")).toSource(), "({start:0, length:261, index:0})", "The script location is correct (1)."); - is(parsed.getScriptInfo(source.indexOf("")).toSource(), "({start:0, length:261})", + is(parsed.getScriptInfo(source.indexOf("")).toSource(), "({start:0, length:261, index:0})", "The script location is correct (3)."); finish(); diff --git a/browser/devtools/debugger/test/browser_dbg_variables-view-popup-09.js b/browser/devtools/debugger/test/browser_dbg_variables-view-popup-09.js new file mode 100644 index 00000000000..0a58983e330 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_variables-view-popup-09.js @@ -0,0 +1,33 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests opening inspecting variables works across scopes. + */ + +const TAB_URL = EXAMPLE_URL + "doc_scope-variable-3.html"; + +function test() { + Task.spawn(function() { + let [tab, debuggee, panel] = yield initDebugger(TAB_URL); + let win = panel.panelWin; + let bubble = win.DebuggerView.VariableBubble; + let tooltip = bubble._tooltip.panel; + + // Allow this generator function to yield first. + executeSoon(() => debuggee.test()); + yield waitForSourceAndCaretAndScopes(panel, ".html", 15); + + yield openVarPopup(panel, { line: 12, ch: 10 }); + ok(true, "The variable inspection popup was shown for the real variable."); + + once(tooltip, "popupshown").then(() => { + ok(false, "The variable inspection popup shouldn't have been opened."); + }); + + reopenVarPopup(panel, { line: 18, ch: 10 }); + yield waitForTime(1000); + + yield resumeDebuggerThenCloseAndFinish(panel); + }); +} diff --git a/browser/devtools/debugger/test/doc_scope-variable-3.html b/browser/devtools/debugger/test/doc_scope-variable-3.html new file mode 100644 index 00000000000..fcd45cc0aa9 --- /dev/null +++ b/browser/devtools/debugger/test/doc_scope-variable-3.html @@ -0,0 +1,23 @@ + + + + + + + Debugger test page + + + + + + + + diff --git a/browser/devtools/shared/Parser.jsm b/browser/devtools/shared/Parser.jsm index ebf4b32f836..822e95f3879 100644 --- a/browser/devtools/shared/Parser.jsm +++ b/browser/devtools/shared/Parser.jsm @@ -132,15 +132,15 @@ SyntaxTreesPool.prototype = { /** * @see SyntaxTree.prototype.getIdentifierAt */ - getIdentifierAt: function(aLine, aColumn) { - return this._first(this._call("getIdentifierAt", aLine, aColumn)); + getIdentifierAt: function({ line, column, scriptIndex }) { + return this._first(this._call("getIdentifierAt", scriptIndex, line, column)); }, /** * @see SyntaxTree.prototype.getNamedFunctionDefinitions */ getNamedFunctionDefinitions: function(aSubstring) { - return this._call("getNamedFunctionDefinitions", aSubstring); + return this._call("getNamedFunctionDefinitions", -1, aSubstring); }, /** @@ -161,12 +161,19 @@ SyntaxTreesPool.prototype = { * The offset and length relative to the enclosing script. */ getScriptInfo: function(aOffset) { + let info = { start: -1, length: -1, index: -1 }; + for (let { offset, length } of this._trees) { - if (offset <= aOffset && offset + length >= aOffset) { - return { start: offset, length: length }; + info.index++; + if (offset <= aOffset && offset + length >= aOffset) { + info.start = offset; + info.length = length; + return info; } } - return { start: -1, length: -1 }; + + info.index = -1; + return info; }, /** @@ -182,23 +189,31 @@ SyntaxTreesPool.prototype = { }, /** - * Handles a request for all known syntax trees. + * Handles a request for a specific or all known syntax trees. * * @param string aFunction * The function name to call on the SyntaxTree instances. + * @param number aSyntaxTreeIndex + * The syntax tree for which to handle the request. If the tree at + * the specified index isn't found, the accumulated results for all + * syntax trees are returned. * @param any aParams * Any kind params to pass to the request function. * @return array * The results given by all known syntax trees. */ - _call: function(aFunction, ...aParams) { + _call: function(aFunction, aSyntaxTreeIndex, ...aParams) { let results = []; - let requestId = aFunction + aParams.toSource(); // Cache all the things! + let requestId = [aFunction, aSyntaxTreeIndex, aParams].toSource(); if (this._cache.has(requestId)) { return this._cache.get(requestId); } - for (let syntaxTree of this._trees) { + + let requestedTree = this._trees[aSyntaxTreeIndex]; + let targettedTrees = requestedTree ? [requestedTree] : this._trees; + + for (let syntaxTree of targettedTrees) { try { results.push({ sourceUrl: syntaxTree.url, From d9450e4f5a7505609d30aec09463dc86ab451a2f Mon Sep 17 00:00:00 2001 From: Jared Wein Date: Fri, 13 Dec 2013 15:57:31 -0500 Subject: [PATCH 05/17] Backed out changeset 1566f020e34e (bug 931891) for timing out some mochitest-bc tests on a CLOSED TREE --HG-- extra : rebase_source : 423f491a0bedebfd9350167340d6f65d275a64cd --- browser/base/content/browser.js | 40 +++++-- browser/base/content/browser.xul | 3 +- browser/base/content/tabbrowser.xml | 45 ++++++-- browser/base/content/test/general/browser.ini | 2 + .../content/test/general/browser_bug887515.js | 75 ++++++++++++ ...wser_bug896291_closeMaxSessionStoreTabs.js | 108 ++++++++++++++++++ .../sessionstore/nsISessionStore.idl | 16 ++- .../RecentlyClosedTabsAndWindowsMenuUtils.jsm | 2 +- .../sessionstore/src/SessionStore.jsm | 42 +++++++ .../sessionstore/test/browser_345898.js | 4 + .../tabview/test/browser_tabview_bug608037.js | 2 +- .../tabview/test/browser_tabview_bug624847.js | 4 +- .../tabview/test/browser_tabview_bug628270.js | 2 +- .../tabview/test/browser_tabview_bug706736.js | 2 +- browser/components/tabview/test/head.js | 2 +- .../locales/en-US/chrome/browser/browser.dtd | 5 + 16 files changed, 325 insertions(+), 29 deletions(-) create mode 100644 browser/base/content/test/general/browser_bug887515.js create mode 100644 browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index a247de742b1..b7adb8dde39 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6101,16 +6101,33 @@ function undoCloseTab(aIndex) { if (gBrowser.tabs.length == 1 && isTabEmpty(gBrowser.selectedTab)) blankTabToRemove = gBrowser.selectedTab; - var tab = null; - if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) { - TabView.prepareUndoCloseTab(blankTabToRemove); - tab = SessionStore.undoCloseTab(window, aIndex || 0); - TabView.afterUndoCloseTab(); + let numberOfTabsToUndoClose = 0; + let index = Number(aIndex); - if (blankTabToRemove) - gBrowser.removeTab(blankTabToRemove); + + if (isNaN(index)) { + index = 0; + numberOfTabsToUndoClose = SessionStore.getNumberOfTabsClosedLast(window); + } else { + if (0 > index || index >= SessionStore.getClosedTabCount(window)) + return null; + numberOfTabsToUndoClose = 1; } + let tab = null; + while (numberOfTabsToUndoClose > 0 && + numberOfTabsToUndoClose--) { + TabView.prepareUndoCloseTab(blankTabToRemove); + tab = SessionStore.undoCloseTab(window, index); + TabView.afterUndoCloseTab(); + if (blankTabToRemove) { + gBrowser.removeTab(blankTabToRemove); + blankTabToRemove = null; + } + } + + // Reset the number of tabs closed last time to the default. + SessionStore.setNumberOfTabsClosedLast(window, 1); return tab; } @@ -6933,8 +6950,13 @@ var TabContextMenu = { menuItem.disabled = disabled; // Session store - document.getElementById("context_undoCloseTab").disabled = - SessionStore.getClosedTabCount(window) == 0; + let undoCloseTabElement = document.getElementById("context_undoCloseTab"); + let closedTabCount = SessionStore.getNumberOfTabsClosedLast(window); + undoCloseTabElement.disabled = closedTabCount == 0; + // Change the label of "Undo Close Tab" to specify if it will undo a batch-close + // or a single close. + let visibleLabel = closedTabCount <= 1 ? "singletablabel" : "multipletablabel"; + undoCloseTabElement.setAttribute("label", undoCloseTabElement.getAttribute(visibleLabel)); // Only one of pin/unpin should be visible document.getElementById("context_pinTab").hidden = this.contextTab.pinned; diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index a7b821703ce..a32bd183a1d 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -115,7 +115,8 @@ oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/> @@ -1743,9 +1758,11 @@ = 0; --i) { + let numberOfTabsToClose = tabs.length; + for (let i = numberOfTabsToClose - 1; i >= 0; --i) { this.removeTab(tabs[i], {animate: true}); } + SessionStore.setNumberOfTabsClosedLast(window, numberOfTabsToClose); } ]]> @@ -1762,10 +1779,14 @@ let tabs = this.visibleTabs; this.selectedTab = aTab; + let closedTabs = 0; for (let i = tabs.length - 1; i >= 0; --i) { - if (tabs[i] != aTab && !tabs[i].pinned) + if (tabs[i] != aTab && !tabs[i].pinned) { this.removeTab(tabs[i], {animate: true}); + closedTabs++; + } } + SessionStore.setNumberOfTabsClosedLast(window, closedTabs); } ]]> @@ -1794,6 +1815,8 @@ var byMouse = aParams.byMouse; } + SessionStore.setNumberOfTabsClosedLast(window, 1); + // Handle requests for synchronously removing an already // asynchronously closing tab. if (!animate && diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index 070a0d390bb..bbc5eb6bc9a 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -229,6 +229,8 @@ run-if = toolkit == "cocoa" [browser_bug839103.js] [browser_bug880101.js] [browser_bug882977.js] +[browser_bug887515.js] +[browser_bug896291_closeMaxSessionStoreTabs.js] [browser_bug902156.js] [browser_bug906190.js] [browser_canonizeURL.js] diff --git a/browser/base/content/test/general/browser_bug887515.js b/browser/base/content/test/general/browser_bug887515.js new file mode 100644 index 00000000000..48796162f4b --- /dev/null +++ b/browser/base/content/test/general/browser_bug887515.js @@ -0,0 +1,75 @@ +function numClosedTabs() + SessionStore.getNumberOfTabsClosedLast(window); + +var originalTab; +var tab1Loaded = false; +var tab2Loaded = false; + +function verifyUndoMultipleClose() { + if (!tab1Loaded || !tab2Loaded) + return; + + gBrowser.removeAllTabsBut(originalTab); + updateTabContextMenu(); + let undoCloseTabElement = document.getElementById("context_undoCloseTab"); + ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled."); + is(numClosedTabs(), 2, "There should be 2 closed tabs."); + is(gBrowser.tabs.length, 1, "There should only be 1 open tab"); + updateTabContextMenu(); + is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"), + "The label should be showing that the command will restore multiple tabs"); + undoCloseTab(); + + is(gBrowser.tabs.length, 3, "There should be 3 open tabs"); + updateTabContextMenu(); + is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), + "The label should be showing that the command will restore a single tab"); + + gBrowser.removeTabsToTheEndFrom(originalTab); + updateTabContextMenu(); + ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled."); + is(numClosedTabs(), 2, "There should be 2 closed tabs."); + is(gBrowser.tabs.length, 1, "There should only be 1 open tab"); + updateTabContextMenu(); + is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"), + "The label should be showing that the command will restore multiple tabs"); + + finish(); +} + +function test() { + waitForExplicitFinish(); + + Services.prefs.setBoolPref("browser.tabs.animate", false); + registerCleanupFunction(function() { + Services.prefs.clearUserPref("browser.tabs.animate"); + originalTab.linkedBrowser.loadURI("about:blank"); + originalTab = null; + }); + + let undoCloseTabElement = document.getElementById("context_undoCloseTab"); + updateTabContextMenu(); + is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), + "The label should be showing that the command will restore a single tab"); + + originalTab = gBrowser.selectedTab; + gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/"); + var tab1 = gBrowser.addTab("http://mochi.test:8888/"); + var tab2 = gBrowser.addTab("http://mochi.test:8888/"); + var browser1 = gBrowser.getBrowserForTab(tab1); + browser1.addEventListener("load", function onLoad1() { + browser1.removeEventListener("load", onLoad1, true); + tab1Loaded = true; + tab1 = null; + + verifyUndoMultipleClose(); + }, true); + var browser2 = gBrowser.getBrowserForTab(tab2); + browser2.addEventListener("load", function onLoad2() { + browser2.removeEventListener("load", onLoad2, true); + tab2Loaded = true; + tab2 = null; + + verifyUndoMultipleClose(); + }, true); +} diff --git a/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js b/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js new file mode 100644 index 00000000000..98f0b35ace1 --- /dev/null +++ b/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js @@ -0,0 +1,108 @@ +function numClosedTabs() + Cc["@mozilla.org/browser/sessionstore;1"]. + getService(Ci.nsISessionStore). + getNumberOfTabsClosedLast(window); + +let originalTab; +let maxTabsUndo; +let maxTabsUndoPlusOne; +let acceptRemoveAllTabsDialogListener; +let cancelRemoveAllTabsDialogListener; + +function test() { + waitForExplicitFinish(); + Services.prefs.setBoolPref("browser.tabs.animate", false); + + registerCleanupFunction(function() { + Services.prefs.clearUserPref("browser.tabs.animate"); + + originalTab.linkedBrowser.loadURI("about:blank"); + originalTab = null; + }); + + // Creating and throwing away this tab guarantees that the + // number of tabs closed in the previous tab-close operation is 1. + let throwaway_tab = gBrowser.addTab("http://mochi.test:8888/"); + gBrowser.removeTab(throwaway_tab); + + let undoCloseTabElement = document.getElementById("context_undoCloseTab"); + updateTabContextMenu(); + is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), + "The label should be showing that the command will restore a single tab"); + + originalTab = gBrowser.selectedTab; + gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/"); + + maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo"); + maxTabsUndoPlusOne = maxTabsUndo + 1; + let numberOfTabsLoaded = 0; + for (let i = 0; i < maxTabsUndoPlusOne; i++) { + let tab = gBrowser.addTab("http://mochi.test:8888/"); + let browser = gBrowser.getBrowserForTab(tab); + browser.addEventListener("load", function onLoad() { + browser.removeEventListener("load", onLoad, true); + + if (++numberOfTabsLoaded == maxTabsUndoPlusOne) + verifyUndoMultipleClose(); + }, true); + } +} + +function verifyUndoMultipleClose() { + info("all tabs opened and loaded"); + cancelRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", cancelRemoveAllTabsDialog); + Services.wm.addListener(cancelRemoveAllTabsDialogListener); + gBrowser.removeAllTabsBut(originalTab); +} + +function cancelRemoveAllTabsDialog(domWindow) { + ok(true, "dialog appeared in response to multiple tab close action"); + domWindow.document.documentElement.cancelDialog(); + Services.wm.removeListener(cancelRemoveAllTabsDialogListener); + + acceptRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", acceptRemoveAllTabsDialog); + Services.wm.addListener(acceptRemoveAllTabsDialogListener); + waitForCondition(function () gBrowser.tabs.length == 1 + maxTabsUndoPlusOne, function verifyCancel() { + is(gBrowser.tabs.length, 1 + maxTabsUndoPlusOne, /* The '1 +' is for the original tab */ + "All tabs should still be open after the 'Cancel' option on the prompt is chosen"); + gBrowser.removeAllTabsBut(originalTab); + }, "Waited too long to find that no tabs were closed."); +} + +function acceptRemoveAllTabsDialog(domWindow) { + ok(true, "dialog appeared in response to multiple tab close action"); + domWindow.document.documentElement.acceptDialog(); + Services.wm.removeListener(acceptRemoveAllTabsDialogListener); + + waitForCondition(function () gBrowser.tabs.length == 1, function verifyAccept() { + is(gBrowser.tabs.length, 1, + "All other tabs should be closed after the 'OK' option on the prompt is chosen"); + finish(); + }, "Waited too long for the other tabs to be closed."); +} + +function WindowListener(aURL, aCallback) { + this.callback = aCallback; + this.url = aURL; +} +WindowListener.prototype = { + onOpenWindow: function(aXULWindow) { + var domWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + var self = this; + domWindow.addEventListener("load", function() { + domWindow.removeEventListener("load", arguments.callee, false); + + info("domWindow.document.location.href: " + domWindow.document.location.href); + if (domWindow.document.location.href != self.url) + return; + + // Allow other window load listeners to execute before passing to callback + executeSoon(function() { + self.callback(domWindow); + }); + }, false); + }, + onCloseWindow: function(aXULWindow) {}, + onWindowTitleChange: function(aXULWindow, aNewTitle) {} +} diff --git a/browser/components/sessionstore/nsISessionStore.idl b/browser/components/sessionstore/nsISessionStore.idl index f8b9dca0923..d11c6fe4001 100644 --- a/browser/components/sessionstore/nsISessionStore.idl +++ b/browser/components/sessionstore/nsISessionStore.idl @@ -25,7 +25,7 @@ interface nsIDOMNode; * |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|. */ -[scriptable, uuid(0c99811f-6c5f-4a78-9c31-2d266d714175)] +[scriptable, uuid(63a4d9f4-373f-11e3-a237-fa91a24410d2)] interface nsISessionStore : nsISupports { /** @@ -100,6 +100,20 @@ interface nsISessionStore : nsISupports nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab, [optional] in long aDelta); + /** + * Set the number of tabs that was closed during the last close-tabs + * operation. This helps us keep track of batch-close operations so + * we can restore multiple tabs at once. + */ + void setNumberOfTabsClosedLast(in nsIDOMWindow aWindow, in unsigned long aNumber); + + /** + * Get the number of tabs that was closed during the last close-tabs + * operation. This helps us keep track of batch-close operations so + * we can restore multiple tabs at once. + */ + unsigned long getNumberOfTabsClosedLast(in nsIDOMWindow aWindow); + /** * Get the number of restore-able tabs for a browser window */ diff --git a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm index fdc2dee7d6f..98a738444d1 100644 --- a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm +++ b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm @@ -66,7 +66,7 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = { let restoreAllTabs = fragment.appendChild(doc.createElementNS(kNSXUL, aTagName)); restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName("menuRestoreAllTabs.label")); restoreAllTabs.setAttribute("oncommand", - "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab();"); + "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab(0);"); } return fragment; }, diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index 278c1889b09..804541a7cab 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -193,6 +193,14 @@ this.SessionStore = { return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta); }, + getNumberOfTabsClosedLast: function ss_getNumberOfTabsClosedLast(aWindow) { + return SessionStoreInternal.getNumberOfTabsClosedLast(aWindow); + }, + + setNumberOfTabsClosedLast: function ss_setNumberOfTabsClosedLast(aWindow, aNumber) { + return SessionStoreInternal.setNumberOfTabsClosedLast(aWindow, aNumber); + }, + getClosedTabCount: function ss_getClosedTabCount(aWindow) { return SessionStoreInternal.getClosedTabCount(aWindow); }, @@ -1608,6 +1616,35 @@ let SessionStoreInternal = { return newTab; }, + setNumberOfTabsClosedLast: function ssi_setNumberOfTabsClosedLast(aWindow, aNumber) { + if (this._disabledForMultiProcess) { + return; + } + + if (!("__SSi" in aWindow)) { + throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG); + } + + return NumberOfTabsClosedLastPerWindow.set(aWindow, aNumber); + }, + + /* Used to undo batch tab-close operations. Defaults to 1. */ + getNumberOfTabsClosedLast: function ssi_getNumberOfTabsClosedLast(aWindow) { + if (this._disabledForMultiProcess) { + return 0; + } + + if (!("__SSi" in aWindow)) { + throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG); + } + // Blank tabs cannot be undo-closed, so the number returned by + // the NumberOfTabsClosedLastPerWindow can be greater than the + // return value of getClosedTabCount. We won't restore blank + // tabs, so we return the minimum of these two values. + return Math.min(NumberOfTabsClosedLastPerWindow.get(aWindow) || 1, + this.getClosedTabCount(aWindow)); + }, + getClosedTabCount: function ssi_getClosedTabCount(aWindow) { if ("__SSi" in aWindow) { return this._windows[aWindow.__SSi]._closedTabs.length; @@ -3992,6 +4029,11 @@ let DirtyWindows = { } }; +// A map storing the number of tabs last closed per windoow. This only +// stores the most recent tab-close operation, and is used to undo +// batch tab-closing operations. +let NumberOfTabsClosedLastPerWindow = new WeakMap(); + // This is used to help meter the number of restoring tabs. This is the control // point for telling the next tab to restore. It gets attached to each gBrowser // via gBrowser.addTabsProgressListener diff --git a/browser/components/sessionstore/test/browser_345898.js b/browser/components/sessionstore/test/browser_345898.js index c4dc7fbb432..e5f3a1fe80b 100644 --- a/browser/components/sessionstore/test/browser_345898.js +++ b/browser/components/sessionstore/test/browser_345898.js @@ -40,4 +40,8 @@ function test() { "Invalid window for getWindowValue throws"); ok(test(function() ss.setWindowValue({}, "", "")), "Invalid window for setWindowValue throws"); + ok(test(function() ss.getNumberOfTabsClosedLast({})), + "Invalid window for getNumberOfTabsClosedLast throws"); + ok(test(function() ss.setNumberOfTabsClosedLast({}, 1)), + "Invalid window for setNumberOfTabsClosedLast throws"); } diff --git a/browser/components/tabview/test/browser_tabview_bug608037.js b/browser/components/tabview/test/browser_tabview_bug608037.js index 8e02d611fed..a85e3aed99c 100644 --- a/browser/components/tabview/test/browser_tabview_bug608037.js +++ b/browser/components/tabview/test/browser_tabview_bug608037.js @@ -38,5 +38,5 @@ function onTabViewWindowLoaded() { gBrowser.removeTab(tabTwo); finish(); }); - }); + }, 0); } diff --git a/browser/components/tabview/test/browser_tabview_bug624847.js b/browser/components/tabview/test/browser_tabview_bug624847.js index bc5cac4a0d4..e91430fd281 100644 --- a/browser/components/tabview/test/browser_tabview_bug624847.js +++ b/browser/components/tabview/test/browser_tabview_bug624847.js @@ -64,7 +64,7 @@ function test() { createBlankTab(); afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab); - }); + }, 0); }); } @@ -94,7 +94,7 @@ function test() { gBrowser.removeTab(gBrowser.tabs[0]); afterAllTabsLoaded(finishTest); - }); + }, 0); }); } diff --git a/browser/components/tabview/test/browser_tabview_bug628270.js b/browser/components/tabview/test/browser_tabview_bug628270.js index 4887c88a593..c64c2e58416 100644 --- a/browser/components/tabview/test/browser_tabview_bug628270.js +++ b/browser/components/tabview/test/browser_tabview_bug628270.js @@ -81,7 +81,7 @@ function test() { gBrowser.removeTab(gBrowser.tabs[1]); gBrowser.removeTab(gBrowser.tabs[1]); hideTabView(finishTest); - }); + }, 0); } waitForExplicitFinish(); diff --git a/browser/components/tabview/test/browser_tabview_bug706736.js b/browser/components/tabview/test/browser_tabview_bug706736.js index 60a1509d2d9..f47a7d3c5e8 100644 --- a/browser/components/tabview/test/browser_tabview_bug706736.js +++ b/browser/components/tabview/test/browser_tabview_bug706736.js @@ -20,7 +20,7 @@ function test() { whenTabViewIsHidden(function() { win.gBrowser.removeTab(win.gBrowser.selectedTab); executeSoon(function() { - win.undoCloseTab(); + win.undoCloseTab(0); groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) { groupItemTwo.removeSubscriber("childAdded", onChildAdded); diff --git a/browser/components/tabview/test/head.js b/browser/components/tabview/test/head.js index d5d9679c434..61eb5948c16 100644 --- a/browser/components/tabview/test/head.js +++ b/browser/components/tabview/test/head.js @@ -362,7 +362,7 @@ function newWindowWithState(state, callback) { function restoreTab(callback, index, win) { win = win || window; - let tab = win.undoCloseTab(index || 0); + let tab = win.undoCloseTab(index); let tabItem = tab._tabViewTabItem; let finalize = function () { diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 0b5ff9e58b5..27b0d1375a8 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -49,6 +49,11 @@ can reach it easily. --> + + From 00e7e3038af5dcfe011f468d0f0482c09eb19048 Mon Sep 17 00:00:00 2001 From: Jared Wein Date: Fri, 13 Dec 2013 16:55:49 -0500 Subject: [PATCH 06/17] Bug 931891 - [Australis] Backed out bug 887515 and the followups (bug 896291, 909662, 896896). r=ttaubert --HG-- extra : rebase_source : f6568e83e23c036f21270e0f133ee06b192b1f9e --- browser/base/content/browser.js | 36 ++---- browser/base/content/browser.xul | 3 +- browser/base/content/tabbrowser.xml | 45 ++------ browser/base/content/test/general/browser.ini | 2 - .../content/test/general/browser_bug887515.js | 75 ------------ ...wser_bug896291_closeMaxSessionStoreTabs.js | 108 ------------------ .../content/test/general/browser_bug906190.js | 15 ++- .../sessionstore/nsISessionStore.idl | 16 +-- .../RecentlyClosedTabsAndWindowsMenuUtils.jsm | 2 +- .../sessionstore/src/SessionStore.jsm | 42 ------- .../sessionstore/test/browser_345898.js | 4 - .../tabview/test/browser_tabview_bug608037.js | 2 +- .../tabview/test/browser_tabview_bug624847.js | 4 +- .../tabview/test/browser_tabview_bug628270.js | 2 +- .../tabview/test/browser_tabview_bug706736.js | 2 +- browser/components/tabview/test/head.js | 2 +- .../locales/en-US/chrome/browser/browser.dtd | 5 - 17 files changed, 37 insertions(+), 328 deletions(-) delete mode 100644 browser/base/content/test/general/browser_bug887515.js delete mode 100644 browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index b7adb8dde39..a247de742b1 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6101,33 +6101,16 @@ function undoCloseTab(aIndex) { if (gBrowser.tabs.length == 1 && isTabEmpty(gBrowser.selectedTab)) blankTabToRemove = gBrowser.selectedTab; - let numberOfTabsToUndoClose = 0; - let index = Number(aIndex); - - - if (isNaN(index)) { - index = 0; - numberOfTabsToUndoClose = SessionStore.getNumberOfTabsClosedLast(window); - } else { - if (0 > index || index >= SessionStore.getClosedTabCount(window)) - return null; - numberOfTabsToUndoClose = 1; - } - - let tab = null; - while (numberOfTabsToUndoClose > 0 && - numberOfTabsToUndoClose--) { + var tab = null; + if (SessionStore.getClosedTabCount(window) > (aIndex || 0)) { TabView.prepareUndoCloseTab(blankTabToRemove); - tab = SessionStore.undoCloseTab(window, index); + tab = SessionStore.undoCloseTab(window, aIndex || 0); TabView.afterUndoCloseTab(); - if (blankTabToRemove) { + + if (blankTabToRemove) gBrowser.removeTab(blankTabToRemove); - blankTabToRemove = null; - } } - // Reset the number of tabs closed last time to the default. - SessionStore.setNumberOfTabsClosedLast(window, 1); return tab; } @@ -6950,13 +6933,8 @@ var TabContextMenu = { menuItem.disabled = disabled; // Session store - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - let closedTabCount = SessionStore.getNumberOfTabsClosedLast(window); - undoCloseTabElement.disabled = closedTabCount == 0; - // Change the label of "Undo Close Tab" to specify if it will undo a batch-close - // or a single close. - let visibleLabel = closedTabCount <= 1 ? "singletablabel" : "multipletablabel"; - undoCloseTabElement.setAttribute("label", undoCloseTabElement.getAttribute(visibleLabel)); + document.getElementById("context_undoCloseTab").disabled = + SessionStore.getClosedTabCount(window) == 0; // Only one of pin/unpin should be visible document.getElementById("context_pinTab").hidden = this.contextTab.pinned; diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index a32bd183a1d..a7b821703ce 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -115,8 +115,7 @@ oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/> @@ -1758,11 +1743,9 @@ = 0; --i) { + for (let i = tabs.length - 1; i >= 0; --i) { this.removeTab(tabs[i], {animate: true}); } - SessionStore.setNumberOfTabsClosedLast(window, numberOfTabsToClose); } ]]> @@ -1779,14 +1762,10 @@ let tabs = this.visibleTabs; this.selectedTab = aTab; - let closedTabs = 0; for (let i = tabs.length - 1; i >= 0; --i) { - if (tabs[i] != aTab && !tabs[i].pinned) { + if (tabs[i] != aTab && !tabs[i].pinned) this.removeTab(tabs[i], {animate: true}); - closedTabs++; - } } - SessionStore.setNumberOfTabsClosedLast(window, closedTabs); } ]]> @@ -1815,8 +1794,6 @@ var byMouse = aParams.byMouse; } - SessionStore.setNumberOfTabsClosedLast(window, 1); - // Handle requests for synchronously removing an already // asynchronously closing tab. if (!animate && diff --git a/browser/base/content/test/general/browser.ini b/browser/base/content/test/general/browser.ini index bbc5eb6bc9a..070a0d390bb 100644 --- a/browser/base/content/test/general/browser.ini +++ b/browser/base/content/test/general/browser.ini @@ -229,8 +229,6 @@ run-if = toolkit == "cocoa" [browser_bug839103.js] [browser_bug880101.js] [browser_bug882977.js] -[browser_bug887515.js] -[browser_bug896291_closeMaxSessionStoreTabs.js] [browser_bug902156.js] [browser_bug906190.js] [browser_canonizeURL.js] diff --git a/browser/base/content/test/general/browser_bug887515.js b/browser/base/content/test/general/browser_bug887515.js deleted file mode 100644 index 48796162f4b..00000000000 --- a/browser/base/content/test/general/browser_bug887515.js +++ /dev/null @@ -1,75 +0,0 @@ -function numClosedTabs() - SessionStore.getNumberOfTabsClosedLast(window); - -var originalTab; -var tab1Loaded = false; -var tab2Loaded = false; - -function verifyUndoMultipleClose() { - if (!tab1Loaded || !tab2Loaded) - return; - - gBrowser.removeAllTabsBut(originalTab); - updateTabContextMenu(); - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled."); - is(numClosedTabs(), 2, "There should be 2 closed tabs."); - is(gBrowser.tabs.length, 1, "There should only be 1 open tab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"), - "The label should be showing that the command will restore multiple tabs"); - undoCloseTab(); - - is(gBrowser.tabs.length, 3, "There should be 3 open tabs"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), - "The label should be showing that the command will restore a single tab"); - - gBrowser.removeTabsToTheEndFrom(originalTab); - updateTabContextMenu(); - ok(!undoCloseTabElement.disabled, "Undo Close Tabs should be enabled."); - is(numClosedTabs(), 2, "There should be 2 closed tabs."); - is(gBrowser.tabs.length, 1, "There should only be 1 open tab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("multipletablabel"), - "The label should be showing that the command will restore multiple tabs"); - - finish(); -} - -function test() { - waitForExplicitFinish(); - - Services.prefs.setBoolPref("browser.tabs.animate", false); - registerCleanupFunction(function() { - Services.prefs.clearUserPref("browser.tabs.animate"); - originalTab.linkedBrowser.loadURI("about:blank"); - originalTab = null; - }); - - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), - "The label should be showing that the command will restore a single tab"); - - originalTab = gBrowser.selectedTab; - gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/"); - var tab1 = gBrowser.addTab("http://mochi.test:8888/"); - var tab2 = gBrowser.addTab("http://mochi.test:8888/"); - var browser1 = gBrowser.getBrowserForTab(tab1); - browser1.addEventListener("load", function onLoad1() { - browser1.removeEventListener("load", onLoad1, true); - tab1Loaded = true; - tab1 = null; - - verifyUndoMultipleClose(); - }, true); - var browser2 = gBrowser.getBrowserForTab(tab2); - browser2.addEventListener("load", function onLoad2() { - browser2.removeEventListener("load", onLoad2, true); - tab2Loaded = true; - tab2 = null; - - verifyUndoMultipleClose(); - }, true); -} diff --git a/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js b/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js deleted file mode 100644 index 98f0b35ace1..00000000000 --- a/browser/base/content/test/general/browser_bug896291_closeMaxSessionStoreTabs.js +++ /dev/null @@ -1,108 +0,0 @@ -function numClosedTabs() - Cc["@mozilla.org/browser/sessionstore;1"]. - getService(Ci.nsISessionStore). - getNumberOfTabsClosedLast(window); - -let originalTab; -let maxTabsUndo; -let maxTabsUndoPlusOne; -let acceptRemoveAllTabsDialogListener; -let cancelRemoveAllTabsDialogListener; - -function test() { - waitForExplicitFinish(); - Services.prefs.setBoolPref("browser.tabs.animate", false); - - registerCleanupFunction(function() { - Services.prefs.clearUserPref("browser.tabs.animate"); - - originalTab.linkedBrowser.loadURI("about:blank"); - originalTab = null; - }); - - // Creating and throwing away this tab guarantees that the - // number of tabs closed in the previous tab-close operation is 1. - let throwaway_tab = gBrowser.addTab("http://mochi.test:8888/"); - gBrowser.removeTab(throwaway_tab); - - let undoCloseTabElement = document.getElementById("context_undoCloseTab"); - updateTabContextMenu(); - is(undoCloseTabElement.label, undoCloseTabElement.getAttribute("singletablabel"), - "The label should be showing that the command will restore a single tab"); - - originalTab = gBrowser.selectedTab; - gBrowser.selectedBrowser.loadURI("http://mochi.test:8888/"); - - maxTabsUndo = Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo"); - maxTabsUndoPlusOne = maxTabsUndo + 1; - let numberOfTabsLoaded = 0; - for (let i = 0; i < maxTabsUndoPlusOne; i++) { - let tab = gBrowser.addTab("http://mochi.test:8888/"); - let browser = gBrowser.getBrowserForTab(tab); - browser.addEventListener("load", function onLoad() { - browser.removeEventListener("load", onLoad, true); - - if (++numberOfTabsLoaded == maxTabsUndoPlusOne) - verifyUndoMultipleClose(); - }, true); - } -} - -function verifyUndoMultipleClose() { - info("all tabs opened and loaded"); - cancelRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", cancelRemoveAllTabsDialog); - Services.wm.addListener(cancelRemoveAllTabsDialogListener); - gBrowser.removeAllTabsBut(originalTab); -} - -function cancelRemoveAllTabsDialog(domWindow) { - ok(true, "dialog appeared in response to multiple tab close action"); - domWindow.document.documentElement.cancelDialog(); - Services.wm.removeListener(cancelRemoveAllTabsDialogListener); - - acceptRemoveAllTabsDialogListener = new WindowListener("chrome://global/content/commonDialog.xul", acceptRemoveAllTabsDialog); - Services.wm.addListener(acceptRemoveAllTabsDialogListener); - waitForCondition(function () gBrowser.tabs.length == 1 + maxTabsUndoPlusOne, function verifyCancel() { - is(gBrowser.tabs.length, 1 + maxTabsUndoPlusOne, /* The '1 +' is for the original tab */ - "All tabs should still be open after the 'Cancel' option on the prompt is chosen"); - gBrowser.removeAllTabsBut(originalTab); - }, "Waited too long to find that no tabs were closed."); -} - -function acceptRemoveAllTabsDialog(domWindow) { - ok(true, "dialog appeared in response to multiple tab close action"); - domWindow.document.documentElement.acceptDialog(); - Services.wm.removeListener(acceptRemoveAllTabsDialogListener); - - waitForCondition(function () gBrowser.tabs.length == 1, function verifyAccept() { - is(gBrowser.tabs.length, 1, - "All other tabs should be closed after the 'OK' option on the prompt is chosen"); - finish(); - }, "Waited too long for the other tabs to be closed."); -} - -function WindowListener(aURL, aCallback) { - this.callback = aCallback; - this.url = aURL; -} -WindowListener.prototype = { - onOpenWindow: function(aXULWindow) { - var domWindow = aXULWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow); - var self = this; - domWindow.addEventListener("load", function() { - domWindow.removeEventListener("load", arguments.callee, false); - - info("domWindow.document.location.href: " + domWindow.document.location.href); - if (domWindow.document.location.href != self.url) - return; - - // Allow other window load listeners to execute before passing to callback - executeSoon(function() { - self.callback(domWindow); - }); - }, false); - }, - onCloseWindow: function(aXULWindow) {}, - onWindowTitleChange: function(aXULWindow, aNewTitle) {} -} diff --git a/browser/base/content/test/general/browser_bug906190.js b/browser/base/content/test/general/browser_bug906190.js index aaf79bfc95c..74829918376 100644 --- a/browser/base/content/test/general/browser_bug906190.js +++ b/browser/base/content/test/general/browser_bug906190.js @@ -212,7 +212,8 @@ function test1C() { is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 1C"); // remove tabs - gTestWin.gBrowser.removeAllTabsBut(mainTab); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false}); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false}); gTestWin.gBrowser.selectTabAtIndex(0); var childTabLink = gHttpTestRoot2 + "file_bug906190_2.html"; @@ -269,7 +270,8 @@ function test2C() { is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 2C"); // remove tabs - gTestWin.gBrowser.removeAllTabsBut(mainTab); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false}); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false}); gTestWin.gBrowser.selectTabAtIndex(0); // file_bug906190_3_4.html redirects to page test1.example.com/* using meta-refresh @@ -336,7 +338,8 @@ function test3E() { is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 3E"); // remove tabs - gTestWin.gBrowser.removeAllTabsBut(mainTab); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false}); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false}); gTestWin.gBrowser.selectTabAtIndex(0); var childTabLink = gHttpTestRoot1 + "file_bug906190_3_4.html"; @@ -403,7 +406,8 @@ function test4E() { is(actual, "Mixed Content Blocker enabled", "OK: Blocked mixed script in Test 4E"); // remove tabs - gTestWin.gBrowser.removeAllTabsBut(mainTab); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false}); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false}); gTestWin.gBrowser.selectTabAtIndex(0); // the sjs files returns a 302 redirect- note, same origins @@ -462,7 +466,8 @@ function test5C() { todo_is(actual, "Mixed Content Blocker disabled", "OK: Executed mixed script in Test 5C!"); // remove tabs - gTestWin.gBrowser.removeAllTabsBut(mainTab); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[2], {animate: false}); + gTestWin.gBrowser.removeTab(gTestWin.gBrowser.tabs[1], {animate: false}); gTestWin.gBrowser.selectTabAtIndex(0); // the sjs files returns a 302 redirect - note, different origins diff --git a/browser/components/sessionstore/nsISessionStore.idl b/browser/components/sessionstore/nsISessionStore.idl index d11c6fe4001..f8b9dca0923 100644 --- a/browser/components/sessionstore/nsISessionStore.idl +++ b/browser/components/sessionstore/nsISessionStore.idl @@ -25,7 +25,7 @@ interface nsIDOMNode; * |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|. */ -[scriptable, uuid(63a4d9f4-373f-11e3-a237-fa91a24410d2)] +[scriptable, uuid(0c99811f-6c5f-4a78-9c31-2d266d714175)] interface nsISessionStore : nsISupports { /** @@ -100,20 +100,6 @@ interface nsISessionStore : nsISupports nsIDOMNode duplicateTab(in nsIDOMWindow aWindow, in nsIDOMNode aTab, [optional] in long aDelta); - /** - * Set the number of tabs that was closed during the last close-tabs - * operation. This helps us keep track of batch-close operations so - * we can restore multiple tabs at once. - */ - void setNumberOfTabsClosedLast(in nsIDOMWindow aWindow, in unsigned long aNumber); - - /** - * Get the number of tabs that was closed during the last close-tabs - * operation. This helps us keep track of batch-close operations so - * we can restore multiple tabs at once. - */ - unsigned long getNumberOfTabsClosedLast(in nsIDOMWindow aWindow); - /** * Get the number of restore-able tabs for a browser window */ diff --git a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm index 98a738444d1..fdc2dee7d6f 100644 --- a/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm +++ b/browser/components/sessionstore/src/RecentlyClosedTabsAndWindowsMenuUtils.jsm @@ -66,7 +66,7 @@ this.RecentlyClosedTabsAndWindowsMenuUtils = { let restoreAllTabs = fragment.appendChild(doc.createElementNS(kNSXUL, aTagName)); restoreAllTabs.setAttribute("label", navigatorBundle.GetStringFromName("menuRestoreAllTabs.label")); restoreAllTabs.setAttribute("oncommand", - "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab(0);"); + "for (var i = 0; i < " + closedTabs.length + "; i++) undoCloseTab();"); } return fragment; }, diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm index 804541a7cab..278c1889b09 100644 --- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -193,14 +193,6 @@ this.SessionStore = { return SessionStoreInternal.duplicateTab(aWindow, aTab, aDelta); }, - getNumberOfTabsClosedLast: function ss_getNumberOfTabsClosedLast(aWindow) { - return SessionStoreInternal.getNumberOfTabsClosedLast(aWindow); - }, - - setNumberOfTabsClosedLast: function ss_setNumberOfTabsClosedLast(aWindow, aNumber) { - return SessionStoreInternal.setNumberOfTabsClosedLast(aWindow, aNumber); - }, - getClosedTabCount: function ss_getClosedTabCount(aWindow) { return SessionStoreInternal.getClosedTabCount(aWindow); }, @@ -1616,35 +1608,6 @@ let SessionStoreInternal = { return newTab; }, - setNumberOfTabsClosedLast: function ssi_setNumberOfTabsClosedLast(aWindow, aNumber) { - if (this._disabledForMultiProcess) { - return; - } - - if (!("__SSi" in aWindow)) { - throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG); - } - - return NumberOfTabsClosedLastPerWindow.set(aWindow, aNumber); - }, - - /* Used to undo batch tab-close operations. Defaults to 1. */ - getNumberOfTabsClosedLast: function ssi_getNumberOfTabsClosedLast(aWindow) { - if (this._disabledForMultiProcess) { - return 0; - } - - if (!("__SSi" in aWindow)) { - throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG); - } - // Blank tabs cannot be undo-closed, so the number returned by - // the NumberOfTabsClosedLastPerWindow can be greater than the - // return value of getClosedTabCount. We won't restore blank - // tabs, so we return the minimum of these two values. - return Math.min(NumberOfTabsClosedLastPerWindow.get(aWindow) || 1, - this.getClosedTabCount(aWindow)); - }, - getClosedTabCount: function ssi_getClosedTabCount(aWindow) { if ("__SSi" in aWindow) { return this._windows[aWindow.__SSi]._closedTabs.length; @@ -4029,11 +3992,6 @@ let DirtyWindows = { } }; -// A map storing the number of tabs last closed per windoow. This only -// stores the most recent tab-close operation, and is used to undo -// batch tab-closing operations. -let NumberOfTabsClosedLastPerWindow = new WeakMap(); - // This is used to help meter the number of restoring tabs. This is the control // point for telling the next tab to restore. It gets attached to each gBrowser // via gBrowser.addTabsProgressListener diff --git a/browser/components/sessionstore/test/browser_345898.js b/browser/components/sessionstore/test/browser_345898.js index e5f3a1fe80b..c4dc7fbb432 100644 --- a/browser/components/sessionstore/test/browser_345898.js +++ b/browser/components/sessionstore/test/browser_345898.js @@ -40,8 +40,4 @@ function test() { "Invalid window for getWindowValue throws"); ok(test(function() ss.setWindowValue({}, "", "")), "Invalid window for setWindowValue throws"); - ok(test(function() ss.getNumberOfTabsClosedLast({})), - "Invalid window for getNumberOfTabsClosedLast throws"); - ok(test(function() ss.setNumberOfTabsClosedLast({}, 1)), - "Invalid window for setNumberOfTabsClosedLast throws"); } diff --git a/browser/components/tabview/test/browser_tabview_bug608037.js b/browser/components/tabview/test/browser_tabview_bug608037.js index a85e3aed99c..8e02d611fed 100644 --- a/browser/components/tabview/test/browser_tabview_bug608037.js +++ b/browser/components/tabview/test/browser_tabview_bug608037.js @@ -38,5 +38,5 @@ function onTabViewWindowLoaded() { gBrowser.removeTab(tabTwo); finish(); }); - }, 0); + }); } diff --git a/browser/components/tabview/test/browser_tabview_bug624847.js b/browser/components/tabview/test/browser_tabview_bug624847.js index e91430fd281..bc5cac4a0d4 100644 --- a/browser/components/tabview/test/browser_tabview_bug624847.js +++ b/browser/components/tabview/test/browser_tabview_bug624847.js @@ -64,7 +64,7 @@ function test() { createBlankTab(); afterAllTabsLoaded(testUndoCloseWithSelectedBlankPinnedTab); - }, 0); + }); }); } @@ -94,7 +94,7 @@ function test() { gBrowser.removeTab(gBrowser.tabs[0]); afterAllTabsLoaded(finishTest); - }, 0); + }); }); } diff --git a/browser/components/tabview/test/browser_tabview_bug628270.js b/browser/components/tabview/test/browser_tabview_bug628270.js index c64c2e58416..4887c88a593 100644 --- a/browser/components/tabview/test/browser_tabview_bug628270.js +++ b/browser/components/tabview/test/browser_tabview_bug628270.js @@ -81,7 +81,7 @@ function test() { gBrowser.removeTab(gBrowser.tabs[1]); gBrowser.removeTab(gBrowser.tabs[1]); hideTabView(finishTest); - }, 0); + }); } waitForExplicitFinish(); diff --git a/browser/components/tabview/test/browser_tabview_bug706736.js b/browser/components/tabview/test/browser_tabview_bug706736.js index f47a7d3c5e8..60a1509d2d9 100644 --- a/browser/components/tabview/test/browser_tabview_bug706736.js +++ b/browser/components/tabview/test/browser_tabview_bug706736.js @@ -20,7 +20,7 @@ function test() { whenTabViewIsHidden(function() { win.gBrowser.removeTab(win.gBrowser.selectedTab); executeSoon(function() { - win.undoCloseTab(0); + win.undoCloseTab(); groupItemTwo.addSubscriber("childAdded", function onChildAdded(data) { groupItemTwo.removeSubscriber("childAdded", onChildAdded); diff --git a/browser/components/tabview/test/head.js b/browser/components/tabview/test/head.js index 61eb5948c16..d5d9679c434 100644 --- a/browser/components/tabview/test/head.js +++ b/browser/components/tabview/test/head.js @@ -362,7 +362,7 @@ function newWindowWithState(state, callback) { function restoreTab(callback, index, win) { win = win || window; - let tab = win.undoCloseTab(index); + let tab = win.undoCloseTab(index || 0); let tabItem = tab._tabViewTabItem; let finalize = function () { diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd index 27b0d1375a8..0b5ff9e58b5 100644 --- a/browser/locales/en-US/chrome/browser/browser.dtd +++ b/browser/locales/en-US/chrome/browser/browser.dtd @@ -49,11 +49,6 @@ can reach it easily. --> - - From c90c7d7476bddd1c8d8f229ed785b853f5432a14 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 13 Dec 2013 17:41:54 -0800 Subject: [PATCH 07/17] Back out 86a9b2253199 (bug 943681) because of mochitest timeouts on OS X on a CLOSED TREE --- browser/devtools/webconsole/hudservice.js | 4 +-- browser/devtools/webconsole/panel.js | 2 +- ...r_webconsole_bug_613642_maintain_scroll.js | 36 ++++++++++++------- browser/devtools/webconsole/test/head.js | 2 +- browser/devtools/webconsole/webconsole.js | 2 +- 5 files changed, 29 insertions(+), 17 deletions(-) diff --git a/browser/devtools/webconsole/hudservice.js b/browser/devtools/webconsole/hudservice.js index fed0a290ab6..caca70d3502 100644 --- a/browser/devtools/webconsole/hudservice.js +++ b/browser/devtools/webconsole/hudservice.js @@ -11,9 +11,9 @@ const {Cc, Ci, Cu} = require("chrome"); let WebConsoleUtils = require("devtools/toolkit/webconsole/utils").Utils; let Heritage = require("sdk/core/heritage"); +loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); loader.lazyGetter(this, "Telemetry", () => require("devtools/shared/telemetry")); loader.lazyGetter(this, "WebConsoleFrame", () => require("devtools/webconsole/webconsole").WebConsoleFrame); -loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm"); loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); @@ -110,7 +110,6 @@ HUD_SERVICE.prototype = function HS_openBrowserConsole(aTarget, aIframeWindow, aChromeWindow) { let hud = new BrowserConsole(aTarget, aIframeWindow, aChromeWindow); - this._browserConsoleID = hud.hudId; this.consoles.set(hud.hudId, hud); return hud.init(); }, @@ -260,6 +259,7 @@ HUD_SERVICE.prototype = connect().then(getTarget).then(openWindow).then((aWindow) => { this.openBrowserConsole(target, aWindow, aWindow) .then((aBrowserConsole) => { + this._browserConsoleID = aBrowserConsole.hudId; this._browserConsoleDefer.resolve(aBrowserConsole); this._browserConsoleDefer = null; }) diff --git a/browser/devtools/webconsole/panel.js b/browser/devtools/webconsole/panel.js index 466f84834de..48a71f576ec 100644 --- a/browser/devtools/webconsole/panel.js +++ b/browser/devtools/webconsole/panel.js @@ -6,7 +6,7 @@ const {Cc, Ci, Cu} = require("chrome"); -loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); +loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); loader.lazyGetter(this, "HUDService", () => require("devtools/webconsole/hudservice")); loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); diff --git a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js index 63da224c9a4..13f01855626 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_613642_maintain_scroll.js @@ -95,25 +95,37 @@ function testGen() { testNext(); }; EventUtils.synthesizeKey("VK_END", {}); - yield undefined; + yield; let oldScrollTop = scrollBox.scrollTop; content.console.log("test message 151"); - scrollBox.onscroll = () => { - dump("\n\nSCROLLED\n\n"); - if (scrollBox.scrollTop == oldScrollTop) { - // Wait for scroll to change. - return; - } - scrollBox.onscroll = null; - isnot(scrollBox.scrollTop, oldScrollTop, "scroll location updated (moved to bottom again)"); - hud = testDriver = null; - finishTest(); - }; + waitForMessages({ + webconsole: hud, + messages: [{ + text: "test message 151", + category: CATEGORY_WEBDEV, + severity: SEVERITY_LOG, + }], + }).then(() => { + scrollBox.onscroll = () => { + if (scrollBox.scrollTop == oldScrollTop) { + // Wait for scroll to change. + return; + } + scrollBox.onscroll = null; + isnot(scrollBox.scrollTop, oldScrollTop, "scroll location updated (moved to bottom again)"); + testNext(); + }; + }); yield undefined; + + hud = testDriver = null; + finishTest(); + + yield undefined; } function test() { diff --git a/browser/devtools/webconsole/test/head.js b/browser/devtools/webconsole/test/head.js index 744401b971d..c60e972e605 100644 --- a/browser/devtools/webconsole/test/head.js +++ b/browser/devtools/webconsole/test/head.js @@ -8,7 +8,7 @@ let WebConsoleUtils, gDevTools, TargetFactory, console, promise, require; (() => { gDevTools = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}).gDevTools; console = Cu.import("resource://gre/modules/devtools/Console.jsm", {}).console; - promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise; + promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise; let tools = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools; let utils = tools.require("devtools/toolkit/webconsole/utils"); diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index 46673010ec1..5e477227fc9 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -14,7 +14,7 @@ loader.lazyServiceGetter(this, "clipboardHelper", "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper"); loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm"); -loader.lazyImporter(this, "promise", "resource://gre/modules/Promise.jsm", "Promise"); +loader.lazyGetter(this, "promise", () => require("sdk/core/promise")); loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter")); loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup); From 703c864bddc1f3d0234c7f10b6915ec7f4cc80f5 Mon Sep 17 00:00:00 2001 From: Michael Comella Date: Wed, 11 Dec 2013 14:31:55 -0800 Subject: [PATCH 08/17] Bug 947550 - Use Robotium side swipe methods. r=margaret --- mobile/android/base/tests/UITest.java | 1 - .../tests/components/AboutHomeComponent.java | 30 +++++++------ .../base/tests/helpers/GestureHelper.java | 44 ------------------- 3 files changed, 16 insertions(+), 59 deletions(-) delete mode 100644 mobile/android/base/tests/helpers/GestureHelper.java diff --git a/mobile/android/base/tests/UITest.java b/mobile/android/base/tests/UITest.java index 28116125657..846d56c3e08 100644 --- a/mobile/android/base/tests/UITest.java +++ b/mobile/android/base/tests/UITest.java @@ -126,7 +126,6 @@ abstract class UITest extends ActivityInstrumentationTestCase2 DeviceHelper.init(this); GeckoHelper.init(this); - GestureHelper.init(this); NavigationHelper.init(this); WaitHelper.init(this); } diff --git a/mobile/android/base/tests/components/AboutHomeComponent.java b/mobile/android/base/tests/components/AboutHomeComponent.java index d12ba88a313..59a6be43e79 100644 --- a/mobile/android/base/tests/components/AboutHomeComponent.java +++ b/mobile/android/base/tests/components/AboutHomeComponent.java @@ -41,6 +41,10 @@ public class AboutHomeComponent extends BaseComponent { HISTORY } + // The percentage of the page to swipe between 0 and 1. This value was set through + // testing: 0.55f was tested on try and fails on armv6 devices. + private static final float SWIPE_PERCENTAGE = 0.70f; + public AboutHomeComponent(final UITestContext testContext) { super(testContext); } @@ -70,35 +74,33 @@ public class AboutHomeComponent extends BaseComponent { return this; } - // TODO: Take specific page as parameter rather than swipe in a direction? public AboutHomeComponent swipeToPageOnRight() { mTestContext.dumpLog("Swiping to the page on the right."); - swipe(Solo.LEFT); + swipeToPage(Solo.RIGHT); return this; } public AboutHomeComponent swipeToPageOnLeft() { mTestContext.dumpLog("Swiping to the page on the left."); - swipe(Solo.RIGHT); + swipeToPage(Solo.LEFT); return this; } - private void swipe(final int direction) { + private void swipeToPage(final int pageDirection) { + assertTrue("Swiping in a vaild direction", + pageDirection == Solo.LEFT || pageDirection == Solo.RIGHT); assertVisible(); final int pageIndex = getHomePagerView().getCurrentItem(); - if (direction == Solo.LEFT) { - GestureHelper.swipeLeft(); - } else { - GestureHelper.swipeRight(); - } - final PagerAdapter adapter = getHomePagerView().getAdapter(); - assertNotNull("The HomePager's PagerAdapter is not null", adapter); + mSolo.scrollViewToSide(getHomePagerView(), pageDirection, SWIPE_PERCENTAGE); - // Swiping left goes to next, swiping right goes to previous - final int unboundedPageIndex = pageIndex + (direction == Solo.LEFT ? 1 : -1); - final int expectedPageIndex = Math.min(Math.max(0, unboundedPageIndex), adapter.getCount() - 1); + // The page on the left is a lower index and vice versa. + final int unboundedPageIndex = pageIndex + (pageDirection == Solo.LEFT ? -1 : 1); + final int pageCount = DeviceHelper.isTablet() ? + TabletPage.values().length : PhonePage.values().length; + final int maxPageIndex = pageCount - 1; + final int expectedPageIndex = Math.min(Math.max(0, unboundedPageIndex), maxPageIndex); waitForPageIndex(expectedPageIndex); } diff --git a/mobile/android/base/tests/helpers/GestureHelper.java b/mobile/android/base/tests/helpers/GestureHelper.java deleted file mode 100644 index 1435ad820be..00000000000 --- a/mobile/android/base/tests/helpers/GestureHelper.java +++ /dev/null @@ -1,44 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -package org.mozilla.gecko.tests.helpers; - -import org.mozilla.gecko.Driver; -import org.mozilla.gecko.tests.UITestContext; - -import com.jayway.android.robotium.solo.Solo; - -/** - * Provides simplified gestures wrapping the Robotium gestures API. - */ -public final class GestureHelper { - private static int DEFAULT_DRAG_STEP_COUNT = 10; - - private static Solo sSolo; - private static Driver sDriver; - - private GestureHelper() { /* To disallow instantation. */ } - - public static void init(final UITestContext context) { - sSolo = context.getSolo(); - sDriver = context.getDriver(); - } - - private static void swipeOnScreen(final int direction) { - final int halfWidth = sDriver.getGeckoWidth() / 2; - final int halfHeight = sDriver.getGeckoHeight() / 2; - - sSolo.drag(direction == Solo.LEFT ? halfWidth : 0, - direction == Solo.LEFT ? 0 : halfWidth, - halfHeight, halfHeight, DEFAULT_DRAG_STEP_COUNT); - } - - public static void swipeLeft() { - swipeOnScreen(Solo.LEFT); - } - - public static void swipeRight() { - swipeOnScreen(Solo.RIGHT); - } -} From 04521c3d14353b26eae3817c406799bac2c247d7 Mon Sep 17 00:00:00 2001 From: ffxbld Date: Sat, 14 Dec 2013 03:11:25 -0800 Subject: [PATCH 09/17] No bug, Automated blocklist update from host bld-centos6-hp-016 - a=blocklist-update --- browser/app/blocklist.xml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/browser/app/blocklist.xml b/browser/app/blocklist.xml index fb6d6516cf1..a8804da86f3 100644 --- a/browser/app/blocklist.xml +++ b/browser/app/blocklist.xml @@ -1,5 +1,5 @@ - + @@ -106,6 +106,10 @@ + + + + @@ -453,6 +457,10 @@ + + + + @@ -693,6 +701,10 @@ + + + + From 42f84b1ce950f5d85f847fb86c449d5d173a0159 Mon Sep 17 00:00:00 2001 From: ffxbld Date: Sat, 14 Dec 2013 03:18:03 -0800 Subject: [PATCH 10/17] No bug, Automated HSTS preload list update from host bld-linux64-ix-037 - a=hsts-update --- security/manager/boot/src/nsSTSPreloadList.errors | 2 ++ security/manager/boot/src/nsSTSPreloadList.inc | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/security/manager/boot/src/nsSTSPreloadList.errors b/security/manager/boot/src/nsSTSPreloadList.errors index 2e80a627a2b..6fb7b561fee 100644 --- a/security/manager/boot/src/nsSTSPreloadList.errors +++ b/security/manager/boot/src/nsSTSPreloadList.errors @@ -47,6 +47,7 @@ groups.google.com: did not receive HSTS header history.google.com: did not receive HSTS header hostedtalkgadget.google.com: did not receive HSTS header id.atlassian.com: did not receive HSTS header +in.xero.com: max-age too low: 3600 iop.intuit.com: max-age too low: 86400 irccloud.com: did not receive HSTS header jitsi.org: did not receive HSTS header @@ -68,6 +69,7 @@ openshift.redhat.com: did not receive HSTS header ottospora.nl: could not connect to host packagist.org: max-age too low: 2592000 paypal.com: max-age too low: 14400 +payroll.xero.com: max-age too low: 3600 platform.lookout.com: could not connect to host play.google.com: did not receive HSTS header plus.google.com: did not receive HSTS header diff --git a/security/manager/boot/src/nsSTSPreloadList.inc b/security/manager/boot/src/nsSTSPreloadList.inc index 93f90fc0361..672a2cbea03 100644 --- a/security/manager/boot/src/nsSTSPreloadList.inc +++ b/security/manager/boot/src/nsSTSPreloadList.inc @@ -8,7 +8,7 @@ /*****************************************************************************/ #include -const PRTime gPreloadListExpirationTime = INT64_C(1397301006735000); +const PRTime gPreloadListExpirationTime = INT64_C(1397905536640000); class nsSTSPreload { @@ -21,6 +21,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "aladdinschools.appspot.com", false }, { "alpha.irccloud.com", false }, { "api.intercom.io", false }, + { "api.xero.com", false }, { "app.recurly.com", false }, { "appseccalifornia.org", true }, { "arivo.com.br", true }, @@ -49,6 +50,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "cupcake.is", true }, { "cybozu.com", true }, { "cyphertite.com", true }, + { "data.qld.gov.au", false }, { "davidlyness.com", true }, { "developer.mydigipass.com", false }, { "dist.torproject.org", false }, @@ -57,6 +59,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "download.jitsi.org", false }, { "ebanking.indovinabank.com.vn", false }, { "ecosystem.atlassian.net", true }, + { "eff.org", true }, { "entropia.de", false }, { "errors.zenpayroll.com", false }, { "espra.com", true }, @@ -66,6 +69,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "forum.quantifiedself.com", true }, { "gernert-server.de", true }, { "getlantern.org", true }, + { "go.xero.com", false }, { "grc.com", false }, { "haste.ch", true }, { "howrandom.org", true }, @@ -83,11 +87,13 @@ static const nsSTSPreload kSTSPreloadList[] = { { "lockify.com", true }, { "login.persona.org", true }, { "login.sapo.pt", true }, + { "login.xero.com", false }, { "logotype.se", true }, { "lolicore.ch", true }, { "lookout.com", false }, { "lumi.do", false }, { "luneta.nearbuysystems.com", false }, + { "mail.de", true }, { "makeyourlaws.org", false }, { "manage.zenpayroll.com", false }, { "manager.linode.com", false }, @@ -98,6 +104,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "members.nearlyfreespeech.net", false }, { "mudcrab.us", true }, { "my.onlime.ch", false }, + { "my.xero.com", false }, { "mylookout.com", false }, { "neg9.org", false }, { "oplop.appspot.com", true }, @@ -110,6 +117,7 @@ static const nsSTSPreload kSTSPreloadList[] = { { "paymill.de", false }, { "piratenlogin.de", true }, { "pixi.me", true }, + { "publications.qld.gov.au", false }, { "riseup.net", true }, { "roundcube.mayfirst.org", false }, { "sandbox.mydigipass.com", false }, From a26e629203011a890904bbc518a060d44ab04c39 Mon Sep 17 00:00:00 2001 From: Peiyong Lin Date: Sat, 14 Dec 2013 08:56:28 -0800 Subject: [PATCH 11/17] Bug 950072 - Metro "View Source" menu item should open a "child" tab. r=mbrubeck --- browser/metro/base/content/ContextCommands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/metro/base/content/ContextCommands.js b/browser/metro/base/content/ContextCommands.js index 04325807a58..bafc1ba929d 100644 --- a/browser/metro/base/content/ContextCommands.js +++ b/browser/metro/base/content/ContextCommands.js @@ -288,7 +288,7 @@ var ContextCommands = { viewPageSource: function cc_viewPageSource() { let uri = this.getPageSource(); if (uri) { - BrowserUI.addAndShowTab(uri); + BrowserUI.addAndShowTab(uri, Browser.selectedTab); } }, From edb6991c06e98108f756cb9267f956f027eb88a4 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Sat, 14 Dec 2013 14:40:55 -0600 Subject: [PATCH 12/17] Bug 941774 - New dom utils methods for injecting native touch input. r=smaug --- dom/base/nsDOMWindowUtils.cpp | 57 ++++++++++++ dom/interfaces/base/nsIDOMWindowUtils.idl | 101 +++++++++++++++++++--- 2 files changed, 145 insertions(+), 13 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 698c9de79ba..2ba41b97318 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -1149,6 +1149,63 @@ nsDOMWindowUtils::SendNativeMouseScrollEvent(int32_t aScreenX, aAdditionalFlags); } +NS_IMETHODIMP +nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId, + uint32_t aTouchState, + int32_t aScreenX, + int32_t aScreenY, + double aPressure, + uint32_t aOrientation) +{ + if (!nsContentUtils::IsCallerChrome()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return NS_ERROR_FAILURE; + } + + if (aPressure < 0 || aPressure > 1 || aOrientation > 359) { + return NS_ERROR_INVALID_ARG; + } + + return widget->SynthesizeNativeTouchPoint(aPointerId, + (nsIWidget::TouchPointerState)aTouchState, + nsIntPoint(aScreenX, aScreenY), + aPressure, aOrientation); +} + +NS_IMETHODIMP +nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX, + int32_t aScreenY, + bool aLongTap) +{ + if (!nsContentUtils::IsCallerChrome()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return NS_ERROR_FAILURE; + } + return widget->SynthesizeNativeTouchTap(nsIntPoint(aScreenX, aScreenY), aLongTap); +} + +NS_IMETHODIMP +nsDOMWindowUtils::ClearNativeTouchSequence() +{ + if (!nsContentUtils::IsCallerChrome()) { + return NS_ERROR_DOM_SECURITY_ERR; + } + + nsCOMPtr widget = GetWidget(); + if (!widget) { + return NS_ERROR_FAILURE; + } + return widget->ClearNativeTouchSequence(); +} + NS_IMETHODIMP nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString) { diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index 4f655903320..57b23a4e728 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -43,7 +43,7 @@ interface nsIDOMEventTarget; interface nsIRunnable; interface nsICompositionStringSynthesizer; -[scriptable, uuid(3d9bf70c-5b83-11e3-9090-3c970e9f4238)] +[scriptable, uuid(38740b7e-095e-4198-a012-cf5f9e102a6a)] interface nsIDOMWindowUtils : nsISupports { /** @@ -442,6 +442,28 @@ interface nsIDOMWindowUtils : nsISupports { in long aModifierFlags, in nsIDOMElement aElement); + /** + * The values for sendNativeMouseScrollEvent's aAdditionalFlags. + */ + + /** + * If MOUSESCROLL_PREFER_WIDGET_AT_POINT is set, widget will dispatch + * the event to a widget which is under the cursor. Otherwise, dispatch to + * a default target on the platform. E.g., on Windows, it's focused window. + */ + const unsigned long MOUSESCROLL_PREFER_WIDGET_AT_POINT = 0x00000001; + + /** + * The platform specific values of aAdditionalFlags. Must be over 0x00010000. + */ + + /** + * If MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL is set and aNativeMessage is + * WM_VSCROLL or WM_HSCROLL, widget will set the window handle to the lParam + * instead of NULL. + */ + const unsigned long MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL = 0x00010000; + /** * See nsIWidget::SynthesizeNativeMouseScrollEvent * @@ -466,26 +488,79 @@ interface nsIDOMWindowUtils : nsISupports { in nsIDOMElement aElement); /** - * The values of aAdditionalFlags. + * Touch states for sendNativeTouchPoint. These values match + * nsIWidget's TouchPointerState. */ - /** - * If MOUSESCROLL_PREFER_WIDGET_AT_POINT is set, widget will dispatch - * the event to a widget which is under the cursor. Otherwise, dispatch to - * a default target on the platform. E.g., on Windows, it's focused window. - */ - const unsigned long MOUSESCROLL_PREFER_WIDGET_AT_POINT = 0x00000001; + // The pointer is in a hover state above the digitizer + const long TOUCH_HOVER = 0x01; + // The pointer is in contact with the digitizer + const long TOUCH_CONTACT = 0x02; + // The pointer has been removed from the digitizer detection area + const long TOUCH_REMOVE = 0x04; + // The pointer has been canceled. Will cancel any pending os level + // gestures that would be triggered as a result of completion of the + // input sequence. This may not cancel moz platform related events + // that might get tirggered by input already delivered. + const long TOUCH_CANCEL = 0x08; /** - * The platform specific values of aAdditionalFlags. Must be over 0x00010000. + * Create a new or update an existing touch point on the digitizer. + * To trigger os level gestures, individual touch points should + * transition through a complete set of touch states which should be + * sent as individual calls. For example: + * tap - msg1:TOUCH_CONTACT, msg2:TOUCH_REMOVE + * drag - msg1-n:TOUCH_CONTACT (moving), msgn+1:TOUCH_REMOVE + * hover drag - msg1-n:TOUCH_HOVER (moving), msgn+1:TOUCH_REMOVE + * + * Widget support: Windows 8.0+, Winrt/Win32. Other widgets will + * throw. + * + * @param aPointerId The touch point id to create or update. + * @param aTouchState one or more of the touch states listed above + * @param aScreenX, aScreenY screen coords of this event + * @param aPressure 0.0 -> 1.0 float val indicating pressure + * @param aOrientation 0 -> 359 degree value indicating the + * orientation of the pointer. Use 90 for normal taps. */ + void sendNativeTouchPoint(in unsigned long aPointerId, + in unsigned long aTouchState, + in long aScreenX, + in long aScreenY, + in double aPressure, + in unsigned long aOrientation); /** - * If MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL is set and aNativeMessage is - * WM_VSCROLL or WM_HSCROLL, widget will set the window handle to the lParam - * instead of NULL. + * Simulates native touch based taps on the input digitizer. Events + * triggered by this call are injected at the os level. Events do not + * bypass widget level input processing and as such can be used to + * test widget event logic and async pan-zoom controller functionality. + * Cannot be accessed from an unprivileged context. + * + * Long taps (based on the aLongTap parameter) will be completed + * asynchrnously after the call returns. Long tap delay is based on + * the ui.click_hold_context_menus.delay pref or 1500 msec if pref + * is not set. + * + * Widget support: Windows 8.0+, Winrt/Win32. Other widgets will + * throw. + * + * @param aScreenX, aScreenY screen coords of this event + * @param aLongTap true if the tap should be long, false for a short + * tap. */ - const unsigned long MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL = 0x00010000; + void sendNativeTouchTap(in long aScreenX, + in long aScreenY, + in boolean aLongTap); + + /** + * Cancel any existing touch points or long tap delays. Calling this is safe + * even if you're sure there aren't any pointers recorded. You should call + * this when tests shut down to reset the digitizer driver. Not doing so can + * leave the digitizer in an undetermined state which can screw up subsequent + * tests and native input. + */ + void clearNativeTouchSequence(); /** * See nsIWidget::ActivateNativeMenuItemAt From c4383a34eddb8b8df8006a1896df019a927186f5 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Sat, 14 Dec 2013 14:40:55 -0600 Subject: [PATCH 13/17] Bug 941774 - Centralize various dpi related queries on Windows. r=bbondy --- gfx/thebes/gfxWindowsPlatform.cpp | 18 +++-- gfx/thebes/gfxWindowsPlatform.h | 7 +- widget/windows/WinUtils.cpp | 42 +++++++++++ widget/windows/WinUtils.h | 9 +++ widget/windows/nsNativeThemeWin.cpp | 5 +- widget/windows/nsWindow.cpp | 2 +- widget/windows/winrt/MetroInput.cpp | 10 ++- widget/windows/winrt/MetroUtils.cpp | 108 ++++++++++++++++----------- widget/windows/winrt/MetroUtils.h | 8 +- widget/windows/winrt/MetroWidget.cpp | 29 +------ widget/windows/winrt/UIABridge.cpp | 8 +- 11 files changed, 149 insertions(+), 97 deletions(-) diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index d265edc8e2d..d777c4d4c25 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -33,6 +33,8 @@ #include "mozilla/layers/CompositorParent.h" // for CompositorParent::IsInCompositorThread #include "DeviceManagerD3D9.h" +#include "WinUtils.h" + #ifdef CAIRO_HAS_DWRITE_FONT #include "gfxDWriteFontList.h" #include "gfxDWriteFonts.h" @@ -46,10 +48,6 @@ #include -using namespace mozilla; -using namespace mozilla::gfx; -using namespace mozilla::layers; - #ifdef CAIRO_HAS_D2D_SURFACE #include "gfxD2DSurface.h" @@ -67,6 +65,9 @@ using namespace mozilla::layers; #include "d3dkmtQueryStatistics.h" using namespace mozilla; +using namespace mozilla::gfx; +using namespace mozilla::layers; +using namespace mozilla::widget; #ifdef CAIRO_HAS_D2D_SURFACE @@ -363,8 +364,6 @@ gfxWindowsPlatform::gfxWindowsPlatform() */ CoInitialize(nullptr); - mScreenDC = GetDC(nullptr); - #ifdef CAIRO_HAS_D2D_SURFACE RegisterStrongMemoryReporter(new GfxD2DSurfaceCacheReporter()); RegisterStrongMemoryReporter(new GfxD2DSurfaceVramReporter()); @@ -384,7 +383,6 @@ gfxWindowsPlatform::~gfxWindowsPlatform() { mDeviceManager = nullptr; - ::ReleaseDC(nullptr, mScreenDC); // not calling FT_Done_FreeType because cairo may still hold references to // these FT_Faces. See bug 458169. #ifdef CAIRO_HAS_D2D_SURFACE @@ -401,6 +399,12 @@ gfxWindowsPlatform::~gfxWindowsPlatform() CoUninitialize(); } +double +gfxWindowsPlatform::GetDPIScale() +{ + return WinUtils::LogToPhysFactor(); +} + void gfxWindowsPlatform::UpdateRenderMode() { diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index c3d05e17d89..07cb51bf55c 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -175,16 +175,12 @@ public: HRESULT CreateDevice(nsRefPtr &adapter1, int featureLevelIndex); #endif - HDC GetScreenDC() { return mScreenDC; } - /** * Return the resolution scaling factor to convert between "logical" or * "screen" pixels as used by Windows (dependent on the DPI scaling option * in the Display control panel) and actual device pixels. */ - double GetDPIScale() { - return GetDeviceCaps(mScreenDC, LOGPIXELSY) / 96.0; - } + double GetDPIScale(); nsresult GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, @@ -274,7 +270,6 @@ protected: int8_t mUseClearTypeForDownloadableFonts; int8_t mUseClearTypeAlways; - HDC mScreenDC; private: void Init(); diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index 4457d57d0bb..cab69860b6e 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -37,6 +37,7 @@ #include "gfxColor.h" #ifdef MOZ_METRO #include "winrt/MetroInput.h" +#include "winrt/MetroUtils.h" #endif // MOZ_METRO #ifdef NS_ENABLE_TSF @@ -185,6 +186,47 @@ WinUtils::Log(const char *fmt, ...) delete[] buffer; } +/* static */ +double +WinUtils::LogToPhysFactor() +{ + // dpi / 96.0 + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { +#ifdef MOZ_METRO + return MetroUtils::LogToPhysFactor(); +#else + return 1.0; +#endif + } else { + HDC hdc = ::GetDC(nullptr); + double result = ::GetDeviceCaps(hdc, LOGPIXELSY) / 96.0; + ::ReleaseDC(nullptr, hdc); + return result; + } +} + +/* static */ +double +WinUtils::PhysToLogFactor() +{ + // 1.0 / (dpi / 96.0) + return 1.0 / LogToPhysFactor(); +} + +/* static */ +double +WinUtils::PhysToLog(int32_t aValue) +{ + return double(aValue) * PhysToLogFactor(); +} + +/* static */ +int32_t +WinUtils::LogToPhys(double aValue) +{ + return int32_t(NS_round(aValue * LogToPhysFactor())); +} + /* static */ bool WinUtils::PeekMessage(LPMSG aMsg, HWND aWnd, UINT aFirstMessage, diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index 6a003d5a250..1a71fb1c42d 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -69,6 +69,15 @@ public: class WinUtils { public: + /** + * Functions to convert between logical pixels as used by most Windows APIs + * and physical (device) pixels. + */ + static double LogToPhysFactor(); + static double PhysToLogFactor(); + static int32_t LogToPhys(double aValue); + static double PhysToLog(int32_t aValue); + /** * Logging helpers that dump output to prlog module 'Widget', console, and * OutputDebugString. Note these output in both debug and release builds. diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp index 7978fa81a1e..3460310268d 100644 --- a/widget/windows/nsNativeThemeWin.cpp +++ b/widget/windows/nsNativeThemeWin.cpp @@ -2430,7 +2430,7 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* a if (NS_FAILED(rv)) return rv; - HDC hdc = gfxWindowsPlatform::GetPlatform()->GetScreenDC(); + HDC hdc = ::GetDC(nullptr); if (!hdc) return NS_ERROR_FAILURE; @@ -2448,12 +2448,13 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* a case NS_THEME_MENUSEPARATOR: { - SIZE gutterSize(GetGutterSize(theme,hdc)); + SIZE gutterSize(GetGutterSize(theme, hdc)); aResult->width += gutterSize.cx; break; } } + ::ReleaseDC(nullptr, hdc); return NS_OK; } diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 4dc262cb319..5eb2d9a48d8 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -979,7 +979,7 @@ float nsWindow::GetDPI() double nsWindow::GetDefaultScaleInternal() { - return gfxWindowsPlatform::GetPlatform()->GetDPIScale(); + return WinUtils::LogToPhysFactor(); } nsWindow* diff --git a/widget/windows/winrt/MetroInput.cpp b/widget/windows/winrt/MetroInput.cpp index f5cc5683e65..73768b215b0 100644 --- a/widget/windows/winrt/MetroInput.cpp +++ b/widget/windows/winrt/MetroInput.cpp @@ -15,6 +15,7 @@ #include "MetroAppShell.h" #include "mozilla/MouseEvents.h" #include "mozilla/TouchEvents.h" +#include "WinUtils.h" // System headers (alphabetical) #include // ABI::Window::UI::Core namespace @@ -26,6 +27,7 @@ using namespace ABI::Windows; // UI, System, Foundation namespaces using namespace Microsoft; // WRL namespace (ComPtr, possibly others) using namespace mozilla; +using namespace mozilla::widget; using namespace mozilla::widget::winrt; using namespace mozilla::dom; @@ -75,8 +77,8 @@ namespace { nsIntPoint touchPoint = MetroUtils::LogToPhys(position); nsIntPoint touchRadius; - touchRadius.x = MetroUtils::LogToPhys(contactRect.Width) / 2; - touchRadius.y = MetroUtils::LogToPhys(contactRect.Height) / 2; + touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2; + touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2; return new Touch(pointerId, touchPoint, // Rotation radius and angle. @@ -122,8 +124,8 @@ namespace { props->get_Pressure(&pressure); nsIntPoint touchPoint = MetroUtils::LogToPhys(position); nsIntPoint touchRadius; - touchRadius.x = MetroUtils::LogToPhys(contactRect.Width) / 2; - touchRadius.y = MetroUtils::LogToPhys(contactRect.Height) / 2; + touchRadius.x = WinUtils::LogToPhys(contactRect.Width) / 2; + touchRadius.y = WinUtils::LogToPhys(contactRect.Height) / 2; // from Touch.Equals return touchPoint != aTouch->mRefPoint || diff --git a/widget/windows/winrt/MetroUtils.cpp b/widget/windows/winrt/MetroUtils.cpp index 9e1db5479d2..c5e0acb1448 100644 --- a/widget/windows/winrt/MetroUtils.cpp +++ b/widget/windows/winrt/MetroUtils.cpp @@ -33,72 +33,92 @@ using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::UI::ViewManagement; using namespace ABI::Windows::Graphics::Display; -// File-scoped statics (unnamed namespace) -namespace { - FLOAT LogToPhysFactor() { - ComPtr dispInfoStatics; - if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), - dispInfoStatics.GetAddressOf()))) { - ComPtr dispInfo; - if (SUCCEEDED(dispInfoStatics->GetForCurrentView(&dispInfo))) { - FLOAT dpi; - if (SUCCEEDED(dispInfo->get_LogicalDpi(&dpi))) { - return dpi / 96.0f; - } - } - } - - ComPtr dispProps; - if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), - dispProps.GetAddressOf()))) { - FLOAT dpi; - if (SUCCEEDED(dispProps->get_LogicalDpi(&dpi))) { - return dpi / 96.0f; - } - } - - return 1.0f; - } - - FLOAT PhysToLogFactor() { - return 1.0f / LogToPhysFactor(); - } -}; - // Conversion between logical and physical coordinates -int32_t -MetroUtils::LogToPhys(FLOAT aValue) + +double +MetroUtils::LogToPhysFactor() { - return int32_t(NS_round(aValue * LogToPhysFactor())); + ComPtr dispInfoStatics; + if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), + dispInfoStatics.GetAddressOf()))) { + ComPtr dispInfo; + if (SUCCEEDED(dispInfoStatics->GetForCurrentView(&dispInfo))) { + FLOAT dpi; + if (SUCCEEDED(dispInfo->get_LogicalDpi(&dpi))) { + return (double)dpi / 96.0f; + } + } + } + + ComPtr dispProps; + if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), + dispProps.GetAddressOf()))) { + FLOAT dpi; + if (SUCCEEDED(dispProps->get_LogicalDpi(&dpi))) { + return (double)dpi / 96.0f; + } + } + + return 1.0; +} + +double +MetroUtils::PhysToLogFactor() +{ + return 1.0 / LogToPhysFactor(); +} + +double +MetroUtils::ScaleFactor() +{ + // Return the resolution scale factor reported by the metro environment. + // XXX TODO: also consider the desktop resolution setting, as IE appears to do? + ComPtr dispInfoStatics; + if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), + dispInfoStatics.GetAddressOf()))) { + ComPtr dispInfo; + if (SUCCEEDED(dispInfoStatics->GetForCurrentView(&dispInfo))) { + ResolutionScale scale; + if (SUCCEEDED(dispInfo->get_ResolutionScale(&scale))) { + return (double)scale / 100.0; + } + } + } + + ComPtr dispProps; + if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), + dispProps.GetAddressOf()))) { + ResolutionScale scale; + if (SUCCEEDED(dispProps->get_ResolutionScale(&scale))) { + return (double)scale / 100.0; + } + } + + return 1.0; } nsIntPoint MetroUtils::LogToPhys(const Point& aPt) { - FLOAT factor = LogToPhysFactor(); + double factor = LogToPhysFactor(); return nsIntPoint(int32_t(NS_round(aPt.X * factor)), int32_t(NS_round(aPt.Y * factor))); } nsIntRect MetroUtils::LogToPhys(const Rect& aRect) { - FLOAT factor = LogToPhysFactor(); + double factor = LogToPhysFactor(); return nsIntRect(int32_t(NS_round(aRect.X * factor)), int32_t(NS_round(aRect.Y * factor)), int32_t(NS_round(aRect.Width * factor)), int32_t(NS_round(aRect.Height * factor))); } -FLOAT -MetroUtils::PhysToLog(int32_t aValue) -{ - return FLOAT(aValue) * PhysToLogFactor(); -} - Point MetroUtils::PhysToLog(const nsIntPoint& aPt) { - FLOAT factor = PhysToLogFactor(); + // Points contain FLOATs + FLOAT factor = (FLOAT)PhysToLogFactor(); Point p = { FLOAT(aPt.x) * factor, FLOAT(aPt.y) * factor }; return p; } diff --git a/widget/windows/winrt/MetroUtils.h b/widget/windows/winrt/MetroUtils.h index 9b6fb9446e9..5b4a110d4c2 100644 --- a/widget/windows/winrt/MetroUtils.h +++ b/widget/windows/winrt/MetroUtils.h @@ -73,13 +73,15 @@ class MetroUtils public: // Functions to convert between logical pixels as used by most Windows APIs // and physical (device) pixels. - // See MSDN documentation about DIPs (device independent pixels) for details. - static int32_t LogToPhys(FLOAT aValue); + static double LogToPhysFactor(); + static double PhysToLogFactor(); static nsIntPoint LogToPhys(const Point& aPt); static nsIntRect LogToPhys(const Rect& aRect); - static FLOAT PhysToLog(int32_t aValue); static Point PhysToLog(const nsIntPoint& aPt); + // Resolution scale factor + static double ScaleFactor(); + static nsresult FireObserver(const char* aMessage, const PRUnichar* aData = nullptr); static HRESULT CreateUri(HSTRING aUriStr, Microsoft::WRL::ComPtr& aUriOut); diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index d48dec1e950..4eb2d95493f 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -1331,33 +1331,10 @@ MetroWidget::GetAccessible() } #endif -double MetroWidget::GetDefaultScaleInternal() +double +MetroWidget::GetDefaultScaleInternal() { - // Return the resolution scale factor reported by the metro environment. - // XXX TODO: also consider the desktop resolution setting, as IE appears to do? - - ComPtr dispInfoStatics; - if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayInformation).Get(), - dispInfoStatics.GetAddressOf()))) { - ComPtr dispInfo; - if (SUCCEEDED(dispInfoStatics->GetForCurrentView(&dispInfo))) { - ResolutionScale scale; - if (SUCCEEDED(dispInfo->get_ResolutionScale(&scale))) { - return (double)scale / 100.0; - } - } - } - - ComPtr dispProps; - if (SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Graphics_Display_DisplayProperties).Get(), - dispProps.GetAddressOf()))) { - ResolutionScale scale; - if (SUCCEEDED(dispProps->get_ResolutionScale(&scale))) { - return (double)scale / 100.0; - } - } - - return 1.0; + return MetroUtils::ScaleFactor(); } LayoutDeviceIntPoint diff --git a/widget/windows/winrt/UIABridge.cpp b/widget/windows/winrt/UIABridge.cpp index 403e82c6a22..2d38304b5cd 100644 --- a/widget/windows/winrt/UIABridge.cpp +++ b/widget/windows/winrt/UIABridge.cpp @@ -315,10 +315,10 @@ UIABridge::get_BoundingRectangle(UiaRect * retVal) mWindow->get_Bounds(&bounds); // we need to return physical pixels - retVal->left = MetroUtils::LogToPhys(bounds.X); - retVal->top = MetroUtils::LogToPhys(bounds.Y); - retVal->width = MetroUtils::LogToPhys(bounds.Width); - retVal->height = MetroUtils::LogToPhys(bounds.Height); + retVal->left = WinUtils::LogToPhys(bounds.X); + retVal->top = WinUtils::LogToPhys(bounds.Y); + retVal->width = WinUtils::LogToPhys(bounds.Width); + retVal->height = WinUtils::LogToPhys(bounds.Height); return S_OK; } From 10b178f798ac4a5ff92203e222398f839f6d6da0 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Sat, 14 Dec 2013 14:40:56 -0600 Subject: [PATCH 14/17] Bug 941774 - Base widget implementation. r=roc --- widget/nsIWidget.h | 90 +++++++++++++++++++++++++- widget/xpwidgets/nsBaseWidget.cpp | 101 ++++++++++++++++++++++++++++++ widget/xpwidgets/nsBaseWidget.h | 13 ++++ 3 files changed, 201 insertions(+), 3 deletions(-) diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index 498d5647e79..1722f213c08 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -12,12 +12,15 @@ #include "nsStringGlue.h" #include "nsCOMPtr.h" +#include "nsAutoPtr.h" #include "nsWidgetInitData.h" #include "nsTArray.h" +#include "nsITimer.h" #include "nsXULAppAPI.h" #include "mozilla/EventForwards.h" #include "mozilla/layers/LayersTypes.h" #include "mozilla/RefPtr.h" +#include "mozilla/TimeStamp.h" #include "Units.h" // forward declarations @@ -97,8 +100,8 @@ typedef void* nsNativeWidget; #endif #define NS_IWIDGET_IID \ -{ 0x746cb189, 0x9793, 0x4e53, \ - { 0x89, 0x47, 0x78, 0x56, 0xb6, 0xcd, 0x9f, 0x71 } } +{ 0x67da44c4, 0xe21b, 0x4742, \ + { 0x9c, 0x2b, 0x26, 0xc7, 0x70, 0x21, 0xde, 0x87 } } /* * Window shadow styles @@ -493,7 +496,9 @@ class nsIWidget : public nsISupports { : mLastChild(nullptr) , mPrevSibling(nullptr) , mOnDestroyCalled(false) - {} + { + ClearNativeTouchSequence(); + } /** @@ -1559,6 +1564,85 @@ class nsIWidget : public nsISupports { uint32_t aModifierFlags, uint32_t aAdditionalFlags) = 0; + /* + * TouchPointerState states for SynthesizeNativeTouchPoint. Match + * touch states in nsIDOMWindowUtils.idl. + */ + enum TouchPointerState { + // The pointer is in a hover state above the digitizer + TOUCH_HOVER = 0x01, + // The pointer is in contact with the digitizer + TOUCH_CONTACT = 0x02, + // The pointer has been removed from the digitizer detection area + TOUCH_REMOVE = 0x04, + // The pointer has been canceled. Will cancel any pending os level + // gestures that would triggered as a result of completion of the + // input sequence. This may not cancel moz platform related events + // that might get tirggered by input already delivered. + TOUCH_CANCEL = 0x08 + }; + + /* + * Create a new or update an existing touch pointer on the digitizer. + * To trigger os level gestures, individual touch points should + * transition through a complete set of touch states which should be + * sent as individual messages. + * + * @param aPointerId The touch point id to create or update. + * @param aPointerState one or more of the touch states listed above + * @param aScreenX, aScreenY screen coords of this event + * @param aPressure 0.0 -> 1.0 float val indicating pressure + * @param aOrientation 0 -> 359 degree value indicating the + * orientation of the pointer. Use 90 for normal taps. + */ + virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, + TouchPointerState aPointerState, + nsIntPoint aPointerScreenPoint, + double aPointerPressure, + uint32_t aPointerOrientation) = 0; + + /* + * Cancels all active simulated touch input points and pending long taps. + * Native widgets should track existing points such that they can clear the + * digitizer state when this call is made. + */ + virtual nsresult ClearNativeTouchSequence(); + + /* + * Helper for simulating a simple tap event with one touch point. When + * aLongTap is true, simulates a native long tap with a duration equal to + * ui.click_hold_context_menus.delay. This pref is compatible with the + * apzc long tap duration. Defaults to 1.5 seconds. + */ + nsresult SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, + bool aLongTap); + +private: + class LongTapInfo + { + public: + LongTapInfo(int32_t aPointerId, nsIntPoint& aPoint, + mozilla::TimeDuration aDuration) : + mPointerId(aPointerId), + mPosition(aPoint), + mDuration(aDuration), + mStamp(mozilla::TimeStamp::Now()) + { + } + + int32_t mPointerId; + nsIntPoint mPosition; + mozilla::TimeDuration mDuration; + mozilla::TimeStamp mStamp; + }; + + static void OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure); + + nsAutoPtr mLongTapTouchPoint; + nsCOMPtr mLongTapTimer; + static int32_t sPointerIdCounter; + +public: /** * Activates a native menu item at the position specified by the index * string. The index string is a string of positive integers separated diff --git a/widget/xpwidgets/nsBaseWidget.cpp b/widget/xpwidgets/nsBaseWidget.cpp index 1916ced3277..384363eb741 100644 --- a/widget/xpwidgets/nsBaseWidget.cpp +++ b/widget/xpwidgets/nsBaseWidget.cpp @@ -70,6 +70,11 @@ nsIContent* nsBaseWidget::mLastRollup = nullptr; // in NativeWindowTheme. bool gDisableNativeTheme = false; +// Async pump timer during injected long touch taps +#define TOUCH_INJECT_PUMP_TIMER_MSEC 50 +#define TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC 1500 +int32_t nsIWidget::sPointerIdCounter = 0; + // nsBaseWidget NS_IMPL_ISUPPORTS1(nsBaseWidget, nsIWidget) @@ -1526,7 +1531,103 @@ nsBaseWidget::GetRootAccessible() return nullptr; } +#endif // ACCESSIBILITY + +nsresult +nsIWidget::SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, bool aLongTap) +{ + if (sPointerIdCounter > TOUCH_INJECT_MAX_POINTS) { + sPointerIdCounter = 0; + } + int pointerId = sPointerIdCounter; + sPointerIdCounter++; + nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_CONTACT, + aPointerScreenPoint, 1.0, 90); + if (NS_FAILED(rv)) { + return rv; + } + + if (!aLongTap) { + nsresult rv = SynthesizeNativeTouchPoint(pointerId, TOUCH_REMOVE, + aPointerScreenPoint, 0, 0); + return rv; + } + + // initiate a long tap + int elapse = Preferences::GetInt("ui.click_hold_context_menus.delay", + TOUCH_INJECT_LONG_TAP_DEFAULT_MSEC); + if (!mLongTapTimer) { + mLongTapTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + SynthesizeNativeTouchPoint(pointerId, TOUCH_CANCEL, + aPointerScreenPoint, 0, 0); + return NS_ERROR_UNEXPECTED; + } + // Windows requires recuring events, so we set this to a smaller window + // than the pref value. + int timeout = elapse; + if (timeout > TOUCH_INJECT_PUMP_TIMER_MSEC) { + timeout = TOUCH_INJECT_PUMP_TIMER_MSEC; + } + mLongTapTimer->InitWithFuncCallback(OnLongTapTimerCallback, this, + timeout, + nsITimer::TYPE_REPEATING_SLACK); + } + + // If we already have a long tap pending, cancel it. We only allow one long + // tap to be active at a time. + if (mLongTapTouchPoint) { + SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL, + mLongTapTouchPoint->mPosition, 0, 0); + } + + mLongTapTouchPoint = new LongTapInfo(pointerId, aPointerScreenPoint, + TimeDuration::FromMilliseconds(elapse)); + return NS_OK; +} + +// static +void +nsIWidget::OnLongTapTimerCallback(nsITimer* aTimer, void* aClosure) +{ + nsIWidget *self = static_cast(aClosure); + + if ((self->mLongTapTouchPoint->mStamp + self->mLongTapTouchPoint->mDuration) > + TimeStamp::Now()) { +#ifdef XP_WIN + // Windows needs us to keep pumping feedback to the digitizer, so update + // the pointer id with the same position. + self->SynthesizeNativeTouchPoint(self->mLongTapTouchPoint->mPointerId, + TOUCH_CONTACT, + self->mLongTapTouchPoint->mPosition, + 1.0, 90); #endif + return; + } + + // finished, remove the touch point + self->mLongTapTimer->Cancel(); + self->mLongTapTimer = nullptr; + self->SynthesizeNativeTouchPoint(self->mLongTapTouchPoint->mPointerId, + TOUCH_REMOVE, + self->mLongTapTouchPoint->mPosition, + 0, 0); + self->mLongTapTouchPoint = nullptr; +} + +nsresult +nsIWidget::ClearNativeTouchSequence() +{ + if (!mLongTapTimer) { + return NS_OK; + } + mLongTapTimer->Cancel(); + mLongTapTimer = nullptr; + SynthesizeNativeTouchPoint(mLongTapTouchPoint->mPointerId, TOUCH_CANCEL, + mLongTapTouchPoint->mPosition, 0, 0); + mLongTapTouchPoint = nullptr; + return NS_OK; +} #ifdef DEBUG ////////////////////////////////////////////////////////////// diff --git a/widget/xpwidgets/nsBaseWidget.h b/widget/xpwidgets/nsBaseWidget.h index 283ecf67b79..787aa679d37 100644 --- a/widget/xpwidgets/nsBaseWidget.h +++ b/widget/xpwidgets/nsBaseWidget.h @@ -41,6 +41,11 @@ namespace base { class Thread; } +// Windows specific constant indicating the maximum number of touch points the +// inject api will allow. This also sets the maximum numerical value for touch +// ids we can use when injecting touch points on Windows. +#define TOUCH_INJECT_MAX_POINTS 256 + class nsBaseWidget; class WidgetShutdownObserver MOZ_FINAL : public nsIObserver @@ -320,6 +325,14 @@ protected: uint32_t aAdditionalFlags) { return NS_ERROR_UNEXPECTED; } + virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, + TouchPointerState aPointerState, + nsIntPoint aPointerScreenPoint, + double aPointerPressure, + uint32_t aPointerOrientation) + { return NS_ERROR_UNEXPECTED; } + +protected: // Stores the clip rectangles in aRects into mClipRects. Returns true // if the new rectangles are different from the old rectangles. bool StoreWindowClipRegion(const nsTArray& aRects); From de63519aeac7fab75bd3cb3e2df79c01bf9bc237 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Sat, 14 Dec 2013 14:40:56 -0600 Subject: [PATCH 15/17] Bug 941774 - Win32/winrt shared implementation. r=bbondy --- widget/windows/nsWindowBase.cpp | 168 ++++++++++++++++++++++++++ widget/windows/nsWindowBase.h | 39 ++++++ widget/windows/touchinjection_sdk80.h | 107 ++++++++++++++++ 3 files changed, 314 insertions(+) create mode 100644 widget/windows/touchinjection_sdk80.h diff --git a/widget/windows/nsWindowBase.cpp b/widget/windows/nsWindowBase.cpp index 695769e5dab..49fbac5c3fe 100644 --- a/widget/windows/nsWindowBase.cpp +++ b/widget/windows/nsWindowBase.cpp @@ -6,9 +6,16 @@ #include "nsWindowBase.h" #include "mozilla/MiscEvents.h" + +#include "WinUtils.h" #include "npapi.h" using namespace mozilla; +using namespace mozilla::widget; + +static const wchar_t kUser32LibName[] = L"user32.dll"; +bool nsWindowBase::sTouchInjectInitialized = false; +InjectTouchInputPtr nsWindowBase::sInjectTouchFuncPtr; bool nsWindowBase::DispatchPluginEvent(const MSG& aMsg) @@ -27,3 +34,164 @@ nsWindowBase::DispatchPluginEvent(const MSG& aMsg) pluginEvent.retargetToFocusedDocument = true; return DispatchWindowEvent(&pluginEvent); } + +// static +bool +nsWindowBase::InitTouchInjection() +{ + if (!sTouchInjectInitialized) { + // Initialize touch injection on the first call + HMODULE hMod = LoadLibraryW(kUser32LibName); + if (!hMod) { + return false; + } + + InitializeTouchInjectionPtr func = + (InitializeTouchInjectionPtr)GetProcAddress(hMod, "InitializeTouchInjection"); + if (!func) { + WinUtils::Log("InitializeTouchInjection not available."); + return false; + } + + if (!func(TOUCH_INJECT_MAX_POINTS, TOUCH_FEEDBACK_DEFAULT)) { + WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d", GetLastError()); + return false; + } + + sInjectTouchFuncPtr = + (InjectTouchInputPtr)GetProcAddress(hMod, "InjectTouchInput"); + if (!sInjectTouchFuncPtr) { + WinUtils::Log("InjectTouchInput not available."); + return false; + } + sTouchInjectInitialized = true; + } + return true; +} + +bool +nsWindowBase::InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint, + POINTER_FLAGS aFlags, uint32_t aPressure, + uint32_t aOrientation) +{ + if (aId > TOUCH_INJECT_MAX_POINTS) { + WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS."); + return false; + } + + POINTER_TOUCH_INFO info; + memset(&info, 0, sizeof(POINTER_TOUCH_INFO)); + + info.touchFlags = TOUCH_FLAG_NONE; + info.touchMask = TOUCH_MASK_CONTACTAREA|TOUCH_MASK_ORIENTATION|TOUCH_MASK_PRESSURE; + info.pressure = aPressure; + info.orientation = aOrientation; + + info.pointerInfo.pointerFlags = aFlags; + info.pointerInfo.pointerType = PT_TOUCH; + info.pointerInfo.pointerId = aId; + info.pointerInfo.ptPixelLocation.x = WinUtils::LogToPhys(aPointerScreenPoint.x); + info.pointerInfo.ptPixelLocation.y = WinUtils::LogToPhys(aPointerScreenPoint.y); + + info.rcContact.top = info.pointerInfo.ptPixelLocation.y - 2; + info.rcContact.bottom = info.pointerInfo.ptPixelLocation.y + 2; + info.rcContact.left = info.pointerInfo.ptPixelLocation.x - 2; + info.rcContact.right = info.pointerInfo.ptPixelLocation.x + 2; + + if (!sInjectTouchFuncPtr(1, &info)) { + WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError()); + return false; + } + return true; +} + +nsresult +nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId, + nsIWidget::TouchPointerState aPointerState, + nsIntPoint aPointerScreenPoint, + double aPointerPressure, + uint32_t aPointerOrientation) +{ + if (!InitTouchInjection()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + bool hover = aPointerState & TOUCH_HOVER; + bool contact = aPointerState & TOUCH_CONTACT; + bool remove = aPointerState & TOUCH_REMOVE; + bool cancel = aPointerState & TOUCH_CANCEL; + + // win api expects a value from 0 to 1024. aPointerPressure is a value + // from 0.0 to 1.0. + uint32_t pressure = (uint32_t)ceil(aPointerPressure * 1024); + + // If we already know about this pointer id get it's record + PointerInfo* info = mActivePointers.Get(aPointerId); + + // We know about this pointer, send an update + if (info) { + POINTER_FLAGS flags = POINTER_FLAG_UPDATE; + if (hover) { + flags |= POINTER_FLAG_INRANGE; + } else if (contact) { + flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_INRANGE; + } else if (remove) { + flags = POINTER_FLAG_UP; + // Remove the pointer from our tracking list. This is nsAutPtr wrapped, + // so shouldn't leak. + mActivePointers.Remove(aPointerId); + } + + if (cancel) { + flags |= POINTER_FLAG_CANCELED; + } + + return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags, + pressure, aPointerOrientation) ? + NS_ERROR_UNEXPECTED : NS_OK; + } + + // Missing init state, error out + if (remove || cancel) { + return NS_ERROR_INVALID_ARG; + } + + // Create a new pointer + info = new PointerInfo(aPointerId, aPointerScreenPoint); + + POINTER_FLAGS flags = POINTER_FLAG_INRANGE; + if (contact) { + flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_DOWN; + } + + mActivePointers.Put(aPointerId, info); + return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags, + pressure, aPointerOrientation) ? + NS_ERROR_UNEXPECTED : NS_OK; +} + +// static +PLDHashOperator +nsWindowBase::CancelTouchPoints(const unsigned int& aPointerId, nsAutoPtr& aInfo, void* aUserArg) +{ + nsWindowBase* self = static_cast(aUserArg); + self->InjectTouchPoint(aInfo.get()->mPointerId, aInfo.get()->mPosition, POINTER_FLAG_CANCELED); + return (PLDHashOperator)(PL_DHASH_NEXT|PL_DHASH_REMOVE); +} + +nsresult +nsWindowBase::ClearNativeTouchSequence() +{ + if (!sTouchInjectInitialized) { + return NS_OK; + } + + // cancel all input points + mActivePointers.Enumerate(CancelTouchPoints, (void*)this); + + nsBaseWidget::ClearNativeTouchSequence(); + + return NS_OK; +} + + diff --git a/widget/windows/nsWindowBase.h b/widget/windows/nsWindowBase.h index d073d7fcdf7..a5740668c7c 100644 --- a/widget/windows/nsWindowBase.h +++ b/widget/windows/nsWindowBase.h @@ -8,7 +8,10 @@ #include "mozilla/EventForwards.h" #include "nsBaseWidget.h" +#include "nsClassHashtable.h" + #include +#include "touchinjection_sdk80.h" /* * nsWindowBase - Base class of common methods other classes need to access @@ -75,6 +78,42 @@ public: return (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN); } +public: + /* + * Touch input injection apis + */ + virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId, + TouchPointerState aPointerState, + nsIntPoint aPointerScreenPoint, + double aPointerPressure, + uint32_t aPointerOrientation); + virtual nsresult ClearNativeTouchSequence(); + +protected: + static bool InitTouchInjection(); + bool InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint, + POINTER_FLAGS aFlags, uint32_t aPressure = 1024, + uint32_t aOrientation = 90); + + class PointerInfo + { + public: + PointerInfo(int32_t aPointerId, nsIntPoint& aPoint) : + mPointerId(aPointerId), + mPosition(aPoint) + { + } + + int32_t mPointerId; + nsIntPoint mPosition; + }; + + static PLDHashOperator CancelTouchPoints(const unsigned int& aPointerId, nsAutoPtr& aInfo, void* aUserArg); + + nsClassHashtable mActivePointers; + static bool sTouchInjectInitialized; + static InjectTouchInputPtr sInjectTouchFuncPtr; + protected: InputContext mInputContext; }; diff --git a/widget/windows/touchinjection_sdk80.h b/widget/windows/touchinjection_sdk80.h new file mode 100644 index 00000000000..e93f62089b3 --- /dev/null +++ b/widget/windows/touchinjection_sdk80.h @@ -0,0 +1,107 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef touchinjection_sdk80_h +#define touchinjection_sdk80_h + +// Note, this isn't inclusive of all touch injection header info. +// You may need to add more to expand on current apis. + +#ifndef TOUCH_FEEDBACK_DEFAULT + +#define TOUCH_FEEDBACK_DEFAULT 0x1 +#define TOUCH_FEEDBACK_INDIRECT 0x2 +#define TOUCH_FEEDBACK_NONE 0x3 + +enum { + PT_POINTER = 0x00000001, // Generic pointer + PT_TOUCH = 0x00000002, // Touch + PT_PEN = 0x00000003, // Pen + PT_MOUSE = 0x00000004, // Mouse +}; + +typedef DWORD POINTER_INPUT_TYPE; +typedef UINT32 POINTER_FLAGS; + +typedef enum { + POINTER_CHANGE_NONE, + POINTER_CHANGE_FIRSTBUTTON_DOWN, + POINTER_CHANGE_FIRSTBUTTON_UP, + POINTER_CHANGE_SECONDBUTTON_DOWN, + POINTER_CHANGE_SECONDBUTTON_UP, + POINTER_CHANGE_THIRDBUTTON_DOWN, + POINTER_CHANGE_THIRDBUTTON_UP, + POINTER_CHANGE_FOURTHBUTTON_DOWN, + POINTER_CHANGE_FOURTHBUTTON_UP, + POINTER_CHANGE_FIFTHBUTTON_DOWN, + POINTER_CHANGE_FIFTHBUTTON_UP, +} POINTER_BUTTON_CHANGE_TYPE; + +typedef struct { + POINTER_INPUT_TYPE pointerType; + UINT32 pointerId; + UINT32 frameId; + POINTER_FLAGS pointerFlags; + HANDLE sourceDevice; + HWND hwndTarget; + POINT ptPixelLocation; + POINT ptHimetricLocation; + POINT ptPixelLocationRaw; + POINT ptHimetricLocationRaw; + DWORD dwTime; + UINT32 historyCount; + INT32 InputData; + DWORD dwKeyStates; + UINT64 PerformanceCount; + POINTER_BUTTON_CHANGE_TYPE ButtonChangeType; +} POINTER_INFO; + +typedef UINT32 TOUCH_FLAGS; +typedef UINT32 TOUCH_MASK; + +typedef struct { + POINTER_INFO pointerInfo; + TOUCH_FLAGS touchFlags; + TOUCH_MASK touchMask; + RECT rcContact; + RECT rcContactRaw; + UINT32 orientation; + UINT32 pressure; +} POINTER_TOUCH_INFO; + +#define TOUCH_FLAG_NONE 0x00000000 // Default + +#define TOUCH_MASK_NONE 0x00000000 // Default - none of the optional fields are valid +#define TOUCH_MASK_CONTACTAREA 0x00000001 // The rcContact field is valid +#define TOUCH_MASK_ORIENTATION 0x00000002 // The orientation field is valid +#define TOUCH_MASK_PRESSURE 0x00000004 // The pressure field is valid + +#define POINTER_FLAG_NONE 0x00000000 // Default +#define POINTER_FLAG_NEW 0x00000001 // New pointer +#define POINTER_FLAG_INRANGE 0x00000002 // Pointer has not departed +#define POINTER_FLAG_INCONTACT 0x00000004 // Pointer is in contact +#define POINTER_FLAG_FIRSTBUTTON 0x00000010 // Primary action +#define POINTER_FLAG_SECONDBUTTON 0x00000020 // Secondary action +#define POINTER_FLAG_THIRDBUTTON 0x00000040 // Third button +#define POINTER_FLAG_FOURTHBUTTON 0x00000080 // Fourth button +#define POINTER_FLAG_FIFTHBUTTON 0x00000100 // Fifth button +#define POINTER_FLAG_PRIMARY 0x00002000 // Pointer is primary +#define POINTER_FLAG_CONFIDENCE 0x00004000 // Pointer is considered unlikely to be accidental +#define POINTER_FLAG_CANCELED 0x00008000 // Pointer is departing in an abnormal manner +#define POINTER_FLAG_DOWN 0x00010000 // Pointer transitioned to down state (made contact) +#define POINTER_FLAG_UPDATE 0x00020000 // Pointer update +#define POINTER_FLAG_UP 0x00040000 // Pointer transitioned from down state (broke contact) +#define POINTER_FLAG_WHEEL 0x00080000 // Vertical wheel +#define POINTER_FLAG_HWHEEL 0x00100000 // Horizontal wheel +#define POINTER_FLAG_CAPTURECHANGED 0x00200000 // Lost capture + +#endif // TOUCH_FEEDBACK_DEFAULT + +#define TOUCH_FLAGS_CONTACTUPDATE (POINTER_FLAG_UPDATE|POINTER_FLAG_INRANGE|POINTER_FLAG_INCONTACT) +#define TOUCH_FLAGS_CONTACTDOWN (POINTER_FLAG_DOWN|POINTER_FLAG_INRANGE|POINTER_FLAG_INCONTACT) + +typedef BOOL (WINAPI* InitializeTouchInjectionPtr)(UINT32 maxCount, DWORD dwMode); +typedef BOOL (WINAPI* InjectTouchInputPtr)(UINT32 count, CONST POINTER_TOUCH_INFO *info); + +#endif // touchinjection_sdk80_h \ No newline at end of file From 6bcb765b2bb523d6cf9cdc4d5bf45a8060a33475 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Sat, 14 Dec 2013 14:40:56 -0600 Subject: [PATCH 16/17] Bug 945765 - Add some basic front end apzc tests to catch regressions. r=mbrubeck --- .../tests/mochitest/browser_apzc_basic.js | 117 ++++++++++++++++++ browser/metro/base/tests/mochitest/head.js | 89 +++++++++++-- browser/metro/base/tests/mochitest/metro.ini | 2 + .../base/tests/mochitest/res/textdivs01.html | 36 ++++++ 4 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 browser/metro/base/tests/mochitest/browser_apzc_basic.js create mode 100644 browser/metro/base/tests/mochitest/res/textdivs01.html diff --git a/browser/metro/base/tests/mochitest/browser_apzc_basic.js b/browser/metro/base/tests/mochitest/browser_apzc_basic.js new file mode 100644 index 00000000000..8a49f105201 --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_apzc_basic.js @@ -0,0 +1,117 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function test() { + if (!isLandscapeMode()) { + todo(false, "browser_snapped_tests need landscape mode to run."); + return; + } + + runTests(); +} + +let kTransformTimeout = 5000; + +let gEdit = null; +let tabAdded = false; + +function setUp() { + if (!tabAdded) { + yield addTab(chromeRoot + "res/textdivs01.html"); + tabAdded = true; + } + yield hideContextUI(); +} + +/* +gTests.push({ + desc: "soft keyboard reliability", + setUp: setUp, + run: function() { + yield waitForMs(3000); + + let edit = Browser.selectedBrowser.contentDocument.getElementById("textinput"); + // show the soft keyboard + let keyboardPromise = waitForObserver("metro_softkeyboard_shown", 20000); + sendNativeTap(edit); + yield waitForMs(5000); + sendNativeTap(edit); + yield keyboardPromise; + yield waitForMs(5000); + + // hide the soft keyboard / navbar + keyboardPromise = waitForObserver("metro_softkeyboard_hidden", 20000); + sendNativeTap(Browser.selectedBrowser.contentDocument.getElementById("first")); + yield keyboardPromise; + yield waitForMs(5000); + }, + tearDown: function () { + clearNativeTouchSequence(); + } +}); +*/ + +gTests.push({ + desc: "native long tap works", + setUp: setUp, + run: function() { + let edit = Browser.selectedBrowser.contentDocument.getElementById("textinput"); + let promise = waitForEvent(document, "popupshown"); + sendNativeLongTap(edit); + yield promise; + ContextMenuUI.hide(); + }, + tearDown: function () { + clearNativeTouchSequence(); + } +}); + +gTests.push({ + desc: "double tap transforms", + setUp: setUp, + run: function() { + let beginPromise = waitForObserver("apzc-transform-begin", kTransformTimeout); + let endPromise = waitForObserver("apzc-transform-end", kTransformTimeout); + + sendNativeDoubleTap(Browser.selectedBrowser.contentDocument.getElementById("second")); + + yield beginPromise; + yield endPromise; + + beginPromise = waitForObserver("apzc-transform-begin", kTransformTimeout); + endPromise = waitForObserver("apzc-transform-end", kTransformTimeout); + + sendNativeDoubleTap(Browser.selectedBrowser.contentDocument.getElementById("second")); + + yield beginPromise; + yield endPromise; + }, + tearDown: function () { + clearNativeTouchSequence(); + } +}); + +gTests.push({ + desc: "scroll transforms", + setUp: setUp, + run: function() { + let beginPromise = waitForObserver("apzc-transform-begin", kTransformTimeout); + let endPromise = waitForObserver("apzc-transform-end", kTransformTimeout); + + var touchdrag = new TouchDragAndHold(); + touchdrag.useNativeEvents = true; + touchdrag.nativePointerId = 1; + yield touchdrag.start(Browser.selectedTab.browser.contentWindow, + 10, 100, 10, 10); + touchdrag.end(); + + yield beginPromise; + yield endPromise; + }, + tearDown: function () { + clearNativeTouchSequence(); + } +}); diff --git a/browser/metro/base/tests/mochitest/head.js b/browser/metro/base/tests/mochitest/head.js index 9aa24539f44..3812ee6d8f2 100644 --- a/browser/metro/base/tests/mochitest/head.js +++ b/browser/metro/base/tests/mochitest/head.js @@ -557,8 +557,11 @@ function waitForObserver(aObsEvent, aTimeoutMs) { } /*============================================================================= - Native input synthesis helpers -=============================================================================*/ + * Native input helpers - these helpers send input directly to the os + * generating os level input events that get processed by widget and + * apzc logic. + *===========================================================================*/ + // Keyboard layouts for use with synthesizeNativeKey const usEnglish = 0x409; const arSpanish = 0x2C0A; @@ -640,6 +643,36 @@ function synthesizeNativeMouseMUp(aElement, aOffsetX, aOffsetY) { 0x0040); // MOUSEEVENTF_MIDDLEUP } +// WARNING: these calls can trigger the soft keyboard on tablets, but not +// on test slaves (bug 947428). +// WARNING: When testing the apzc, be careful of bug 933990. Events sent +// shortly after loading a page may get ignored. + +function sendNativeLongTap(aElement, aX, aY) { + let coords = logicalCoordsForElement(aElement, aX, aY); + Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, true); +} + +function sendNativeTap(aElement, aX, aY) { + let coords = logicalCoordsForElement(aElement, aX, aY); + Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, false); +} + +function sendNativeDoubleTap(aElement, aX, aY) { + let coords = logicalCoordsForElement(aElement, aX, aY); + Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, false); + Browser.windowUtils.sendNativeTouchTap(coords.x, coords.y, false); +} + +function clearNativeTouchSequence() { + Browser.windowUtils.clearNativeTouchSequence(); +} + +/*============================================================================= + * Synthesized event helpers - these helpers synthesize input events that get + * dispatched directly to the dom. As such widget and apzc logic is bypassed. + *===========================================================================*/ + /* * logicalCoordsForElement - given coordinates relative to top-left of * given element, returns logical coordinates for window. If a non-numeric @@ -784,6 +817,17 @@ TouchDragAndHold.prototype = { _numSteps: 50, _debug: false, _win: null, + _native: false, + _pointerId: 1, + _dui: Components.interfaces.nsIDOMWindowUtils, + + set useNativeEvents(aValue) { + this._native = aValue; + }, + + set nativePointerId(aValue) { + this._pointerId = aValue; + }, callback: function callback() { if (this._win == null) @@ -795,8 +839,14 @@ TouchDragAndHold.prototype = { } if (++this._step.steps >= this._numSteps) { - EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos, - { type: "touchmove" }, this._win); + if (this._native) { + this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_CONTACT, + this._endPoint.xPos, this._endPoint.yPos, + 1, 90); + } else { + EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos, + { type: "touchmove" }, this._win); + } this._defer.resolve(); return; } @@ -805,8 +855,16 @@ TouchDragAndHold.prototype = { if (this._debug) { info("[" + this._step.steps + "] touchmove " + this._currentPoint.xPos + " x " + this._currentPoint.yPos); } - EventUtils.synthesizeTouchAtPoint(this._currentPoint.xPos, this._currentPoint.yPos, - { type: "touchmove" }, this._win); + + if (this._native) { + this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_CONTACT, + this._currentPoint.xPos, this._currentPoint.yPos, + 1, 90); + } else { + EventUtils.synthesizeTouchAtPoint(this._currentPoint.xPos, this._currentPoint.yPos, + { type: "touchmove" }, this._win); + } + let self = this; setTimeout(function () { self.callback(); }, this._timeoutStep); }, @@ -814,13 +872,20 @@ TouchDragAndHold.prototype = { start: function start(aWindow, aStartX, aStartY, aEndX, aEndY) { this._defer = Promise.defer(); this._win = aWindow; + this._utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); this._endPoint = { xPos: aEndX, yPos: aEndY }; this._currentPoint = { xPos: aStartX, yPos: aStartY }; this._step = { steps: 0, x: (aEndX - aStartX) / this._numSteps, y: (aEndY - aStartY) / this._numSteps }; if (this._debug) { info("[0] touchstart " + aStartX + " x " + aStartY); } - EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow); + if (this._native) { + this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_CONTACT, + aStartX, aStartY, 1, 90); + } else { + EventUtils.synthesizeTouchAtPoint(aStartX, aStartY, { type: "touchstart" }, aWindow); + } let self = this; setTimeout(function () { self.callback(); }, this._timeoutStep); return this._defer.promise; @@ -847,8 +912,14 @@ TouchDragAndHold.prototype = { info("[" + this._step.steps + "] touchend " + this._endPoint.xPos + " x " + this._endPoint.yPos); SelectionHelperUI.debugClearDebugPoints(); } - EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos, - { type: "touchend" }, this._win); + if (this._native) { + this._utils.sendNativeTouchPoint(this._pointerId, this._dui.TOUCH_REMOVE, + this._endPoint.xPos, this._endPoint.yPos, + 1, 90); + } else { + EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos, + { type: "touchend" }, this._win); + } this._win = null; }, }; diff --git a/browser/metro/base/tests/mochitest/metro.ini b/browser/metro/base/tests/mochitest/metro.ini index 307029ee5c6..e3a68774ec9 100644 --- a/browser/metro/base/tests/mochitest/metro.ini +++ b/browser/metro/base/tests/mochitest/metro.ini @@ -24,6 +24,7 @@ support-files = helpers/ViewStateHelper.js res/image01.png res/textblock01.html + res/textdivs01.html res/textinput01.html res/textarea01.html res/testEngine.xml @@ -55,6 +56,7 @@ support-files = [browser_urlbar.js] [browser_urlbar_highlightURLs.js] [browser_urlbar_trimURLs.js] +[browser_apzc_basic.js] # These tests have known failures in debug builds [browser_selection_basic.js] diff --git a/browser/metro/base/tests/mochitest/res/textdivs01.html b/browser/metro/base/tests/mochitest/res/textdivs01.html new file mode 100644 index 00000000000..dd6ecc34831 --- /dev/null +++ b/browser/metro/base/tests/mochitest/res/textdivs01.html @@ -0,0 +1,36 @@ + + + + + + + +
+ +
+ Alice was beginning to get very tired of sitting by her sister on the bank, and of having + nothing to do: once or twice she had peeped into the book her sister was reading + but it had no pictures or conversations in it, `and what is the use of a book,' thought + Alice `without pictures or conversation?' +
+
+ Alice was beginning to get very tired of sitting by her sister on the bank, and of having + nothing to do: once or twice she had peeped into the book her sister was reading, but it + had no pictures or conversations in it, `and what is the use of a book,' thought Alice + `without pictures or conversation?' +
+
+ So she was considering in her own mind (as well as she could, for the hot day made her + feel very sleepy and stupid), whether the pleasure of making a daisy-chain would be worth + the trouble of getting up and picking the daisies, when suddenly a White Rabbit with pink + eyes ran close by her. +
+
+ \ No newline at end of file From bcc3ee9ff906e561b0ccaa74f98863c8674662f8 Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Sat, 14 Dec 2013 14:40:56 -0600 Subject: [PATCH 17/17] Bug 947146 - Remove hover states from elements that get touch tapped, plus some tests. r=mbrubeck --- .../mochitest/browser_menu_hoverstate.js | 147 ++++++++++++++++++ browser/metro/base/tests/mochitest/metro.ini | 1 + widget/windows/winrt/MetroInput.cpp | 44 +++--- widget/windows/winrt/MetroWidget.cpp | 16 +- widget/windows/winrt/MetroWidget.h | 2 + 5 files changed, 186 insertions(+), 24 deletions(-) create mode 100644 browser/metro/base/tests/mochitest/browser_menu_hoverstate.js diff --git a/browser/metro/base/tests/mochitest/browser_menu_hoverstate.js b/browser/metro/base/tests/mochitest/browser_menu_hoverstate.js new file mode 100644 index 00000000000..59e6dcb1286 --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_menu_hoverstate.js @@ -0,0 +1,147 @@ +// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*- +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function test() { + if (!isLandscapeMode()) { + todo(false, "browser_snapped_tests need landscape mode to run."); + return; + } + + runTests(); +} +let tabAdded = false; + +function setUp() { + if (!tabAdded) { + yield addTab(chromeRoot + "res/textdivs01.html"); + tabAdded = true; + } + yield hideContextUI(); +} + +XPCOMUtils.defineLazyServiceGetter(this, "gDOMUtils", + "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils"); + +const kActiveState = 0x00000001; +const kHoverState = 0x00000004; + +gTests.push({ + desc: "hover states of menus", + setUp: setUp, + run: function() { + // Clicking on menu items should not leave the clicked menu item + // in the :active or :hover state. + + let typesArray = [ + "copy", + "paste" + ]; + + let promise = waitForEvent(document, "popupshown"); + ContextMenuUI.showContextMenu({ + target: null, + json: { + types: typesArray, + string: '', + xPos: 1, + yPos: 1, + leftAligned: true, + bottomAligned: true + }}); + yield promise; + + // should be visible + ok(ContextMenuUI._menuPopup.visible, "is visible"); + + let menuItem = document.getElementById("context-copy"); + promise = waitForEvent(document, "popuphidden"); + sendNativeTap(menuItem); + yield promise; + + for (let idx = 0; idx < ContextMenuUI.commands.childNodes.length; idx++) { + let item = ContextMenuUI.commands.childNodes[idx]; + let state = gDOMUtils.getContentState(item); + if ((state & kHoverState) || (state & kActiveState)) { + ok(false, "found invalid state on context menu item (" + state.toString(2) + ")"); + } + } + + // Do it again, but this time check the visible menu too and + // click a different menu item. + promise = waitForEvent(document, "popupshown"); + ContextMenuUI.showContextMenu({ + target: null, + json: { + types: typesArray, + string: '', + xPos: 1, + yPos: 1, + leftAligned: true, + bottomAligned: true + }}); + yield promise; + + // should be visible + ok(ContextMenuUI._menuPopup.visible, "is visible"); + + for (let idx = 0; idx < ContextMenuUI.commands.childNodes.length; idx++) { + let item = ContextMenuUI.commands.childNodes[idx]; + let state = gDOMUtils.getContentState(item); + if ((state & kHoverState) || (state & kActiveState)) { + ok(false, "found invalid state on context menu item (" + state.toString(2) + ")"); + } + } + + menuItem = document.getElementById("context-paste"); + promise = waitForEvent(document, "popuphidden"); + sendNativeTap(menuItem); + yield promise; + + for (let idx = 0; idx < ContextMenuUI.commands.childNodes.length; idx++) { + let item = ContextMenuUI.commands.childNodes[idx]; + let state = gDOMUtils.getContentState(item); + if ((state & kHoverState) || (state & kActiveState)) { + ok(false, "found invalid state on context menu item (" + state.toString(2) + ")"); + } + } + }, + tearDown: function () { + clearNativeTouchSequence(); + } +}); + +gTests.push({ + desc: "hover states of nav bar buttons", + setUp: setUp, + run: function() { + // show nav bar + yield showNavBar(); + + // tap bookmark button + sendNativeTap(Appbar.starButton); + yield waitForMs(100); + + // check hover state + let state = gDOMUtils.getContentState(Appbar.starButton); + if ((state & kHoverState) || (state & kActiveState)) { + ok(false, "found invalid state on star button (" + state.toString(2) + ")"); + } + + // tap bookmark button + sendNativeTap(Appbar.starButton); + yield waitForMs(100); + + // check hover state + let state = gDOMUtils.getContentState(Appbar.starButton); + if ((state & kHoverState) || (state & kActiveState)) { + ok(false, "found invalid state on star button (" + state.toString(2) + ")"); + } + }, + tearDown: function () { + clearNativeTouchSequence(); + } +}); + diff --git a/browser/metro/base/tests/mochitest/metro.ini b/browser/metro/base/tests/mochitest/metro.ini index e3a68774ec9..f3d3825d300 100644 --- a/browser/metro/base/tests/mochitest/metro.ini +++ b/browser/metro/base/tests/mochitest/metro.ini @@ -57,6 +57,7 @@ support-files = [browser_urlbar_highlightURLs.js] [browser_urlbar_trimURLs.js] [browser_apzc_basic.js] +[browser_menu_hoverstate.js] # These tests have known failures in debug builds [browser_selection_basic.js] diff --git a/widget/windows/winrt/MetroInput.cpp b/widget/windows/winrt/MetroInput.cpp index 73768b215b0..d8e02ecabf1 100644 --- a/widget/windows/winrt/MetroInput.cpp +++ b/widget/windows/winrt/MetroInput.cpp @@ -16,6 +16,8 @@ #include "mozilla/MouseEvents.h" #include "mozilla/TouchEvents.h" #include "WinUtils.h" +#include "nsIPresShell.h" +#include "nsEventStateManager.h" // System headers (alphabetical) #include // ABI::Window::UI::Core namespace @@ -966,7 +968,6 @@ MetroInput::HandleTap(const Foundation::Point& aPoint, unsigned int aTapCount) return; } - // send mousemove WidgetMouseEvent* mouseEvent = new WidgetMouseEvent(true, NS_MOUSE_MOVE, mWidget.Get(), WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); @@ -975,7 +976,6 @@ MetroInput::HandleTap(const Foundation::Point& aPoint, unsigned int aTapCount) mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; DispatchAsyncEventIgnoreStatus(mouseEvent); - // Send the mousedown mouseEvent = new WidgetMouseEvent(true, NS_MOUSE_BUTTON_DOWN, mWidget.Get(), WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); @@ -993,22 +993,6 @@ MetroInput::HandleTap(const Foundation::Point& aPoint, unsigned int aTapCount) mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; mouseEvent->button = WidgetMouseEvent::buttonType::eLeftButton; DispatchAsyncEventIgnoreStatus(mouseEvent); - - // Send one more mousemove to avoid getting a hover state. - // In the Metro environment for any application, a tap does not imply a - // mouse cursor move. In desktop environment for any application a tap - // does imply a cursor move. - POINT point; - if (GetCursorPos(&point)) { - ScreenToClient((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW), &point); - mouseEvent = - new WidgetMouseEvent(true, NS_MOUSE_MOVE, mWidget.Get(), - WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); - mouseEvent->refPoint = LayoutDeviceIntPoint(point.x, point.y); - mouseEvent->clickCount = aTapCount; - mouseEvent->inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH; - DispatchAsyncEventIgnoreStatus(mouseEvent); - } } void @@ -1048,11 +1032,27 @@ MetroInput::DispatchAsyncEventIgnoreStatus(WidgetInputEvent* aEvent) void MetroInput::DeliverNextQueuedEventIgnoreStatus() { - WidgetGUIEvent* event = + nsAutoPtr event = static_cast(mInputEventQueue.PopFront()); - MOZ_ASSERT(event); - DispatchEventIgnoreStatus(event); - delete event; + MOZ_ASSERT(event.get()); + DispatchEventIgnoreStatus(event.get()); + + // Clear :hover/:active states for mouse events generated by HandleTap + WidgetMouseEvent* mouseEvent = event.get()->AsMouseEvent(); + if (!mouseEvent) { + return; + } + if (mouseEvent->message != NS_MOUSE_BUTTON_UP || + mouseEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) { + return; + } + nsCOMPtr presShell = mWidget->GetPresShell(); + if (presShell) { + nsEventStateManager* esm = presShell->GetPresContext()->EventStateManager(); + if (esm) { + esm->SetContentState(nullptr, NS_EVENT_STATE_HOVER); + } + } } void diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index 4eb2d95493f..deeb3f7a02d 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -1346,7 +1346,8 @@ MetroWidget::CSSIntPointToLayoutDeviceIntPoint(const CSSIntPoint &aCSSPoint) return devPx; } -float MetroWidget::GetDPI() +float +MetroWidget::GetDPI() { if (!mView) { return 96.0; @@ -1354,7 +1355,8 @@ float MetroWidget::GetDPI() return mView->GetDPI(); } -void MetroWidget::ChangedDPI() +void +MetroWidget::ChangedDPI() { if (mWidgetListener) { nsIPresShell* presShell = mWidgetListener->GetPresShell(); @@ -1364,6 +1366,16 @@ void MetroWidget::ChangedDPI() } } +already_AddRefed +MetroWidget::GetPresShell() +{ + if (mWidgetListener) { + nsCOMPtr ps = mWidgetListener->GetPresShell(); + return ps.forget(); + } + return nullptr; +} + NS_IMETHODIMP MetroWidget::ConstrainPosition(bool aAllowSlop, int32_t *aX, int32_t *aY) { diff --git a/widget/windows/winrt/MetroWidget.h b/widget/windows/winrt/MetroWidget.h index 7c57e1a78e3..084267aad60 100644 --- a/widget/windows/winrt/MetroWidget.h +++ b/widget/windows/winrt/MetroWidget.h @@ -175,6 +175,8 @@ public: virtual void FreeNativeData(void * data, uint32_t aDataType); virtual nsIntPoint WidgetToScreenOffset(); + already_AddRefed GetPresShell(); + void UserActivity(); #ifdef ACCESSIBILITY