зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1328304 - Hide notifications when the address bar has focus and the URL is being edited. r=johannh
MozReview-Commit-ID: 5GhCsA9Gi3f --HG-- extra : rebase_source : 17befcf3c98465dc9bf6407a812e81d83bc185bc
This commit is contained in:
Родитель
e04a84bbc8
Коммит
4bf8de0d84
|
@ -151,9 +151,16 @@ XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function() {
|
|||
let tmp = {};
|
||||
Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
|
||||
try {
|
||||
// Hide all notifications while the URL is being edited and the address bar
|
||||
// has focus, including the virtual focus in the results popup.
|
||||
let shouldSuppress = () => {
|
||||
return gURLBar.getAttribute("pageproxystate") != "valid" &&
|
||||
gURLBar.focused;
|
||||
};
|
||||
return new tmp.PopupNotifications(gBrowser,
|
||||
document.getElementById("notification-popup"),
|
||||
document.getElementById("notification-popup-box"));
|
||||
document.getElementById("notification-popup-box"),
|
||||
{ shouldSuppress });
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
return null;
|
||||
|
@ -2442,17 +2449,8 @@ function BrowserPageInfo(documentURL, initialTab, imageElement, frameOuterWindow
|
|||
*
|
||||
* @param aURI [optional]
|
||||
* nsIURI to set. If this is unspecified, the current URI will be used.
|
||||
* @param aOptions [optional]
|
||||
* An object with the following properties:
|
||||
* {
|
||||
* isForLocationChange:
|
||||
* Set to true to indicate that the function was invoked to respond
|
||||
* to a location change event, rather than to reset the current URI
|
||||
* value. This is useful to avoid calling PopupNotifications.jsm
|
||||
* multiple times.
|
||||
* }
|
||||
*/
|
||||
function URLBarSetURI(aURI, aOptions = {}) {
|
||||
function URLBarSetURI(aURI) {
|
||||
var value = gBrowser.userTypedValue;
|
||||
var valid = false;
|
||||
|
||||
|
@ -2485,7 +2483,7 @@ function URLBarSetURI(aURI, aOptions = {}) {
|
|||
|
||||
gURLBar.value = value;
|
||||
gURLBar.valueIsTyped = !valid;
|
||||
SetPageProxyState(valid ? "valid" : "invalid", aOptions);
|
||||
SetPageProxyState(valid ? "valid" : "invalid");
|
||||
}
|
||||
|
||||
function losslessDecodeURI(aURI) {
|
||||
|
@ -2592,19 +2590,15 @@ function UpdatePageProxyState() {
|
|||
* related user interface elments should be shown because the URI in the
|
||||
* location bar matches the loaded page. The string "invalid" indicates
|
||||
* that the URI in the location bar is different than the loaded page.
|
||||
* @param aOptions [optional]
|
||||
* An object with the following properties:
|
||||
* {
|
||||
* isForLocationChange:
|
||||
* Set to true to indicate that the function was invoked to respond
|
||||
* to a location change event. This is useful to avoid calling
|
||||
* PopupNotifications.jsm multiple times.
|
||||
* }
|
||||
*/
|
||||
function SetPageProxyState(aState, aOptions = {}) {
|
||||
function SetPageProxyState(aState) {
|
||||
if (!gURLBar)
|
||||
return;
|
||||
|
||||
let oldPageProxyState = gURLBar.getAttribute("pageproxystate");
|
||||
// The "browser_urlbar_stop_pending.js" test uses a MutationObserver to do
|
||||
// some verifications at this point, and it breaks if we don't write the
|
||||
// attribute, even if it hasn't changed (bug 1338115).
|
||||
gURLBar.setAttribute("pageproxystate", aState);
|
||||
|
||||
// the page proxy state is set to valid via OnLocationChange, which
|
||||
|
@ -2616,14 +2610,26 @@ function SetPageProxyState(aState, aOptions = {}) {
|
|||
gURLBar.removeEventListener("input", UpdatePageProxyState);
|
||||
}
|
||||
|
||||
// Only need to call anchorVisibilityChange if the PopupNotifications object
|
||||
// for this window has already been initialized (i.e. its getter no
|
||||
// longer exists). If this is the result of a locations change, then we will
|
||||
// already invoke PopupNotifications.locationChange separately.
|
||||
if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get &&
|
||||
!aOptions.isForLocationChange) {
|
||||
PopupNotifications.anchorVisibilityChange();
|
||||
// After we've ensured that we've applied the listeners and updated the value
|
||||
// of gLastValidURLStr, return early if the actual state hasn't changed.
|
||||
if (oldPageProxyState == aState) {
|
||||
return;
|
||||
}
|
||||
|
||||
UpdatePopupNotificationsVisibility();
|
||||
}
|
||||
|
||||
function UpdatePopupNotificationsVisibility() {
|
||||
// Only need to do something if the PopupNotifications object for this window
|
||||
// has already been initialized (i.e. its getter no longer exists).
|
||||
if (Object.getOwnPropertyDescriptor(window, "PopupNotifications").get) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify PopupNotifications that the visible anchors may have changed. This
|
||||
// also checks the suppression state according to the "shouldSuppress"
|
||||
// function defined earlier in this file.
|
||||
PopupNotifications.anchorVisibilityChange();
|
||||
}
|
||||
|
||||
function PageProxyClickHandler(aEvent) {
|
||||
|
@ -4542,7 +4548,7 @@ var XULBrowserWindow = {
|
|||
this.reloadCommand.removeAttribute("disabled");
|
||||
}
|
||||
|
||||
URLBarSetURI(aLocationURI, { isForLocationChange: true });
|
||||
URLBarSetURI(aLocationURI);
|
||||
|
||||
BookmarkingUI.onLocationChange();
|
||||
|
||||
|
|
|
@ -102,49 +102,126 @@ var tests = [
|
|||
gBrowser.selectedTab = this.oldSelectedTab;
|
||||
}
|
||||
},
|
||||
// Test that popupnotifications are anchored to the identity icon while
|
||||
// editing the URL in the location bar, and restored to their anchors when the
|
||||
// URL is reverted.
|
||||
// Test that popupnotifications are hidden while editing the URL in the
|
||||
// location bar, anchored to the identity icon when the focus is moved away
|
||||
// from the location bar, and restored when the URL is reverted.
|
||||
{ id: "Test#4",
|
||||
*run() {
|
||||
this.oldSelectedTab = gBrowser.selectedTab;
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
|
||||
for (let persistent of [false, true]) {
|
||||
let shown = waitForNotificationPanel();
|
||||
this.notifyObj = new BasicNotification(this.id);
|
||||
this.notifyObj.anchorID = "geo-notification-icon";
|
||||
this.notifyObj.addOptions({ persistent });
|
||||
this.notification = showNotification(this.notifyObj);
|
||||
yield shown;
|
||||
|
||||
let shownInitially = waitForNotificationPanel();
|
||||
checkPopup(PopupNotifications.panel, this.notifyObj);
|
||||
|
||||
// Typing in the location bar should hide the notification.
|
||||
let hidden = waitForNotificationPanelHidden();
|
||||
gURLBar.select();
|
||||
EventUtils.synthesizeKey("*", {});
|
||||
yield hidden;
|
||||
|
||||
is(document.getElementById("geo-notification-icon").boxObject.width, 0,
|
||||
"geo anchor shouldn't be visible");
|
||||
|
||||
// Moving focus to the next control should show the notifications again,
|
||||
// anchored to the identity icon. We clear the URL bar before moving the
|
||||
// focus so that the awesomebar popup doesn't get in the way.
|
||||
shown = waitForNotificationPanel();
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
yield shown;
|
||||
|
||||
is(PopupNotifications.panel.anchorNode.id, "identity-icon",
|
||||
"notification anchored to identity icon");
|
||||
|
||||
// Moving focus to the location bar should hide the notification again.
|
||||
hidden = waitForNotificationPanelHidden();
|
||||
EventUtils.synthesizeKey("VK_TAB", { shiftKey: true });
|
||||
yield hidden;
|
||||
|
||||
// Reverting the URL should show the notification again.
|
||||
shown = waitForNotificationPanel();
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield shown;
|
||||
|
||||
checkPopup(PopupNotifications.panel, this.notifyObj);
|
||||
|
||||
hidden = waitForNotificationPanelHidden();
|
||||
this.notification.remove();
|
||||
yield hidden;
|
||||
}
|
||||
goNext();
|
||||
}
|
||||
},
|
||||
// Test that popupnotifications triggered while editing the URL in the
|
||||
// location bar are only shown later when the URL is reverted.
|
||||
{ id: "Test#5",
|
||||
*run() {
|
||||
for (let persistent of [false, true]) {
|
||||
// Start editing the URL, ensuring that the awesomebar popup is hidden.
|
||||
gURLBar.select();
|
||||
EventUtils.synthesizeKey("*", {});
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
|
||||
// Trying to show a notification should display nothing.
|
||||
let notShowing = promiseTopicObserved("PopupNotifications-updateNotShowing");
|
||||
this.notifyObj = new BasicNotification(this.id);
|
||||
this.notifyObj.anchorID = "geo-notification-icon";
|
||||
this.notifyObj.addOptions({ persistent });
|
||||
this.notification = showNotification(this.notifyObj);
|
||||
yield notShowing;
|
||||
|
||||
// Reverting the URL should show the notification.
|
||||
let shown = waitForNotificationPanel();
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield shown;
|
||||
|
||||
checkPopup(PopupNotifications.panel, this.notifyObj);
|
||||
|
||||
let hidden = waitForNotificationPanelHidden();
|
||||
this.notification.remove();
|
||||
yield hidden;
|
||||
}
|
||||
|
||||
goNext();
|
||||
}
|
||||
},
|
||||
// Test that persistent panels are still open after switching to another tab
|
||||
// and back, even while editing the URL in the new tab.
|
||||
{ id: "Test#6",
|
||||
*run() {
|
||||
let shown = waitForNotificationPanel();
|
||||
this.notifyObj = new BasicNotification(this.id);
|
||||
this.notifyObj.anchorID = "geo-notification-icon";
|
||||
this.notifyObj.addOptions({
|
||||
persistent: true,
|
||||
});
|
||||
this.notification = showNotification(this.notifyObj);
|
||||
yield shownInitially;
|
||||
yield shown;
|
||||
|
||||
checkPopup(PopupNotifications.panel, this.notifyObj);
|
||||
// Switching to a new tab should hide the notification.
|
||||
let hidden = waitForNotificationPanelHidden();
|
||||
this.oldSelectedTab = gBrowser.selectedTab;
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
|
||||
yield hidden;
|
||||
|
||||
let shownAgain = waitForNotificationPanel();
|
||||
// This will cause the popup to hide and show again.
|
||||
// Start editing the URL.
|
||||
gURLBar.select();
|
||||
EventUtils.synthesizeKey("*", {});
|
||||
// Keep the URL bar empty, so we don't show the awesomebar.
|
||||
EventUtils.synthesizeKey("VK_BACK_SPACE", {});
|
||||
yield shownAgain;
|
||||
|
||||
is(document.getElementById("geo-notification-icon").boxObject.width, 0,
|
||||
"geo anchor shouldn't be visible");
|
||||
is(PopupNotifications.panel.anchorNode.id, "identity-icon",
|
||||
"notification anchored to identity icon");
|
||||
|
||||
let shownLastTime = waitForNotificationPanel();
|
||||
// This will cause the popup to hide and show again.
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
yield shownLastTime;
|
||||
// Switching to the old tab should show the notification again.
|
||||
shown = waitForNotificationPanel();
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
gBrowser.selectedTab = this.oldSelectedTab;
|
||||
yield shown;
|
||||
|
||||
checkPopup(PopupNotifications.panel, this.notifyObj);
|
||||
|
||||
let hidden = new Promise(resolve => onPopupEvent("popuphidden", resolve));
|
||||
hidden = waitForNotificationPanelHidden();
|
||||
this.notification.remove();
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
gBrowser.selectedTab = this.oldSelectedTab;
|
||||
yield hidden;
|
||||
|
||||
goNext();
|
||||
|
|
|
@ -264,6 +264,14 @@ function waitForNotificationPanel() {
|
|||
});
|
||||
}
|
||||
|
||||
function waitForNotificationPanelHidden() {
|
||||
return new Promise(resolve => {
|
||||
onPopupEvent("popuphidden", function() {
|
||||
resolve(this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function triggerMainCommand(popup) {
|
||||
let notifications = popup.childNodes;
|
||||
ok(notifications.length > 0, "at least one notification displayed");
|
||||
|
|
|
@ -1227,6 +1227,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
if (event.originalTarget == this.inputField) {
|
||||
this._hideURLTooltip();
|
||||
this.formatValue();
|
||||
if (this.getAttribute("pageproxystate") != "valid") {
|
||||
UpdatePopupNotificationsVisibility();
|
||||
}
|
||||
}
|
||||
]]></handler>
|
||||
|
||||
|
@ -1234,6 +1237,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||
if (event.originalTarget == this.inputField) {
|
||||
this._clearNoActions();
|
||||
this.formatValue();
|
||||
if (this.getAttribute("pageproxystate") != "valid") {
|
||||
UpdatePopupNotificationsVisibility();
|
||||
}
|
||||
}
|
||||
if (ExtensionSearchHandler.hasActiveInputSession()) {
|
||||
ExtensionSearchHandler.handleInputCancelled();
|
||||
|
|
|
@ -202,8 +202,17 @@ Notification.prototype = {
|
|||
* parent of anchor elements whose IDs are passed to show().
|
||||
* It is used as a fallback popup anchor if notifications specify
|
||||
* invalid or non-existent anchor IDs.
|
||||
* @param options
|
||||
* An optional object with the following optional properties:
|
||||
* {
|
||||
* shouldSuppress:
|
||||
* If this function returns true, then all notifications are
|
||||
* suppressed for this window. This state is checked on construction
|
||||
* and when the "anchorVisibilityChange" method is called.
|
||||
* }
|
||||
*/
|
||||
this.PopupNotifications = function PopupNotifications(tabbrowser, panel, iconBox) {
|
||||
this.PopupNotifications = function PopupNotifications(tabbrowser, panel,
|
||||
iconBox, options = {}) {
|
||||
if (!(tabbrowser instanceof Ci.nsIDOMXULElement))
|
||||
throw "Invalid tabbrowser";
|
||||
if (iconBox && !(iconBox instanceof Ci.nsIDOMXULElement))
|
||||
|
@ -211,6 +220,9 @@ this.PopupNotifications = function PopupNotifications(tabbrowser, panel, iconBox
|
|||
if (!(panel instanceof Ci.nsIDOMXULElement))
|
||||
throw "Invalid panel";
|
||||
|
||||
this._shouldSuppress = options.shouldSuppress || (() => false);
|
||||
this._suppress = this._shouldSuppress();
|
||||
|
||||
this.window = tabbrowser.ownerGlobal;
|
||||
this.panel = panel;
|
||||
this.tabbrowser = tabbrowser;
|
||||
|
@ -524,25 +536,35 @@ PopupNotifications.prototype = {
|
|||
this._setNotificationsForBrowser(aBrowser, notifications);
|
||||
|
||||
if (this._isActiveBrowser(aBrowser)) {
|
||||
// get the anchor element if the browser has defined one so it will
|
||||
// _update will handle both the tabs iconBox and non-tab permission
|
||||
// anchors.
|
||||
this._update(notifications, this._getAnchorsForNotifications(notifications,
|
||||
getAnchorFromBrowser(aBrowser)));
|
||||
this.anchorVisibilityChange();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called by the consumer to indicate that the visibility of the notification
|
||||
* anchors may have changed, but the location has not changed. This may result
|
||||
* in the "showing" and "shown" events for visible notifications to be
|
||||
* invoked even if the anchor has not changed.
|
||||
* anchors may have changed, but the location has not changed. This also
|
||||
* checks whether all notifications are suppressed for this window.
|
||||
*
|
||||
* Calling this method may result in the "showing" and "shown" events for
|
||||
* visible notifications to be invoked even if the anchor has not changed.
|
||||
*/
|
||||
anchorVisibilityChange() {
|
||||
let notifications =
|
||||
this._getNotificationsForBrowser(this.tabbrowser.selectedBrowser);
|
||||
this._update(notifications, this._getAnchorsForNotifications(notifications,
|
||||
getAnchorFromBrowser(this.tabbrowser.selectedBrowser)));
|
||||
let suppress = this._shouldSuppress();
|
||||
if (!suppress) {
|
||||
// If notifications are not suppressed, always update the visibility.
|
||||
this._suppress = false;
|
||||
let notifications =
|
||||
this._getNotificationsForBrowser(this.tabbrowser.selectedBrowser);
|
||||
this._update(notifications, this._getAnchorsForNotifications(notifications,
|
||||
getAnchorFromBrowser(this.tabbrowser.selectedBrowser)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Notifications are suppressed, ensure that the panel is hidden.
|
||||
if (!this._suppress) {
|
||||
this._suppress = true;
|
||||
this._hidePanel().catch(Cu.reportError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -1014,10 +1036,12 @@ PopupNotifications.prototype = {
|
|||
}
|
||||
|
||||
// Filter out notifications that have been dismissed, unless they are
|
||||
// persistent.
|
||||
let notificationsToShow = notifications.filter(function(n) {
|
||||
return (!n.dismissed || n.options.persistent) && !n.options.neverShow;
|
||||
});
|
||||
// persistent. Also check if we should not show any notification.
|
||||
let notificationsToShow = [];
|
||||
if (!this._suppress) {
|
||||
notificationsToShow = notifications.filter(
|
||||
n => (!n.dismissed || n.options.persistent) && !n.options.neverShow);
|
||||
}
|
||||
|
||||
if (useIconBox) {
|
||||
// Hide icons of the previous tab.
|
||||
|
@ -1283,18 +1307,23 @@ PopupNotifications.prototype = {
|
|||
},
|
||||
|
||||
_onPopupHidden: function PopupNotifications_onPopupHidden(event) {
|
||||
if (event.target != this.panel || this._ignoreDismissal) {
|
||||
if (this._ignoreDismissal) {
|
||||
this._ignoreDismissal.resolve();
|
||||
this._ignoreDismissal = null;
|
||||
}
|
||||
if (event.target != this.panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that when the panel comes up without user interaction,
|
||||
// we don't autofocus it.
|
||||
// We may have removed the "noautofocus" attribute before showing the panel
|
||||
// if it was opened with user interaction. When the panel is closed, we have
|
||||
// to restore the attribute to its default value, so we don't autofocus it
|
||||
// if it is subsequently opened from a different code path.
|
||||
this.panel.setAttribute("noautofocus", "true");
|
||||
|
||||
// Handle the case where the panel was closed programmatically.
|
||||
if (this._ignoreDismissal) {
|
||||
this._ignoreDismissal.resolve();
|
||||
this._ignoreDismissal = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this._dismissOrRemoveCurrentNotifications();
|
||||
|
||||
this._clearPanel();
|
||||
|
|
Загрузка…
Ссылка в новой задаче