Bumping manifests a=b2g-bump
[gecko.git] / browser / modules / Chat.jsm
blobebb06ac5e27b1ed126c0e64efa43653b78cc352b
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 chat windows.
8 this.EXPORTED_SYMBOLS = ["Chat"];
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, "PrivateBrowsingUtils",
16   "resource://gre/modules/PrivateBrowsingUtils.jsm");
18 // A couple of internal helper function.
19 function isWindowChromeless(win) {
20   // XXX - stolen from browser-social.js, but there's no obvious place to
21   // put this so it can be shared.
23   // Is this a popup window that doesn't want chrome shown?
24   let docElem = win.document.documentElement;
25   // extrachrome is not restored during session restore, so we need
26   // to check for the toolbar as well.
27   let chromeless = docElem.getAttribute("chromehidden").contains("extrachrome") ||
28                    docElem.getAttribute('chromehidden').contains("toolbar");
29   return chromeless;
32 function isWindowGoodForChats(win) {
33   return !win.closed &&
34          !!win.document.getElementById("pinnedchats") &&
35          !isWindowChromeless(win) &&
36          !PrivateBrowsingUtils.isWindowPrivate(win);
39 function getChromeWindow(contentWin) {
40   return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
41                    .getInterface(Ci.nsIWebNavigation)
42                    .QueryInterface(Ci.nsIDocShellTreeItem)
43                    .rootTreeItem
44                    .QueryInterface(Ci.nsIInterfaceRequestor)
45                    .getInterface(Ci.nsIDOMWindow);
49  * The exported Chat object
50  */
52 let Chat = {
53   /**
54    * Open a new chatbox.
55    *
56    * @param contentWindow [optional]
57    *        The content window that requested this chat.  May be null.
58    * @param origin
59    *        The origin for the chat.  This is primarily used as an identifier
60    *        to help identify all chats from the same provider.
61    * @param title
62    *        The title to be used if a new chat window is created.
63    * @param url
64    *        The URL for the that.  Should be under the origin.  If an existing
65    *        chatbox exists with the same URL, it will be reused and returned.
66    * @param mode [optional]
67    *        May be undefined or 'minimized'
68    * @param focus [optional]
69    *        Indicates if the chatbox should be focused.  If undefined the chat
70    *        will be focused if the window is currently handling user input (ie,
71    *        if the chat is being opened as a direct result of user input)
73    * @return A chatbox binding.  This binding has a number of promises which
74    *         can be used to determine when the chatbox is being created and
75    *         has loaded.  Will return null if no chat can be created (Which
76    *         should only happen in edge-cases)
77    */
78   open: function(contentWindow, origin, title, url, mode, focus, callback) {
79     let chromeWindow = this.findChromeWindowForChats(contentWindow);
80     if (!chromeWindow) {
81       Cu.reportError("Failed to open a chat window - no host window could be found.");
82       return null;
83     }
85     let chatbar = chromeWindow.document.getElementById("pinnedchats");
86     chatbar.hidden = false;
87     let chatbox = chatbar.openChat(origin, title, url, mode, callback);
88     // getAttention is ignored if the target window is already foreground, so
89     // we can call it unconditionally.
90     chromeWindow.getAttention();
91     // If focus is undefined we want automatic focus handling, and only focus
92     // if a direct result of user action.
93     if (focus === undefined) {
94       let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
95                             .getInterface(Ci.nsIDOMWindowUtils);
96       focus = dwu.isHandlingUserInput;
97     }
98     if (focus) {
99       chatbar.focus();
100     }
101     return chatbox;
102   },
104   /**
105    * Close all chats from the specified origin.
106    *
107    * @param origin
108    *        The origin from which all chats should be closed.
109    */
110   closeAll: function(origin) {
111     // close all attached chat windows
112     let winEnum = Services.wm.getEnumerator("navigator:browser");
113     while (winEnum.hasMoreElements()) {
114       let win = winEnum.getNext();
115       let chatbar = win.document.getElementById("pinnedchats");
116       if (!chatbar)
117         continue;
118       let chats = [c for (c of chatbar.children) if (c.content.getAttribute("origin") == origin)];
119       [c.close() for (c of chats)];
120     }
122     // close all standalone chat windows
123     winEnum = Services.wm.getEnumerator("Social:Chat");
124     while (winEnum.hasMoreElements()) {
125       let win = winEnum.getNext();
126       if (win.closed)
127         continue;
128       let chatOrigin = win.document.getElementById("chatter").content.getAttribute("origin");
129       if (origin == chatOrigin)
130         win.close();
131     }
132   },
134   /**
135    * Focus the chatbar associated with a window
136    *
137    * @param window
138    */
139   focus: function(win) {
140     let chatbar = win.document.getElementById("pinnedchats");
141     if (chatbar && !chatbar.hidden) {
142       chatbar.focus();
143     }
145   },
147   // This is exported as socialchat.xml needs to find a window when a chat
148   // is re-docked.
149   findChromeWindowForChats: function(preferredWindow) {
150     if (preferredWindow) {
151       preferredWindow = getChromeWindow(preferredWindow);
152       if (isWindowGoodForChats(preferredWindow)) {
153         return preferredWindow;
154       }
155     }
156     // no good - we just use the "most recent" browser window which can host
157     // chats (we used to try and "group" all chats in the same browser window,
158     // but that didn't work out so well - see bug 835111
160     // Try first the most recent window as getMostRecentWindow works
161     // even on platforms where getZOrderDOMWindowEnumerator is broken
162     // (ie. Linux).  This will handle most cases, but won't work if the
163     // foreground window is a popup.
165     let mostRecent = Services.wm.getMostRecentWindow("navigator:browser");
166     if (isWindowGoodForChats(mostRecent))
167       return mostRecent;
169     let topMost, enumerator;
170     // *sigh* - getZOrderDOMWindowEnumerator is broken except on Mac and
171     // Windows.  We use BROKEN_WM_Z_ORDER as that is what some other code uses
172     // and a few bugs recommend searching mxr for this symbol to identify the
173     // workarounds - we want this code to be hit in such searches.
174     let os = Services.appinfo.OS;
175     const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
176     if (BROKEN_WM_Z_ORDER) {
177       // this is oldest to newest and no way to change the order.
178       enumerator = Services.wm.getEnumerator("navigator:browser");
179     } else {
180       // here we explicitly ask for bottom-to-top so we can use the same logic
181       // where BROKEN_WM_Z_ORDER is true.
182       enumerator = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", false);
183     }
184     while (enumerator.hasMoreElements()) {
185       let win = enumerator.getNext();
186       if (!win.closed && isWindowGoodForChats(win))
187         topMost = win;
188     }
189     return topMost;
190   },