author Margaret Leibovic <>
Mon, 05 Jan 2015 16:29:08 -0800
changeset 235072 e9ef000ac1a6db6e97015564663189bf70baa108
parent 233874 df07507d7e78211ff122048f6761be4fd304d818
child 260446 61e6cb9215a43c081fbc00376b7d6ffe9ffb6315
permissions -rw-r--r--
Bug 1117841 - Remove "title bar" from display settings summary. r=mfinkle

/* 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 */
"use strict";

// A module for working with chat windows.

this.EXPORTED_SYMBOLS = ["Chat"];

const { classes: Cc, interfaces: Ci, utils: Cu } = Components;


XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",

// A couple of internal helper function.
function isWindowChromeless(win) {
  // XXX - stolen from browser-social.js, but there's no obvious place to
  // put this so it can be shared.

  // Is this a popup window that doesn't want chrome shown?
  let docElem = win.document.documentElement;
  // extrachrome is not restored during session restore, so we need
  // to check for the toolbar as well.
  let chromeless = docElem.getAttribute("chromehidden").contains("extrachrome") ||
  return chromeless;

function isWindowGoodForChats(win) {
  return !win.closed &&
         !!win.document.getElementById("pinnedchats") &&
         !isWindowChromeless(win) &&

function getChromeWindow(contentWin) {
  return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)

 * The exported Chat object

let Chat = {

   * Iterator of <chatbox> elements from this module in all windows.
  get chatboxes() {
    return function*() {
      let winEnum = Services.wm.getEnumerator("navigator:browser");
      while (winEnum.hasMoreElements()) {
        let win = winEnum.getNext();
        let chatbar = win.document.getElementById("pinnedchats");
        if (!chatbar)

        // Make a new array instead of the live NodeList so this iterator can be
        // used for closing/deleting.
        let chatboxes = [c for (c of chatbar.children)];
        for (let chatbox of chatboxes) {
          yield chatbox;

      // include standalone chat windows
      winEnum = Services.wm.getEnumerator("Social:Chat");
      while (winEnum.hasMoreElements()) {
        let win = winEnum.getNext();
        if (win.closed)
        yield win.document.getElementById("chatter");

   * Open a new chatbox.
   * @param contentWindow [optional]
   *        The content window that requested this chat.  May be null.
   * @param origin
   *        The origin for the chat.  This is primarily used as an identifier
   *        to help identify all chats from the same provider.
   * @param title
   *        The title to be used if a new chat window is created.
   * @param url
   *        The URL for the that.  Should be under the origin.  If an existing
   *        chatbox exists with the same URL, it will be reused and returned.
   * @param mode [optional]
   *        May be undefined or 'minimized'
   * @param focus [optional]
   *        Indicates if the chatbox should be focused.  If undefined the chat
   *        will be focused if the window is currently handling user input (ie,
   *        if the chat is being opened as a direct result of user input)

   * @return A chatbox binding.  This binding has a number of promises which
   *         can be used to determine when the chatbox is being created and
   *         has loaded.  Will return null if no chat can be created (Which
   *         should only happen in edge-cases)
  open: function(contentWindow, origin, title, url, mode, focus, callback) {
    let chromeWindow = this.findChromeWindowForChats(contentWindow);
    if (!chromeWindow) {
      Cu.reportError("Failed to open a chat window - no host window could be found.");
      return null;

    let chatbar = chromeWindow.document.getElementById("pinnedchats");
    chatbar.hidden = false;
    let chatbox = chatbar.openChat(origin, title, url, mode, callback);
    // getAttention is ignored if the target window is already foreground, so
    // we can call it unconditionally.
    // If focus is undefined we want automatic focus handling, and only focus
    // if a direct result of user action.
    if (focus === undefined) {
      let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
      focus = dwu.isHandlingUserInput;
    if (focus) {
    return chatbox;

   * Close all chats from the specified origin.
   * @param origin
   *        The origin from which all chats should be closed.
  closeAll: function(origin) {
    for (let chatbox of this.chatboxes) {
      if (chatbox.content.getAttribute("origin") != origin) {

   * Focus the chatbar associated with a window
   * @param window
  focus: function(win) {
    let chatbar = win.document.getElementById("pinnedchats");
    if (chatbar && !chatbar.hidden) {


  // This is exported as socialchat.xml needs to find a window when a chat
  // is re-docked.
  findChromeWindowForChats: function(preferredWindow) {
    if (preferredWindow) {
      preferredWindow = getChromeWindow(preferredWindow);
      if (isWindowGoodForChats(preferredWindow)) {
        return preferredWindow;
    // no good - we just use the "most recent" browser window which can host
    // chats (we used to try and "group" all chats in the same browser window,
    // but that didn't work out so well - see bug 835111

    // Try first the most recent window as getMostRecentWindow works
    // even on platforms where getZOrderDOMWindowEnumerator is broken
    // (ie. Linux).  This will handle most cases, but won't work if the
    // foreground window is a popup.

    let mostRecent = Services.wm.getMostRecentWindow("navigator:browser");
    if (isWindowGoodForChats(mostRecent))
      return mostRecent;

    let topMost, enumerator;
    // *sigh* - getZOrderDOMWindowEnumerator is broken except on Mac and
    // Windows.  We use BROKEN_WM_Z_ORDER as that is what some other code uses
    // and a few bugs recommend searching mxr for this symbol to identify the
    // workarounds - we want this code to be hit in such searches.
    let os = Services.appinfo.OS;
    const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
    if (BROKEN_WM_Z_ORDER) {
      // this is oldest to newest and no way to change the order.
      enumerator = Services.wm.getEnumerator("navigator:browser");
    } else {
      // here we explicitly ask for bottom-to-top so we can use the same logic
      // where BROKEN_WM_Z_ORDER is true.
      enumerator = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", false);
    while (enumerator.hasMoreElements()) {
      let win = enumerator.getNext();
      if (!win.closed && isWindowGoodForChats(win))
        topMost = win;
    return topMost;