Bumping manifests a=b2g-bump
[gecko.git] / browser / modules / PanelFrame.jsm
bloba116c63f4a9818cac1867cb50b092839e9f83382
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 "use strict";
6 // A module for working with panels with iframes shared across windows.
8 this.EXPORTED_SYMBOLS = ["PanelFrame"];
10 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
12 Cu.import("resource://gre/modules/Services.jsm");
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
15 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
17 XPCOMUtils.defineLazyModuleGetter(this, "DynamicResizeWatcher", "resource:///modules/Social.jsm");
19 // The minimum sizes for the auto-resize panel code.
20 const PANEL_MIN_HEIGHT = 100;
21 const PANEL_MIN_WIDTH = 330;
23 let PanelFrameInternal = {
24   /**
25    * Helper function to get and hold a single instance of a DynamicResizeWatcher.
26    */
27   get _dynamicResizer() {
28     delete this._dynamicResizer;
29     this._dynamicResizer = new DynamicResizeWatcher();
30     return this._dynamicResizer;
31   },
33   /**
34    * Status panels are one-per button per-process, we swap the docshells between
35    * windows when necessary.
36    *
37    * @param {DOMWindow} aWindow The window in which to show the popup.
38    * @param {PanelUI} aPanelUI The panel UI object that represents the application menu.
39    * @param {DOMElement} aButton The button element that is pressed to show the popup.
40    * @param {String} aType The type of panel this is, e.g. "social" or "loop".
41    * @param {String} aOrigin Optional, the origin to use for the iframe.
42    * @param {String} aSrc The url to load into the iframe.
43    * @param {String} aSize The initial size of the panel (width and height are the same
44    *                       if specified).
45    */
46   _attachNotificatonPanel: function(aWindow, aParent, aButton, aType, aOrigin, aSrc, aSize) {
47     aParent.hidden = false;
48     let notificationFrameId = aOrigin ? aType + "-status-" + aOrigin : aType;
49     let doc = aWindow.document;
50     let frame = doc.getElementById(notificationFrameId);
52     // If the button was customized to a new location, destroy the
53     // iframe and start fresh.
54     if (frame && frame.parentNode != aParent) {
55       frame.parentNode.removeChild(frame);
56       frame = null;
57     }
59     if (!frame) {
60       let {width, height} = aSize ? aSize : {width: PANEL_MIN_WIDTH, height: PANEL_MIN_HEIGHT};
61       frame = doc.createElement("browser");
62       let attrs = {
63         "type": "content",
64         "mozbrowser": "true",
65         // All frames use social-panel-frame as the class.
66         "class": "social-panel-frame",
67         "id": notificationFrameId,
68         "tooltip": "aHTMLTooltip",
69         "context": "contentAreaContextMenu",
70         "flex": "1",
72         // work around bug 793057 - by making the panel roughly the final size
73         // we are more likely to have the anchor in the correct position.
74         "style": "width: " + width + "px; height: " + height + "px;",
75         "dynamicresizer": !aSize,
77         "origin": aOrigin,
78         "src": aSrc
79       };
80       for (let [k, v] of Iterator(attrs)) {
81         frame.setAttribute(k, v);
82       }
83       aParent.appendChild(frame);
84     } else {
85       frame.setAttribute("origin", aOrigin);
86       frame.setAttribute("src", aSrc);
87     }
88     aButton.setAttribute("notificationFrameId", notificationFrameId);
89   }
92 /**
93  * The exported PanelFrame object
94  */
95 let PanelFrame = {
96   /**
97    * Shows a popup in a pop-up panel, or in a sliding panel view in the application menu.
98    * It will move the iframe to different DOM locations depending on where it needs to be
99    * shown, enabling one iframe to be used for the entire session.
100    *
101    * @param {DOMWindow} aWindow The window in which to show the popup.
102    * @param {PanelUI} aPanelUI The panel UI object that represents the application menu.
103    * @param {DOMElement} aToolbarButton The button element that is pressed to show the popup.
104    * @param {String} aType The type of panel this is, e.g. "social" or "loop".
105    * @param {String} aOrigin Optional, the origin to use for the iframe.
106    * @param {String} aSrc The url to load into the iframe.
107    * @param {String} aSize The initial size of the panel (width and height are the same
108    *                       if specified).
109    * @param {Function} aCallback Optional, callback to be called with the iframe when it is
110    *                             set up.
111    */
112   showPopup: function(aWindow, aToolbarButton, aType, aOrigin, aSrc, aSize, aCallback) {
113     // if we're overflowed, our anchor needs to be the overflow button
114     let widgetGroup = CustomizableUI.getWidget(aToolbarButton.getAttribute("id"));
115     let widget = widgetGroup.forWindow(aWindow);
116     // if we're a slice in the hamburger, our anchor will be the menu button,
117     // this panel will replace the menu panel when the button is clicked on
118     let anchorBtn = widget.anchor;
120     let panel = aWindow.document.getElementById(aType + "-notification-panel");
121     PanelFrameInternal._attachNotificatonPanel(aWindow, panel, aToolbarButton, aType, aOrigin, aSrc, aSize);
123     let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
124     let notificationFrame = aWindow.document.getElementById(notificationFrameId);
127     // Clear dimensions on all browsers so the panel size will
128     // only use the selected browser.
129     let frameIter = panel.firstElementChild;
130     while (frameIter) {
131       frameIter.collapsed = (frameIter != notificationFrame);
132       frameIter = frameIter.nextElementSibling;
133     }
135     function dispatchPanelEvent(name) {
136       let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
137       evt.initCustomEvent(name, true, true, {});
138       notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
139     }
141     // we only use a dynamic resizer when we're located the toolbar.
142     let dynamicResizer;
143     if (notificationFrame.getAttribute("dynamicresizer") == "true") {
144       dynamicResizer = PanelFrameInternal._dynamicResizer;
145     }
146     panel.addEventListener("popuphidden", function onpopuphiding() {
147       panel.removeEventListener("popuphidden", onpopuphiding);
148       anchorBtn.removeAttribute("open");
149       if (dynamicResizer)
150         dynamicResizer.stop();
151       notificationFrame.docShell.isActive = false;
152       dispatchPanelEvent(aType + "FrameHide");
153     });
155     panel.addEventListener("popupshown", function onpopupshown() {
156       panel.removeEventListener("popupshown", onpopupshown);
157       let initFrameShow = () => {
158         notificationFrame.docShell.isActive = true;
159         notificationFrame.docShell.isAppTab = true;
160         if (dynamicResizer)
161           dynamicResizer.start(panel, notificationFrame);
162         dispatchPanelEvent(aType + "FrameShow");
163       };
164       // This attribute is needed on both the button and the
165       // containing toolbaritem since the buttons on OS X have
166       // moz-appearance:none, while their container gets
167       // moz-appearance:toolbarbutton due to the way that toolbar buttons
168       // get combined on OS X.
169       anchorBtn.setAttribute("open", "true");
170       if (notificationFrame.contentDocument &&
171           notificationFrame.contentDocument.readyState == "complete") {
172         initFrameShow();
173       } else {
174         // first time load, wait for load and dispatch after load
175         notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
176           notificationFrame.removeEventListener("load", panelBrowserOnload, true);
177           initFrameShow();
178         }, true);
179       }
180     });
182     // in overflow, the anchor is a normal toolbarbutton, in toolbar it is a badge button
183     let anchor = aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-badge-container") ||
184                  aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-icon");
185     // Bug 849216 - open the popup asynchronously so we avoid the auto-rollup
186     // handling from preventing it being opened in some cases.
187     Services.tm.mainThread.dispatch(function() {
188       panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
189     }, Ci.nsIThread.DISPATCH_NORMAL);
191     if (aCallback)
192       aCallback(notificationFrame);
193   }