зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 3 changesets (bug 1434883) for frequent leaks in AbstractThread, AnimationTimeline, Annotators, Array, AsyncFreeSnowWhite, ... on a CLOSED TREE
Backed out changeset c8115e90ffa8 (bug 1434883) Backed out changeset 22c83b1d417d (bug 1434883) Backed out changeset 085796417462 (bug 1434883)
This commit is contained in:
Родитель
c88a7c210e
Коммит
793dad7903
|
@ -187,25 +187,22 @@ var BrowserPageActions = {
|
|||
togglePanelForAction(action, panelNode = null) {
|
||||
let aaPanelNode = this.activatedActionPanelNode;
|
||||
if (panelNode) {
|
||||
// Note that this particular code path will not prevent the panel from
|
||||
// opening later if PanelMultiView.showPopup was called but the panel has
|
||||
// not been opened yet.
|
||||
if (panelNode.state != "closed") {
|
||||
PanelMultiView.hidePopup(panelNode);
|
||||
panelNode.hidePopup();
|
||||
return;
|
||||
}
|
||||
if (aaPanelNode) {
|
||||
PanelMultiView.hidePopup(aaPanelNode);
|
||||
aaPanelNode.hidePopup();
|
||||
}
|
||||
} else if (aaPanelNode) {
|
||||
PanelMultiView.hidePopup(aaPanelNode);
|
||||
aaPanelNode.hidePopup();
|
||||
return;
|
||||
} else {
|
||||
panelNode = this._makeActivatedActionPanelForAction(action);
|
||||
}
|
||||
|
||||
// Hide the main panel before showing the action's panel.
|
||||
PanelMultiView.hidePopup(this.panelNode);
|
||||
this.panelNode.hidePopup();
|
||||
|
||||
let anchorNode = this.panelAnchorNodeForAction(action);
|
||||
anchorNode.setAttribute("open", "true");
|
||||
|
@ -213,8 +210,7 @@ var BrowserPageActions = {
|
|||
anchorNode.removeAttribute("open");
|
||||
}, { once: true });
|
||||
|
||||
PanelMultiView.openPopup(panelNode, anchorNode, "bottomcenter topright")
|
||||
.catch(Cu.reportError);
|
||||
panelNode.openPopup(anchorNode, "bottomcenter topright");
|
||||
},
|
||||
|
||||
_makeActivatedActionPanelForAction(action) {
|
||||
|
@ -558,7 +554,7 @@ var BrowserPageActions = {
|
|||
return;
|
||||
}
|
||||
// Otherwise, hide the main popup in case it was open:
|
||||
PanelMultiView.hidePopup(this.panelNode);
|
||||
this.panelNode.hidePopup();
|
||||
|
||||
// Toggle the activated action's panel if necessary
|
||||
if (action.subview || action.wantsIframe) {
|
||||
|
@ -698,12 +694,12 @@ var BrowserPageActions = {
|
|||
// close it.
|
||||
let panelNode = this.activatedActionPanelNode;
|
||||
if (panelNode && panelNode.anchorNode.id == this.mainButtonNode.id) {
|
||||
PanelMultiView.hidePopup(panelNode);
|
||||
panelNode.hidePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.panelNode.state == "open") {
|
||||
PanelMultiView.hidePopup(this.panelNode);
|
||||
this.panelNode.hidePopup();
|
||||
} else if (this.panelNode.state == "closed") {
|
||||
this.showPanel(event);
|
||||
}
|
||||
|
@ -727,10 +723,10 @@ var BrowserPageActions = {
|
|||
this.mainButtonNode.removeAttribute("open");
|
||||
}, {once: true});
|
||||
this.mainButtonNode.setAttribute("open", "true");
|
||||
PanelMultiView.openPopup(this.panelNode, this.mainButtonNode, {
|
||||
this.panelNode.openPopup(this.mainButtonNode, {
|
||||
position: "bottomcenter topright",
|
||||
triggerEvent: event,
|
||||
}).catch(Cu.reportError);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -885,10 +881,10 @@ var BrowserPageActionFeedback = {
|
|||
this.panelNode.hidden = false;
|
||||
|
||||
let anchor = BrowserPageActions.panelAnchorNodeForAction(action, event);
|
||||
PanelMultiView.openPopup(this.panelNode, anchor, {
|
||||
this.panelNode.openPopup(anchor, {
|
||||
position: "bottomcenter topright",
|
||||
triggerEvent: event,
|
||||
}).catch(Cu.reportError);
|
||||
});
|
||||
|
||||
this.panelNode.addEventListener("popupshown", () => {
|
||||
this.feedbackAnimationBox.setAttribute("animate", "true");
|
||||
|
@ -916,7 +912,7 @@ BrowserPageActions.bookmark = {
|
|||
},
|
||||
|
||||
onCommand(event, buttonNode) {
|
||||
PanelMultiView.hidePopup(BrowserPageActions.panelNode);
|
||||
BrowserPageActions.panelNode.hidePopup();
|
||||
BookmarkingUI.onStarCommand(event);
|
||||
},
|
||||
};
|
||||
|
@ -929,7 +925,7 @@ BrowserPageActions.copyURL = {
|
|||
},
|
||||
|
||||
onCommand(event, buttonNode) {
|
||||
PanelMultiView.hidePopup(BrowserPageActions.panelNode);
|
||||
BrowserPageActions.panelNode.hidePopup();
|
||||
Cc["@mozilla.org/widget/clipboardhelper;1"]
|
||||
.getService(Ci.nsIClipboardHelper)
|
||||
.copyString(gURLBar.makeURIReadable(gBrowser.selectedBrowser.currentURI).displaySpec);
|
||||
|
@ -946,7 +942,7 @@ BrowserPageActions.emailLink = {
|
|||
},
|
||||
|
||||
onCommand(event, buttonNode) {
|
||||
PanelMultiView.hidePopup(BrowserPageActions.panelNode);
|
||||
BrowserPageActions.panelNode.hidePopup();
|
||||
MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
|
||||
},
|
||||
};
|
||||
|
@ -996,7 +992,7 @@ BrowserPageActions.sendToDevice = {
|
|||
|
||||
item.addEventListener("command", event => {
|
||||
if (panelNode) {
|
||||
PanelMultiView.hidePopup(panelNode);
|
||||
panelNode.hidePopup();
|
||||
}
|
||||
// There are items in the subview that don't represent devices: "Sign
|
||||
// in", "Learn about Sync", etc. Device items will be .sendtab-target.
|
||||
|
|
|
@ -41,7 +41,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
|
||||
PageActions: "resource:///modules/PageActions.jsm",
|
||||
PageThumbs: "resource://gre/modules/PageThumbs.jsm",
|
||||
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
|
||||
PanelView: "resource:///modules/PanelMultiView.jsm",
|
||||
PluralForm: "resource://gre/modules/PluralForm.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
|
@ -7260,7 +7259,7 @@ var gIdentityHandler = {
|
|||
handleMoreInfoClick(event) {
|
||||
displaySecurityInfo();
|
||||
event.stopPropagation();
|
||||
PanelMultiView.hidePopup(this._identityPopup);
|
||||
this._identityPopup.hidePopup();
|
||||
},
|
||||
|
||||
showSecuritySubView() {
|
||||
|
@ -7282,14 +7281,14 @@ var gIdentityHandler = {
|
|||
// Reload the page with the content unblocked
|
||||
BrowserReloadWithFlags(
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_MIXED_CONTENT);
|
||||
PanelMultiView.hidePopup(this._identityPopup);
|
||||
this._identityPopup.hidePopup();
|
||||
},
|
||||
|
||||
enableMixedContentProtection() {
|
||||
gBrowser.selectedBrowser.messageManager.sendAsyncMessage(
|
||||
"MixedContent:ReenableProtection", {});
|
||||
BrowserReload();
|
||||
PanelMultiView.hidePopup(this._identityPopup);
|
||||
this._identityPopup.hidePopup();
|
||||
},
|
||||
|
||||
removeCertException() {
|
||||
|
@ -7301,7 +7300,7 @@ var gIdentityHandler = {
|
|||
let port = this._uri.port > 0 ? this._uri.port : 443;
|
||||
this._overrideService.clearValidityOverride(host, port);
|
||||
BrowserReloadSkipCache();
|
||||
PanelMultiView.hidePopup(this._identityPopup);
|
||||
this._identityPopup.hidePopup();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -7367,7 +7366,7 @@ var gIdentityHandler = {
|
|||
// Handle a location change while the Control Center is focused
|
||||
// by closing the popup (bug 1207542)
|
||||
if (shouldHidePopup) {
|
||||
PanelMultiView.hidePopup(this._identityPopup);
|
||||
this._identityPopup.hidePopup();
|
||||
}
|
||||
|
||||
// NOTE: We do NOT update the identity popup (the control center) when
|
||||
|
@ -7831,8 +7830,7 @@ var gIdentityHandler = {
|
|||
this._identityBox.setAttribute("open", "true");
|
||||
|
||||
// Now open the popup, anchored off the primary chrome element
|
||||
PanelMultiView.openPopup(this._identityPopup, this._identityIcon,
|
||||
"bottomcenter topleft").catch(Cu.reportError);
|
||||
this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
|
||||
},
|
||||
|
||||
onPopupShown(event) {
|
||||
|
@ -7865,7 +7863,7 @@ var gIdentityHandler = {
|
|||
// Hide the panel when focusing an element that is
|
||||
// neither an ancestor nor descendant unless the panel has
|
||||
// @noautohide (e.g. for a tour).
|
||||
PanelMultiView.hidePopup(this._identityPopup);
|
||||
this._identityPopup.hidePopup();
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -14,10 +14,7 @@ add_task(async function() {
|
|||
await loadBadCertPage("https://expired.example.com");
|
||||
|
||||
let { gIdentityHandler } = gBrowser.ownerGlobal;
|
||||
let promisePanelOpen = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
|
||||
gIdentityHandler._identityBox.click();
|
||||
await promisePanelOpen;
|
||||
|
||||
let promiseViewShown = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "ViewShown");
|
||||
document.getElementById("identity-popup-security-expander").click();
|
||||
await promiseViewShown;
|
||||
|
|
|
@ -14,7 +14,7 @@ const EXPECTED_APPMENU_OPEN_REFLOWS = [
|
|||
{
|
||||
stack: [
|
||||
"openPopup@chrome://global/content/bindings/popup.xml",
|
||||
"openPopup/this._openPopupPromise<@resource:///modules/PanelMultiView.jsm",
|
||||
"show/</<@chrome://browser/content/customizableui/panelUI.js",
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
@ -204,9 +204,7 @@ async function assertMixedContentBlockingState(tabbrowser, states = {}) {
|
|||
}
|
||||
|
||||
// Make sure the identity popup has the correct mixedcontent states
|
||||
let promisePanelOpen = BrowserTestUtils.waitForEvent(gIdentityHandler._identityPopup, "popupshown");
|
||||
gIdentityHandler._identityBox.click();
|
||||
await promisePanelOpen;
|
||||
let popupAttr = doc.getElementById("identity-popup").getAttribute("mixedcontent");
|
||||
let bodyAttr = doc.getElementById("identity-popup-securityView-body").getAttribute("mixedcontent");
|
||||
|
||||
|
|
|
@ -560,10 +560,10 @@ add_task(async function sendToDevice_inUrlbar() {
|
|||
Assert.notEqual(urlbarButton, null, "The urlbar button should exist");
|
||||
Assert.ok(!urlbarButton.disabled,
|
||||
"The urlbar button should not be disabled");
|
||||
let panelPromise =
|
||||
promisePanelShown(BrowserPageActions._activatedActionPanelID);
|
||||
EventUtils.synthesizeMouseAtCenter(urlbarButton, {});
|
||||
// The panel element for _activatedActionPanelID is created synchronously
|
||||
// only after the associated button has been clicked.
|
||||
await promisePanelShown(BrowserPageActions._activatedActionPanelID);
|
||||
await panelPromise;
|
||||
Assert.equal(urlbarButton.getAttribute("open"), "true",
|
||||
"Button has open attribute");
|
||||
|
||||
|
|
|
@ -266,14 +266,10 @@ function promisePanelHidden(panelIDOrNode) {
|
|||
|
||||
function promisePanelEvent(panelIDOrNode, eventType) {
|
||||
return new Promise(resolve => {
|
||||
let panel = panelIDOrNode;
|
||||
if (typeof panel == "string") {
|
||||
panel = document.getElementById(panelIDOrNode);
|
||||
if (!panel) {
|
||||
throw new Error(`Panel with ID "${panelIDOrNode}" does not exist.`);
|
||||
}
|
||||
}
|
||||
if ((eventType == "popupshown" && panel.state == "open") ||
|
||||
let panel = typeof(panelIDOrNode) != "string" ? panelIDOrNode :
|
||||
document.getElementById(panelIDOrNode);
|
||||
if (!panel ||
|
||||
(eventType == "popupshown" && panel.state == "open") ||
|
||||
(eventType == "popuphidden" && panel.state == "closed")) {
|
||||
executeSoon(resolve);
|
||||
return;
|
||||
|
|
|
@ -16,7 +16,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
|||
SearchWidgetTracker: "resource:///modules/SearchWidgetTracker.jsm",
|
||||
CustomizableWidgets: "resource:///modules/CustomizableWidgets.jsm",
|
||||
DeferredTask: "resource://gre/modules/DeferredTask.jsm",
|
||||
PanelMultiView: "resource:///modules/PanelMultiView.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
|
||||
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
|
||||
|
@ -1779,7 +1778,7 @@ var CustomizableUIInternal = {
|
|||
hidePanelForNode(aNode) {
|
||||
let panel = this._getPanelForNode(aNode);
|
||||
if (panel) {
|
||||
PanelMultiView.hidePopup(panel);
|
||||
panel.hidePopup();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -4265,7 +4264,7 @@ OverflowableToolbar.prototype = {
|
|||
if (aEvent.target == this._chevron) {
|
||||
this._onClickChevron(aEvent);
|
||||
} else {
|
||||
PanelMultiView.hidePopup(this._panel);
|
||||
this._panel.hidePopup();
|
||||
}
|
||||
break;
|
||||
case "customizationstarting":
|
||||
|
@ -4277,7 +4276,7 @@ OverflowableToolbar.prototype = {
|
|||
}
|
||||
break;
|
||||
case "dragend":
|
||||
PanelMultiView.hidePopup(this._panel);
|
||||
this._panel.hidePopup();
|
||||
break;
|
||||
case "popuphiding":
|
||||
this._onPanelHiding(aEvent);
|
||||
|
@ -4303,9 +4302,7 @@ OverflowableToolbar.prototype = {
|
|||
// Ensure we update the gEditUIVisible flag when opening the popup, in
|
||||
// case the edit controls are in it.
|
||||
this._panel.addEventListener("popupshowing", () => doc.defaultView.updateEditUIVisibility(), {once: true});
|
||||
PanelMultiView.openPopup(this._panel, anchor || this._chevron, {
|
||||
triggerEvent: aEvent,
|
||||
}).catch(Cu.reportError);
|
||||
this._panel.openPopup(anchor || this._chevron, { triggerEvent: aEvent });
|
||||
this._chevron.open = true;
|
||||
|
||||
this._panel.addEventListener("popupshown", () => {
|
||||
|
@ -4318,8 +4315,8 @@ OverflowableToolbar.prototype = {
|
|||
|
||||
_onClickChevron(aEvent) {
|
||||
if (this._chevron.open) {
|
||||
this._panel.hidePopup();
|
||||
this._chevron.open = false;
|
||||
PanelMultiView.hidePopup(this._panel);
|
||||
} else if (this._panel.state != "hiding" && !this._chevron.disabled) {
|
||||
this.show(aEvent);
|
||||
}
|
||||
|
@ -4611,7 +4608,7 @@ OverflowableToolbar.prototype = {
|
|||
}
|
||||
this._hideTimeoutId = window.setTimeout(() => {
|
||||
if (!this._panel.firstChild.matches(":hover")) {
|
||||
PanelMultiView.hidePopup(this._panel);
|
||||
this._panel.hidePopup();
|
||||
}
|
||||
}, OVERFLOW_PANEL_HIDE_DELAY_MS);
|
||||
});
|
||||
|
|
|
@ -12,13 +12,9 @@
|
|||
* <panelmultiview> element, although they don't need to be, as views can also
|
||||
* be imported into the panel from other panels or popup sets.
|
||||
*
|
||||
* The panel should be opened asynchronously using the openPopup static method
|
||||
* on the PanelMultiView object. This will display the view specified using the
|
||||
* mainViewId attribute on the contained <panelmultiview> element.
|
||||
*
|
||||
* Specific subviews can slide in using the showSubView method, and backwards
|
||||
* navigation can be done using the goBack method or through a button in the
|
||||
* subview headers.
|
||||
* The main view can be declared using the mainViewId attribute, and specific
|
||||
* subviews can slide in using the showSubView method. Backwards navigation can
|
||||
* be done using the goBack method or through a button in the subview headers.
|
||||
*
|
||||
* This diagram shows how <panelview> nodes move during navigation:
|
||||
*
|
||||
|
@ -53,7 +49,6 @@ this.EXPORTED_SYMBOLS = [
|
|||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "AppConstants",
|
||||
"resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
||||
|
@ -141,41 +136,6 @@ this.AssociatedToNode = class {
|
|||
* This is associated to <panelmultiview> elements by the panelUI.xml binding.
|
||||
*/
|
||||
this.PanelMultiView = class extends this.AssociatedToNode {
|
||||
/**
|
||||
* Tries to open the specified <panel> and displays the main view specified
|
||||
* with the "mainViewId" attribute on the <panelmultiview> node it contains.
|
||||
*
|
||||
* If the panel does not contain a <panelmultiview>, it is opened directly.
|
||||
* This allows consumers like page actions to accept different panel types.
|
||||
*
|
||||
* @see The non-static openPopup method for details.
|
||||
*/
|
||||
static async openPopup(panelNode, ...args) {
|
||||
let panelMultiViewNode = panelNode.querySelector("panelmultiview");
|
||||
if (panelMultiViewNode) {
|
||||
return this.forNode(panelMultiViewNode).openPopup(...args);
|
||||
}
|
||||
panelNode.openPopup(...args);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the specified <panel> which contains a <panelmultiview> node.
|
||||
*
|
||||
* If the panel does not contain a <panelmultiview>, it is closed directly.
|
||||
* This allows consumers like page actions to accept different panel types.
|
||||
*
|
||||
* @see The non-static hidePopup method for details.
|
||||
*/
|
||||
static hidePopup(panelNode) {
|
||||
let panelMultiViewNode = panelNode.querySelector("panelmultiview");
|
||||
if (panelMultiViewNode) {
|
||||
this.forNode(panelMultiViewNode).hidePopup();
|
||||
} else {
|
||||
panelNode.hidePopup();
|
||||
}
|
||||
}
|
||||
|
||||
get _panel() {
|
||||
return this.node.parentNode;
|
||||
}
|
||||
|
@ -228,15 +188,15 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
let panelView = this.openViews[this.openViews.length - 1];
|
||||
return (panelView && panelView.node) || this._mainView;
|
||||
}
|
||||
|
||||
constructor(node) {
|
||||
super(node);
|
||||
this._openPopupPromise = Promise.resolve(false);
|
||||
this._openPopupCancelCallback = () => {};
|
||||
/**
|
||||
* @return {Promise} showSubView() returns a promise, which is kept here for
|
||||
* random access.
|
||||
*/
|
||||
get currentShowPromise() {
|
||||
return this._currentShowPromise || Promise.resolve();
|
||||
}
|
||||
|
||||
connect() {
|
||||
this.connected = true;
|
||||
this.knownViews = new Set(Array.from(
|
||||
this.node.getElementsByTagName("panelview"),
|
||||
node => PanelView.forNode(node)));
|
||||
|
@ -266,6 +226,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
// Set CSS-determined attributes now to prevent a layout flush when we do
|
||||
// it when transitioning between panels.
|
||||
this._dir = cs.direction;
|
||||
this.showMainView();
|
||||
|
||||
// Proxy these public properties and methods, as used elsewhere by various
|
||||
// parts of the browser, to this instance.
|
||||
|
@ -275,7 +236,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
value: (...args) => this[method](...args)
|
||||
});
|
||||
});
|
||||
["current", "showingSubView"].forEach(property => {
|
||||
["current", "currentShowPromise", "showingSubView"].forEach(property => {
|
||||
Object.defineProperty(this.node, property, {
|
||||
enumerable: true,
|
||||
get: () => this[property]
|
||||
|
@ -305,154 +266,10 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
this._panel.removeEventListener("popupshown", this);
|
||||
this._panel.removeEventListener("popuphidden", this);
|
||||
this.window.removeEventListener("keydown", this);
|
||||
this.node = this._openPopupPromise = this._openPopupCancelCallback =
|
||||
this._viewContainer = this._viewStack = this.__dwu =
|
||||
this.node = this._viewContainer = this._viewStack = this.__dwu =
|
||||
this._panelViewCache = this._transitionDetails = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to open the panel associated with this PanelMultiView, and displays
|
||||
* the main view specified with the "mainViewId" attribute.
|
||||
*
|
||||
* The hidePopup method can be called while the operation is in progress to
|
||||
* prevent the panel from being displayed. View events may also cancel the
|
||||
* operation, so there is no guarantee that the panel will become visible.
|
||||
*
|
||||
* The "popuphidden" event will be fired either when the operation is canceled
|
||||
* or when the popup is closed later. This event can be used for example to
|
||||
* reset the "open" state of the anchor or tear down temporary panels.
|
||||
*
|
||||
* If this method is called again before the panel is shown, the result
|
||||
* depends on the operation currently in progress. If the operation was not
|
||||
* canceled, the panel is opened using the arguments from the previous call,
|
||||
* and this call is ignored. If the operation was canceled, it will be
|
||||
* retried again using the arguments from this call.
|
||||
*
|
||||
* It's not necessary for the <panelmultiview> binding to be connected when
|
||||
* this method is called, but the containing panel must have its display
|
||||
* turned on, for example it shouldn't have the "hidden" attribute.
|
||||
*
|
||||
* @param args
|
||||
* Arguments to be forwarded to the openPopup method of the panel.
|
||||
*
|
||||
* @resolves With true as soon as the request to display the panel has been
|
||||
* sent, or with false if the operation was canceled. The state of
|
||||
* the panel at this point is not guaranteed. It may be still
|
||||
* showing, completely shown, or completely hidden.
|
||||
* @rejects If an exception is thrown at any point in the process before the
|
||||
* request to display the panel is sent.
|
||||
*/
|
||||
async openPopup(...args) {
|
||||
// Set up the function that allows hidePopup or a second call to showPopup
|
||||
// to cancel the specific panel opening operation that we're starting below.
|
||||
// This function must be synchronous, meaning we can't use Promise.race,
|
||||
// because hidePopup wants to dispatch the "popuphidden" event synchronously
|
||||
// even if the panel has not been opened yet.
|
||||
let canCancel = true;
|
||||
let cancelCallback = this._openPopupCancelCallback = () => {
|
||||
// If the cancel callback is called and the panel hasn't been prepared
|
||||
// yet, cancel showing it. Setting canCancel to false will prevent the
|
||||
// popup from opening. If the panel has opened by the time the cancel
|
||||
// callback is called, canCancel will be false already, and we will not
|
||||
// fire the "popuphidden" event.
|
||||
if (canCancel && this.node) {
|
||||
canCancel = false;
|
||||
this.dispatchCustomEvent("popuphidden");
|
||||
}
|
||||
};
|
||||
|
||||
// Create a promise that is resolved with the result of the last call to
|
||||
// this method, where errors indicate that the panel was not opened.
|
||||
let openPopupPromise = this._openPopupPromise.catch(() => {
|
||||
return false;
|
||||
});
|
||||
|
||||
// Make the preparation done before showing the panel non-reentrant. The
|
||||
// promise created here will be resolved only after the panel preparation is
|
||||
// completed, even if a cancellation request is received in the meantime.
|
||||
return this._openPopupPromise = openPopupPromise.then(async wasShown => {
|
||||
// The panel may have been destroyed in the meantime.
|
||||
if (!this.node) {
|
||||
return false;
|
||||
}
|
||||
// If the panel has been already opened there is nothing more to do. We
|
||||
// check the actual state of the panel rather than setting some state in
|
||||
// our handler of the "popuphidden" event because this has a lower chance
|
||||
// of locking indefinitely if events aren't raised in the expected order.
|
||||
if (wasShown && ["open", "showing"].includes(this._panel.state)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
// Most of the panel elements in the browser window have their display
|
||||
// turned off for performance reasons, typically by setting the "hidden"
|
||||
// attribute. If the caller has just turned on the display, the XBL
|
||||
// binding for the <panelmultiview> element may still be disconnected.
|
||||
// In this case, give the layout code a chance to run.
|
||||
if (!this.connected) {
|
||||
await BrowserUtils.promiseLayoutFlushed(this.document, "layout",
|
||||
() => {});
|
||||
// The XBL binding must be connected at this point. If this is not the
|
||||
// case, the calling code should be updated to unhide the panel.
|
||||
if (!this.connected) {
|
||||
throw new Error("The binding for the panelmultiview element isn't" +
|
||||
" connected. The containing panel may still have" +
|
||||
" its display turned off by the hidden attribute.");
|
||||
}
|
||||
}
|
||||
// Allow any of the ViewShowing handlers to prevent showing the main view.
|
||||
if (!(await this.showMainView())) {
|
||||
cancelCallback();
|
||||
}
|
||||
} catch (ex) {
|
||||
cancelCallback();
|
||||
throw ex;
|
||||
}
|
||||
// If a cancellation request was received there is nothing more to do.
|
||||
if (!canCancel || !this.node) {
|
||||
return false;
|
||||
}
|
||||
// We have to set canCancel to false before opening the popup because the
|
||||
// hidePopup method of PanelMultiView can be re-entered by event handlers.
|
||||
// If the openPopup call fails, however, we still have to dispatch the
|
||||
// "popuphidden" event even if canCancel was set to false.
|
||||
try {
|
||||
canCancel = false;
|
||||
this._panel.openPopup(...args);
|
||||
return true;
|
||||
} catch (ex) {
|
||||
this.dispatchCustomEvent("popuphidden");
|
||||
throw ex;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the panel associated with this PanelMultiView.
|
||||
*
|
||||
* If the openPopup method was called but the panel has not been displayed
|
||||
* yet, the operation is canceled and the panel will not be displayed, but the
|
||||
* "popuphidden" event is fired synchronously anyways.
|
||||
*
|
||||
* This means that by the time this method returns all the operations handled
|
||||
* by the "popuphidden" event are completed, for example resetting the "open"
|
||||
* state of the anchor, and the panel is already invisible.
|
||||
*/
|
||||
hidePopup() {
|
||||
if (!this.node) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have already reached the _panel.openPopup call in the openPopup
|
||||
// method, we can call hidePopup. Otherwise, we have to cancel the latest
|
||||
// request to open the panel, which will have no effect if the request has
|
||||
// been canceled already.
|
||||
if (["open", "showing"].includes(this._panel.state)) {
|
||||
this._panel.hidePopup();
|
||||
} else {
|
||||
this._openPopupCancelCallback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any child subviews into the panelViewCache, to ensure
|
||||
* they remain usable even if this panelmultiview instance is removed
|
||||
|
@ -487,7 +304,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
showMainView() {
|
||||
if (!this.node || !this._mainViewId)
|
||||
return Promise.resolve(false);
|
||||
return Promise.resolve();
|
||||
|
||||
return this.showSubView(this._mainView);
|
||||
}
|
||||
|
@ -519,8 +336,8 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
this.showingSubView = nextPanelView.node.id != this._mainViewId;
|
||||
}
|
||||
|
||||
async showSubView(aViewId, aAnchor, aPreviousView) {
|
||||
try {
|
||||
showSubView(aViewId, aAnchor, aPreviousView) {
|
||||
this._currentShowPromise = (async () => {
|
||||
// Support passing in the node directly.
|
||||
let viewNode = typeof aViewId == "string" ? this.node.querySelector("#" + aViewId) : aViewId;
|
||||
if (!viewNode) {
|
||||
|
@ -591,7 +408,7 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
|
||||
if (cancel) {
|
||||
this._viewShowing = null;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,12 +421,8 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
} else {
|
||||
this.hideAllViewsExcept(nextPanelView);
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
return false;
|
||||
}
|
||||
})().catch(e => Cu.reportError(e));
|
||||
return this._currentShowPromise;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -932,14 +745,10 @@ this.PanelMultiView = class extends this.AssociatedToNode {
|
|||
case "popuphidden": {
|
||||
// WebExtensions consumers can hide the popup from viewshowing, or
|
||||
// mid-transition, which disrupts our state:
|
||||
if (this._viewShowing) {
|
||||
PanelView.forNode(this._viewShowing).dispatchCustomEvent("ViewHiding");
|
||||
}
|
||||
this._viewShowing = null;
|
||||
this._transitioning = false;
|
||||
this.node.removeAttribute("panelopen");
|
||||
// Raise the ViewHiding event for the current view.
|
||||
this.hideAllViewsExcept(null);
|
||||
this.showMainView();
|
||||
this.window.removeEventListener("keydown", this);
|
||||
this._panel.removeEventListener("mousemove", this);
|
||||
this.openViews.forEach(panelView => panelView.clearNavigation());
|
||||
|
|
|
@ -6,8 +6,6 @@ ChromeUtils.defineModuleGetter(this, "AppMenuNotifications",
|
|||
"resource://gre/modules/AppMenuNotifications.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "NewTabUtils",
|
||||
"resource://gre/modules/NewTabUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PanelMultiView",
|
||||
"resource:///modules/PanelMultiView.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ScrollbarSampler",
|
||||
"resource:///modules/ScrollbarSampler.jsm");
|
||||
|
||||
|
@ -217,9 +215,7 @@ const PanelUI = {
|
|||
}, {once: true});
|
||||
|
||||
anchor = this._getPanelAnchor(anchor);
|
||||
PanelMultiView.openPopup(this.panel, anchor, {
|
||||
triggerEvent: domEvent,
|
||||
}).catch(Cu.reportError);
|
||||
this.panel.openPopup(anchor, { triggerEvent: domEvent });
|
||||
}, (reason) => {
|
||||
console.error("Error showing the PanelUI menu", reason);
|
||||
});
|
||||
|
@ -234,7 +230,7 @@ const PanelUI = {
|
|||
return;
|
||||
}
|
||||
|
||||
PanelMultiView.hidePopup(this.panel);
|
||||
this.panel.hidePopup();
|
||||
},
|
||||
|
||||
observe(subject, topic, status) {
|
||||
|
@ -422,6 +418,10 @@ const PanelUI = {
|
|||
await oldMultiView.showMainView();
|
||||
}
|
||||
|
||||
let viewShown = false;
|
||||
let listener = () => viewShown = true;
|
||||
viewNode.addEventListener("ViewShown", listener, {once: true});
|
||||
|
||||
let multiView = document.createElement("panelmultiview");
|
||||
multiView.setAttribute("id", "customizationui-widget-multiview");
|
||||
multiView.setAttribute("viewCacheId", "appMenu-viewCache");
|
||||
|
@ -431,7 +431,6 @@ const PanelUI = {
|
|||
tempPanel.appendChild(multiView);
|
||||
viewNode.classList.add("cui-widget-panelview");
|
||||
|
||||
let viewShown = false;
|
||||
let panelRemover = () => {
|
||||
viewNode.classList.remove("cui-widget-panelview");
|
||||
if (viewShown) {
|
||||
|
@ -446,6 +445,18 @@ const PanelUI = {
|
|||
tempPanel.remove();
|
||||
};
|
||||
|
||||
// Wait until all the tasks needed to show a view are done.
|
||||
await multiView.currentShowPromise;
|
||||
|
||||
if (!viewShown) {
|
||||
viewNode.removeEventListener("ViewShown", listener);
|
||||
panelRemover();
|
||||
return;
|
||||
}
|
||||
|
||||
CustomizableUI.addPanelCloseListeners(tempPanel);
|
||||
tempPanel.addEventListener("popuphidden", panelRemover);
|
||||
|
||||
if (aAnchor.parentNode.id == "PersonalToolbar") {
|
||||
tempPanel.classList.add("bookmarks-toolbar");
|
||||
}
|
||||
|
@ -456,21 +467,10 @@ const PanelUI = {
|
|||
anchor.setAttribute("consumeanchor", aAnchor.id);
|
||||
}
|
||||
|
||||
try {
|
||||
viewShown = await PanelMultiView.openPopup(tempPanel, anchor, {
|
||||
position: "bottomcenter topright",
|
||||
triggerEvent: domEvent,
|
||||
});
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
|
||||
if (viewShown) {
|
||||
CustomizableUI.addPanelCloseListeners(tempPanel);
|
||||
tempPanel.addEventListener("popuphidden", panelRemover);
|
||||
} else {
|
||||
panelRemover();
|
||||
}
|
||||
tempPanel.openPopup(anchor, {
|
||||
position: "bottomcenter topright",
|
||||
triggerEvent: domEvent,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -620,7 +620,9 @@ const PanelUI = {
|
|||
this.navbar.setAttribute("nonemptyoverflow", "true");
|
||||
this.overflowPanel.setAttribute("hasfixeditems", "true");
|
||||
} else if (!hasKids && this.navbar.hasAttribute("nonemptyoverflow")) {
|
||||
PanelMultiView.hidePopup(this.overflowPanel);
|
||||
if (this.overflowPanel.state != "closed") {
|
||||
this.overflowPanel.hidePopup();
|
||||
}
|
||||
this.overflowPanel.removeAttribute("hasfixeditems");
|
||||
this.navbar.removeAttribute("nonemptyoverflow");
|
||||
}
|
||||
|
|
|
@ -250,7 +250,7 @@ var DownloadsPanel = {
|
|||
return;
|
||||
}
|
||||
|
||||
PanelMultiView.hidePopup(this.panel);
|
||||
this.panel.hidePopup();
|
||||
|
||||
// Ensure that we allow the panel to be reopened. Note that, if the popup
|
||||
// was open, then the onPopupHidden event handler has already updated the
|
||||
|
@ -570,8 +570,7 @@ var DownloadsPanel = {
|
|||
}
|
||||
|
||||
DownloadsCommon.log("Opening downloads panel popup.");
|
||||
PanelMultiView.openPopup(this.panel, anchor, "bottomcenter topright",
|
||||
0, 0, false, null).catch(Cu.reportError);
|
||||
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, null);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -431,7 +431,7 @@ add_task(async function withSubview() {
|
|||
Assert.notEqual(panelViewButtonNodeUrlbar, null, "panelViewButtonNodeUrlbar");
|
||||
onButtonCommandExpectedButtonID = panelViewButtonIDUrlbar;
|
||||
EventUtils.synthesizeMouseAtCenter(panelViewButtonNodeUrlbar, {});
|
||||
assertActivatedPageActionPanelHidden();
|
||||
await promisePanelHidden(BrowserPageActions._activatedActionPanelID);
|
||||
Assert.equal(onButtonCommandCallCount, 2,
|
||||
"onButtonCommandCallCount should be inc'ed");
|
||||
|
||||
|
@ -532,7 +532,7 @@ add_task(async function withIframe() {
|
|||
Assert.notEqual(aaPanel, null, "activated-action panel");
|
||||
Assert.equal(aaPanel.anchorNode.id, urlbarButtonID, "aaPanel.anchorNode.id");
|
||||
EventUtils.synthesizeMouseAtCenter(urlbarButtonNode, {});
|
||||
assertActivatedPageActionPanelHidden();
|
||||
await promisePanelHidden(BrowserPageActions._activatedActionPanelID);
|
||||
|
||||
// Click the action's urlbar button.
|
||||
EventUtils.synthesizeMouseAtCenter(urlbarButtonNode, {});
|
||||
|
@ -546,7 +546,7 @@ add_task(async function withIframe() {
|
|||
Assert.notEqual(aaPanel, null, "aaPanel");
|
||||
Assert.equal(aaPanel.anchorNode.id, urlbarButtonID, "aaPanel.anchorNode.id");
|
||||
EventUtils.synthesizeMouseAtCenter(urlbarButtonNode, {});
|
||||
assertActivatedPageActionPanelHidden();
|
||||
await promisePanelHidden(BrowserPageActions._activatedActionPanelID);
|
||||
|
||||
// Hide the action's button in the urlbar.
|
||||
action.pinnedToUrlbar = false;
|
||||
|
@ -567,7 +567,7 @@ add_task(async function withIframe() {
|
|||
Assert.equal(aaPanel.anchorNode.id, BrowserPageActions.mainButtonNode.id,
|
||||
"aaPanel.anchorNode.id");
|
||||
EventUtils.synthesizeMouseAtCenter(BrowserPageActions.mainButtonNode, {});
|
||||
assertActivatedPageActionPanelHidden();
|
||||
await promisePanelHidden(BrowserPageActions._activatedActionPanelID);
|
||||
|
||||
// Remove the action.
|
||||
action.remove();
|
||||
|
@ -1416,10 +1416,6 @@ add_task(async function contextMenu() {
|
|||
});
|
||||
|
||||
|
||||
function assertActivatedPageActionPanelHidden() {
|
||||
Assert.ok(!document.getElementById(BrowserPageActions._activatedActionPanelID));
|
||||
}
|
||||
|
||||
function promisePageActionPanelOpen() {
|
||||
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
@ -1463,14 +1459,10 @@ function promisePanelHidden(panelIDOrNode) {
|
|||
|
||||
function promisePanelEvent(panelIDOrNode, eventType) {
|
||||
return new Promise(resolve => {
|
||||
let panel = panelIDOrNode;
|
||||
if (typeof panel == "string") {
|
||||
panel = document.getElementById(panelIDOrNode);
|
||||
if (!panel) {
|
||||
throw new Error(`Panel with ID "${panelIDOrNode}" does not exist.`);
|
||||
}
|
||||
}
|
||||
if ((eventType == "popupshown" && panel.state == "open") ||
|
||||
let panel = typeof(panelIDOrNode) != "string" ? panelIDOrNode :
|
||||
document.getElementById(panelIDOrNode);
|
||||
if (!panel ||
|
||||
(eventType == "popupshown" && panel.state == "open") ||
|
||||
(eventType == "popuphidden" && panel.state == "closed")) {
|
||||
executeSoon(resolve);
|
||||
return;
|
||||
|
|
Загрузка…
Ссылка в новой задаче