mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 07:05:24 +00:00
215 lines
9.0 KiB
JavaScript
215 lines
9.0 KiB
JavaScript
/* 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/. */
|
|
"use strict";
|
|
|
|
// A module for working with panels with iframes shared across windows.
|
|
|
|
this.EXPORTED_SYMBOLS = ["PanelFrame"];
|
|
|
|
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame", "resource:///modules/SharedFrame.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "DynamicResizeWatcher", "resource:///modules/Social.jsm");
|
|
|
|
// The minimum sizes for the auto-resize panel code.
|
|
const PANEL_MIN_HEIGHT = 100;
|
|
const PANEL_MIN_WIDTH = 330;
|
|
|
|
let PanelFrameInternal = {
|
|
/**
|
|
* Helper function to get and hold a single instance of a DynamicResizeWatcher.
|
|
*/
|
|
get _dynamicResizer() {
|
|
delete this._dynamicResizer;
|
|
this._dynamicResizer = new DynamicResizeWatcher();
|
|
return this._dynamicResizer;
|
|
},
|
|
|
|
/**
|
|
* Status panels are one-per button per-process, we swap the docshells between
|
|
* windows when necessary.
|
|
*
|
|
* @param {DOMWindow} aWindow The window in which to show the popup.
|
|
* @param {PanelUI} aPanelUI The panel UI object that represents the application menu.
|
|
* @param {DOMElement} aButton The button element that is pressed to show the popup.
|
|
* @param {String} aType The type of panel this is, e.g. "social" or "loop".
|
|
* @param {String} aOrigin Optional, the origin to use for the iframe.
|
|
* @param {String} aSrc The url to load into the iframe.
|
|
* @param {String} aSize The initial size of the panel (width and height are the same
|
|
* if specified).
|
|
*/
|
|
_attachNotificatonPanel: function(aWindow, aParent, aButton, aType, aOrigin, aSrc, aSize) {
|
|
aParent.hidden = false;
|
|
let notificationFrameId = aOrigin ? aType + "-status-" + aOrigin : aType;
|
|
let frame = aWindow.document.getElementById(notificationFrameId);
|
|
|
|
// If the button was customized to a new location, we we'll destroy the
|
|
// iframe and start fresh.
|
|
if (frame && frame.parentNode != aParent) {
|
|
SharedFrame.forgetGroup(frame.id);
|
|
frame.parentNode.removeChild(frame);
|
|
frame = null;
|
|
}
|
|
|
|
if (!frame) {
|
|
let {width, height} = aSize ? aSize : {width: PANEL_MIN_WIDTH, height: PANEL_MIN_HEIGHT};
|
|
|
|
frame = SharedFrame.createFrame(
|
|
notificationFrameId, /* frame name */
|
|
aParent, /* parent */
|
|
{
|
|
"type": "content",
|
|
"mozbrowser": "true",
|
|
// All frames use social-panel-frame as the class.
|
|
"class": "social-panel-frame",
|
|
"id": notificationFrameId,
|
|
"tooltip": "aHTMLTooltip",
|
|
"context": "contentAreaContextMenu",
|
|
"flex": "1",
|
|
|
|
// work around bug 793057 - by making the panel roughly the final size
|
|
// we are more likely to have the anchor in the correct position.
|
|
"style": "width: " + width + "px; height: " + height + "px;",
|
|
"dynamicresizer": !aSize,
|
|
|
|
"origin": aOrigin,
|
|
"src": aSrc
|
|
}
|
|
);
|
|
} else {
|
|
frame.setAttribute("origin", aOrigin);
|
|
SharedFrame.updateURL(notificationFrameId, aSrc);
|
|
}
|
|
aButton.setAttribute("notificationFrameId", notificationFrameId);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* The exported PanelFrame object
|
|
*/
|
|
let PanelFrame = {
|
|
/**
|
|
* Shows a popup in a pop-up panel, or in a sliding panel view in the application menu.
|
|
* It will move the iframe to different DOM locations depending on where it needs to be
|
|
* shown, enabling one iframe to be used for the entire session.
|
|
*
|
|
* @param {DOMWindow} aWindow The window in which to show the popup.
|
|
* @param {PanelUI} aPanelUI The panel UI object that represents the application menu.
|
|
* @param {DOMElement} aToolbarButton The button element that is pressed to show the popup.
|
|
* @param {String} aType The type of panel this is, e.g. "social" or "loop".
|
|
* @param {String} aOrigin Optional, the origin to use for the iframe.
|
|
* @param {String} aSrc The url to load into the iframe.
|
|
* @param {String} aSize The initial size of the panel (width and height are the same
|
|
* if specified).
|
|
* @param {Function} aCallback Optional, callback to be called with the iframe when it is
|
|
* set up.
|
|
*/
|
|
showPopup: function(aWindow, aPanelUI, aToolbarButton, aType, aOrigin, aSrc, aSize, aCallback) {
|
|
// if we're overflowed, our anchor needs to be the overflow button
|
|
let widgetGroup = CustomizableUI.getWidget(aToolbarButton.getAttribute("id"));
|
|
let widget = widgetGroup.forWindow(aWindow);
|
|
let anchorBtn = widget.anchor;
|
|
|
|
// if we're a slice in the hamburger, use that panel instead
|
|
let panel, showingEvent, hidingEvent;
|
|
let inMenuPanel = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
|
|
if (inMenuPanel) {
|
|
panel = aWindow.document.getElementById("PanelUI-" + aType + "api");
|
|
PanelFrameInternal._attachNotificatonPanel(aWindow, panel, aToolbarButton, aType, aOrigin, aSrc, aSize);
|
|
widget.node.setAttribute("closemenu", "none");
|
|
showingEvent = "ViewShowing";
|
|
hidingEvent = "ViewHiding";
|
|
} else {
|
|
panel = aWindow.document.getElementById(aType + "-notification-panel");
|
|
PanelFrameInternal._attachNotificatonPanel(aWindow, panel, aToolbarButton, aType, aOrigin, aSrc, aSize);
|
|
showingEvent = "popupshown";
|
|
hidingEvent = "popuphidden";
|
|
}
|
|
let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
|
|
let notificationFrame = aWindow.document.getElementById(notificationFrameId);
|
|
|
|
SharedFrame.setOwner(notificationFrameId, notificationFrame);
|
|
|
|
// Clear dimensions on all browsers so the panel size will
|
|
// only use the selected browser.
|
|
let frameIter = panel.firstElementChild;
|
|
while (frameIter) {
|
|
frameIter.collapsed = (frameIter != notificationFrame);
|
|
frameIter = frameIter.nextElementSibling;
|
|
}
|
|
|
|
function dispatchPanelEvent(name) {
|
|
let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
|
|
evt.initCustomEvent(name, true, true, {});
|
|
notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
|
|
}
|
|
|
|
// we only use a dynamic resizer when we're located the toolbar.
|
|
let dynamicResizer;
|
|
if (!inMenuPanel && notificationFrame.getAttribute("dynamicresizer") == "true") {
|
|
dynamicResizer = PanelFrameInternal._dynamicResizer;
|
|
}
|
|
panel.addEventListener(hidingEvent, function onpopuphiding() {
|
|
panel.removeEventListener(hidingEvent, onpopuphiding);
|
|
if (!inMenuPanel)
|
|
anchorBtn.removeAttribute("open");
|
|
if (dynamicResizer)
|
|
dynamicResizer.stop();
|
|
notificationFrame.docShell.isActive = false;
|
|
dispatchPanelEvent(aType + "FrameHide");
|
|
});
|
|
|
|
panel.addEventListener(showingEvent, function onpopupshown() {
|
|
panel.removeEventListener(showingEvent, onpopupshown);
|
|
// This attribute is needed on both the button and the
|
|
// containing toolbaritem since the buttons on OS X have
|
|
// moz-appearance:none, while their container gets
|
|
// moz-appearance:toolbarbutton due to the way that toolbar buttons
|
|
// get combined on OS X.
|
|
let initFrameShow = () => {
|
|
notificationFrame.docShell.isActive = true;
|
|
notificationFrame.docShell.isAppTab = true;
|
|
if (dynamicResizer)
|
|
dynamicResizer.start(panel, notificationFrame);
|
|
dispatchPanelEvent(aType + "FrameShow");
|
|
};
|
|
if (!inMenuPanel)
|
|
anchorBtn.setAttribute("open", "true");
|
|
if (notificationFrame.contentDocument &&
|
|
notificationFrame.contentDocument.readyState == "complete") {
|
|
initFrameShow();
|
|
} else {
|
|
// first time load, wait for load and dispatch after load
|
|
notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
|
|
notificationFrame.removeEventListener("load", panelBrowserOnload, true);
|
|
initFrameShow();
|
|
}, true);
|
|
}
|
|
});
|
|
|
|
if (inMenuPanel) {
|
|
aPanelUI.showSubView("PanelUI-" + aType + "api", widget.node,
|
|
CustomizableUI.AREA_PANEL);
|
|
} else {
|
|
// in overflow, the anchor is a normal toolbarbutton, in toolbar it is a badge button
|
|
let anchor = aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-badge-container") ||
|
|
aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-icon");
|
|
// Bug 849216 - open the popup asynchronously so we avoid the auto-rollup
|
|
// handling from preventing it being opened in some cases.
|
|
Services.tm.mainThread.dispatch(function() {
|
|
panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
|
|
}, Ci.nsIThread.DISPATCH_NORMAL);
|
|
}
|
|
|
|
if (aCallback)
|
|
aCallback(notificationFrame);
|
|
}
|
|
};
|