DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (dcc6d7a0dc00)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
/* 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, "DynamicResizeWatcher", "resource:///modules/Social.jsm");

// The minimum sizes for the auto-resize panel code.
const PANEL_MIN_HEIGHT = 100;
const PANEL_MIN_WIDTH = 330;

var 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 + "-panel-iframe";
    let doc = aWindow.document;
    let frame = doc.getElementById(notificationFrameId);

    // If the button was customized to a new location, destroy the
    // iframe and start fresh.
    if (frame && frame.parentNode != aParent) {
      frame.parentNode.removeChild(frame);
      frame = null;
    }

    if (!frame) {
      let {width, height} = aSize ? aSize : {width: PANEL_MIN_WIDTH, height: PANEL_MIN_HEIGHT};
      frame = doc.createElement("browser");
      let attrs = {
        "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
      };
      if (aType == "social") {
        attrs["message"] = "true";
        attrs["messagemanagergroup"] = aType;
      }
      for (let [k, v] of Iterator(attrs)) {
        frame.setAttribute(k, v);
      }
      aParent.appendChild(frame);
    } else {
      frame.setAttribute("origin", aOrigin);
      frame.setAttribute("src", aSrc);
    }
    aButton.setAttribute("notificationFrameId", notificationFrameId);
  }
};

/**
 * The exported PanelFrame object
 */
var 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, 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);
    // if we're a slice in the hamburger, our anchor will be the menu button,
    // this panel will replace the menu panel when the button is clicked on
    let anchorBtn = widget.anchor;

    let panel = aWindow.document.getElementById(aType + "-notification-panel");
    PanelFrameInternal._attachNotificatonPanel(aWindow, panel, aToolbarButton, aType, aOrigin, aSrc, aSize);

    let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
    let notificationFrame = aWindow.document.getElementById(notificationFrameId);


    // 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 (notificationFrame.getAttribute("dynamicresizer") == "true") {
      dynamicResizer = PanelFrameInternal._dynamicResizer;
    }
    panel.addEventListener("popuphidden", function onpopuphiding() {
      panel.removeEventListener("popuphidden", onpopuphiding);
      anchorBtn.removeAttribute("open");
      if (dynamicResizer)
        dynamicResizer.stop();
      notificationFrame.docShell.isActive = false;
      dispatchPanelEvent(aType + "FrameHide");
    });

    panel.addEventListener("popupshown", function onpopupshown() {
      panel.removeEventListener("popupshown", onpopupshown);
      let initFrameShow = () => {
        notificationFrame.docShell.isActive = true;
        notificationFrame.docShell.isAppTab = true;
        if (dynamicResizer)
          dynamicResizer.start(panel, notificationFrame);
        dispatchPanelEvent(aType + "FrameShow");
      };
      // 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.
      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);
      }
    });

    let anchor = 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);
  }
};