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/. */
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");
32 function isWindowGoodForChats(win) {
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)
44 .QueryInterface(Ci.nsIInterfaceRequestor)
45 .getInterface(Ci.nsIDOMWindow);
49 * The exported Chat object
56 * @param contentWindow [optional]
57 * The content window that requested this chat. May be null.
59 * The origin for the chat. This is primarily used as an identifier
60 * to help identify all chats from the same provider.
62 * The title to be used if a new chat window is created.
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)
78 open: function(contentWindow, origin, title, url, mode, focus, callback) {
79 let chromeWindow = this.findChromeWindowForChats(contentWindow);
81 Cu.reportError("Failed to open a chat window - no host window could be found.");
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;
105 * Close all chats from the specified origin.
108 * The origin from which all chats should be closed.
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");
118 let chats = [c for (c of chatbar.children) if (c.content.getAttribute("origin") == origin)];
119 [c.close() for (c of chats)];
122 // close all standalone chat windows
123 winEnum = Services.wm.getEnumerator("Social:Chat");
124 while (winEnum.hasMoreElements()) {
125 let win = winEnum.getNext();
128 let chatOrigin = win.document.getElementById("chatter").content.getAttribute("origin");
129 if (origin == chatOrigin)
135 * Focus the chatbar associated with a window
139 focus: function(win) {
140 let chatbar = win.document.getElementById("pinnedchats");
141 if (chatbar && !chatbar.hidden) {
147 // This is exported as socialchat.xml needs to find a window when a chat
149 findChromeWindowForChats: function(preferredWindow) {
150 if (preferredWindow) {
151 preferredWindow = getChromeWindow(preferredWindow);
152 if (isWindowGoodForChats(preferredWindow)) {
153 return preferredWindow;
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))
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");
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);
184 while (enumerator.hasMoreElements()) {
185 let win = enumerator.getNext();
186 if (!win.closed && isWindowGoodForChats(win))