fune/browser/base/content/browser-customization.js
Drew Willcoxon c901619704 Bug 1545742 - Quantumbar: Remove the proper nsIController on uninit and customize start so that left/right arrow and home/end keys work after exiting customize mode. r=mak
When UrlbarInput.uninit is called after customize mode ends, uninit calls this.inputField.controllers.removeControllerAt(0), which is supposed to remove the input's CopyCutController inserted in the constructor. But the controller at index 0 at that point is not the CopyCutController. Instead it's some built-in controller that supports these commands (at least these): cmd_charPrevious, cmd_charPrevious, cmd_beginLine, cmd_endLine. (Verified by adding logging to nsXULControllers::GetControllerForCommand.) That's why arrow left/right and home/end don't work after ending customize mode.

The problem is that this.inputField.controllers in the constructor and this.inputField.controllers in uninit (when customize mode ends) are not the same. I wasn't able to track down why, but I'm guessing that the textbox or something in its state is being reset or cloned when customized mode ends or maybe right after it starts. The CopyCutController isn't in the controllers array at all on uninit. (Verified by adding support for cmd_adw and iterating through the controllers array, looking for a controller supporting cmd_adw.)

Note that urlbarBindings.xml has a try-catch around removeController(), I'm guessing for what turns out to be this reason: https://searchfox.org/mozilla-central/rev/7944190ad1668a94223b950a19f1fffe8662d6b8/browser/base/content/urlbarBindings.xml#190

However, CopyCutController *is* in the controllers array when customize mode starts. So I added a new gURLBarHandler.customizeStart method that calls a new UrlbarInput.removeCopyCutController method.

Other things I tried or thought of doing:

Call gURLBarHandler._reset on customize start instead of end. Problem with that is that the UrlbarInput ends up getting immediately recreated because some other parts of the browser access gURLBar at that time. (Of course I replaced the `gURLBar = this.urlbar` assignment in _reset with another lazy getter definition.)

Just don't worry about removing CopyCutController at all. That seems bad because then we'd leak it, unless the controller is removed or the controllers array is emptied at some point by XUL, and I'm not at all certain about that. (Although I guess this is effectively what awesomebar does, given the link above!)

Differential Revision: https://phabricator.services.mozilla.com/D29613

--HG--
extra : moz-landing-system : lando
2019-05-03 16:03:00 +00:00

184 lines
5 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
* 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/. */
// This file is loaded into the browser window scope.
/* eslint-env mozilla/browser-window */
/**
* Customization handler prepares this browser window for entering and exiting
* customization mode by handling customizationstarting and customizationending
* events.
*/
var CustomizationHandler = {
handleEvent(aEvent) {
switch (aEvent.type) {
case "customizationstarting":
this._customizationStarting();
break;
case "customizationending":
this._customizationEnding(aEvent.detail);
break;
}
},
isCustomizing() {
return document.documentElement.hasAttribute("customizing");
},
_customizationStarting() {
// Disable the toolbar context menu items
let menubar = document.getElementById("main-menubar");
for (let childNode of menubar.children)
childNode.setAttribute("disabled", true);
let cmd = document.getElementById("cmd_CustomizeToolbars");
cmd.setAttribute("disabled", "true");
UpdateUrlbarSearchSplitterState();
PlacesToolbarHelper.customizeStart();
gURLBarHandler.customizeStart();
},
_customizationEnding(aDetails) {
// Update global UI elements that may have been added or removed
if (aDetails.changed &&
AppConstants.platform != "macosx") {
updateEditUIVisibility();
}
PlacesToolbarHelper.customizeDone();
UpdateUrlbarSearchSplitterState();
// Update the urlbar
URLBarSetURI();
XULBrowserWindow.asyncUpdateUI();
// Re-enable parts of the UI we disabled during the dialog
let menubar = document.getElementById("main-menubar");
for (let childNode of menubar.children)
childNode.setAttribute("disabled", false);
let cmd = document.getElementById("cmd_CustomizeToolbars");
cmd.removeAttribute("disabled");
gBrowser.selectedBrowser.focus();
gURLBarHandler.customizeEnd();
},
};
var AutoHideMenubar = {
get _node() {
delete this._node;
return this._node = document.getElementById("toolbar-menubar");
},
_contextMenuListener: {
contextMenu: null,
get active() {
return !!this.contextMenu;
},
init(event) {
// Ignore mousedowns in <menupopup>s.
if (event.target.closest("menupopup")) {
return;
}
let contextMenuId = AutoHideMenubar._node.getAttribute("context");
this.contextMenu = document.getElementById(contextMenuId);
this.contextMenu.addEventListener("popupshown", this);
this.contextMenu.addEventListener("popuphiding", this);
AutoHideMenubar._node.addEventListener("mousemove", this);
},
handleEvent(event) {
switch (event.type) {
case "popupshown":
AutoHideMenubar._node.removeEventListener("mousemove", this);
break;
case "popuphiding":
case "mousemove":
AutoHideMenubar._setInactiveAsync();
AutoHideMenubar._node.removeEventListener("mousemove", this);
this.contextMenu.removeEventListener("popuphiding", this);
this.contextMenu.removeEventListener("popupshown", this);
this.contextMenu = null;
break;
}
},
},
init() {
this._node.addEventListener("toolbarvisibilitychange", this);
if (this._node.getAttribute("autohide") == "true") {
this._enable();
}
},
_updateState() {
if (this._node.getAttribute("autohide") == "true") {
this._enable();
} else {
this._disable();
}
},
_events: ["DOMMenuBarInactive", "DOMMenuBarActive", "popupshowing", "mousedown"],
_enable() {
this._node.setAttribute("inactive", "true");
for (let event of this._events) {
this._node.addEventListener(event, this);
}
},
_disable() {
this._setActive();
for (let event of this._events) {
this._node.removeEventListener(event, this);
}
},
handleEvent(event) {
switch (event.type) {
case "toolbarvisibilitychange":
this._updateState();
break;
case "popupshowing":
// fall through
case "DOMMenuBarActive":
this._setActive();
break;
case "mousedown":
if (event.button == 2) {
this._contextMenuListener.init(event);
}
break;
case "DOMMenuBarInactive":
if (!this._contextMenuListener.active)
this._setInactiveAsync();
break;
}
},
_setInactiveAsync() {
this._inactiveTimeout = setTimeout(() => {
if (this._node.getAttribute("autohide") == "true") {
this._inactiveTimeout = null;
this._node.setAttribute("inactive", "true");
}
}, 0);
},
_setActive() {
if (this._inactiveTimeout) {
clearTimeout(this._inactiveTimeout);
this._inactiveTimeout = null;
}
this._node.removeAttribute("inactive");
},
};