mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1002914 (part 1) - refactor the chat window so it can be used by loop and social. r=mixedpuppy
This commit is contained in:
parent
e6d72f3c2b
commit
54b7c91e28
@ -119,8 +119,8 @@
|
|||||||
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
|
<command id="Social:SharePage" oncommand="SocialShare.sharePage();" disabled="true"/>
|
||||||
<command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
|
<command id="Social:ToggleSidebar" oncommand="SocialSidebar.toggleSidebar();" hidden="true"/>
|
||||||
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
|
<command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
|
||||||
<command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
|
|
||||||
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
|
<command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
|
||||||
|
<command id="Chat:Focus" oncommand="Cu.import('resource:///modules/Chat.jsm', {}).Chat.focus(window);"/>
|
||||||
</commandset>
|
</commandset>
|
||||||
|
|
||||||
<commandset id="placesCommands">
|
<commandset id="placesCommands">
|
||||||
@ -380,7 +380,15 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
<!--<key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>-->
|
<!--<key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>-->
|
||||||
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
|
<key id="focusChatBar" key="&social.chatBar.commandkey;" command="Chat:Focus"
|
||||||
|
#ifdef XP_MACOSX
|
||||||
|
# Sadly the devtools uses shift-accel-c on non-mac and alt-accel-c everywhere else
|
||||||
|
# So we just use the other
|
||||||
|
modifiers="accel,shift"
|
||||||
|
#else
|
||||||
|
modifiers="accel,alt"
|
||||||
|
#endif
|
||||||
|
/>
|
||||||
|
|
||||||
<key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
|
<key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
|
||||||
|
|
||||||
|
@ -173,7 +173,6 @@ SocialUI = {
|
|||||||
_providersChanged: function() {
|
_providersChanged: function() {
|
||||||
SocialSidebar.clearProviderMenus();
|
SocialSidebar.clearProviderMenus();
|
||||||
SocialSidebar.update();
|
SocialSidebar.update();
|
||||||
SocialChatBar.update();
|
|
||||||
SocialShare.populateProviderMenu();
|
SocialShare.populateProviderMenu();
|
||||||
SocialStatus.populateToolbarPalette();
|
SocialStatus.populateToolbarPalette();
|
||||||
SocialMarks.populateToolbarPalette();
|
SocialMarks.populateToolbarPalette();
|
||||||
@ -301,39 +300,13 @@ SocialChatBar = {
|
|||||||
get chatbar() {
|
get chatbar() {
|
||||||
return document.getElementById("pinnedchats");
|
return document.getElementById("pinnedchats");
|
||||||
},
|
},
|
||||||
// Whether the chatbar is available for this window. Note that in full-screen
|
|
||||||
// mode chats are available, but not shown.
|
|
||||||
get isAvailable() {
|
|
||||||
return SocialUI.enabled;
|
|
||||||
},
|
|
||||||
// Does this chatbar have any chats (whether minimized, collapsed or normal)
|
|
||||||
get hasChats() {
|
|
||||||
return !!this.chatbar.firstElementChild;
|
|
||||||
},
|
|
||||||
openChat: function(aProvider, aURL, aCallback, aMode) {
|
openChat: function(aProvider, aURL, aCallback, aMode) {
|
||||||
this.update();
|
// Until we refactor the tests we can't remove this.
|
||||||
if (!this.isAvailable)
|
// Reach into the social API.
|
||||||
return false;
|
let openChatWindow = Cu.import("resource://gre/modules/MozSocialAPI.jsm", {}).openChatWindow;
|
||||||
this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
|
openChatWindow(window, aProvider, aURL, aCallback, aMode);
|
||||||
// We only want to focus the chat if it is as a result of user input.
|
|
||||||
let dwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsIDOMWindowUtils);
|
|
||||||
if (dwu.isHandlingUserInput)
|
|
||||||
this.chatbar.focus();
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
update: function() {
|
|
||||||
let command = document.getElementById("Social:FocusChat");
|
|
||||||
if (!this.isAvailable) {
|
|
||||||
this.chatbar.hidden = command.hidden = true;
|
|
||||||
} else {
|
|
||||||
this.chatbar.hidden = command.hidden = false;
|
|
||||||
}
|
|
||||||
command.setAttribute("disabled", command.hidden ? "true" : "false");
|
|
||||||
},
|
|
||||||
focus: function SocialChatBar_focus() {
|
|
||||||
this.chatbar.focus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SocialFlyout = {
|
SocialFlyout = {
|
||||||
|
@ -30,50 +30,43 @@
|
|||||||
|
|
||||||
<implementation implements="nsIDOMEventListener">
|
<implementation implements="nsIDOMEventListener">
|
||||||
<constructor><![CDATA[
|
<constructor><![CDATA[
|
||||||
let Social = Components.utils.import("resource:///modules/Social.jsm", {}).Social;
|
|
||||||
this.content.__defineGetter__("popupnotificationanchor",
|
this.content.__defineGetter__("popupnotificationanchor",
|
||||||
() => document.getAnonymousElementByAttribute(this, "anonid", "notification-icon"));
|
() => document.getAnonymousElementByAttribute(this, "anonid", "notification-icon"));
|
||||||
Social.setErrorListener(this.content, function(aBrowser) {
|
|
||||||
aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
|
|
||||||
encodeURIComponent(aBrowser.getAttribute("origin")),
|
|
||||||
null, null, null, null);
|
|
||||||
});
|
|
||||||
if (!this.chatbar) {
|
if (!this.chatbar) {
|
||||||
document.getAnonymousElementByAttribute(this, "anonid", "minimize").hidden = true;
|
document.getAnonymousElementByAttribute(this, "anonid", "minimize").hidden = true;
|
||||||
document.getAnonymousElementByAttribute(this, "anonid", "close").hidden = true;
|
document.getAnonymousElementByAttribute(this, "anonid", "close").hidden = true;
|
||||||
}
|
}
|
||||||
let contentWindow = this.contentWindow;
|
let contentWindow = this.contentWindow;
|
||||||
|
// process this._callbacks, then set to null so the chatbox creator
|
||||||
|
// knows to make new callbacks immediately.
|
||||||
|
if (this._callbacks) {
|
||||||
|
for (let callback of this._callbacks) {
|
||||||
|
callback(this);
|
||||||
|
}
|
||||||
|
this._callbacks = null;
|
||||||
|
}
|
||||||
this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
|
this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
|
||||||
if (event.target != this.contentDocument)
|
if (event.target != this.contentDocument)
|
||||||
return;
|
return;
|
||||||
this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
|
this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
|
||||||
this.isActive = !this.minimized;
|
this.isActive = !this.minimized;
|
||||||
// process this._callbacks, then set to null so the chatbox creator
|
this._deferredChatLoaded.resolve(this);
|
||||||
// knows to make new callbacks immediately.
|
|
||||||
if (this._callbacks) {
|
|
||||||
for (let callback of this._callbacks) {
|
|
||||||
if (callback)
|
|
||||||
callback(contentWindow);
|
|
||||||
}
|
|
||||||
this._callbacks = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// content can send a socialChatActivity event to have the UI update.
|
|
||||||
let chatActivity = function() {
|
|
||||||
this.setAttribute("activity", true);
|
|
||||||
if (this.chatbar)
|
|
||||||
this.chatbar.updateTitlebar(this);
|
|
||||||
}.bind(this);
|
|
||||||
contentWindow.addEventListener("socialChatActivity", chatActivity);
|
|
||||||
contentWindow.addEventListener("unload", function unload() {
|
|
||||||
contentWindow.removeEventListener("unload", unload);
|
|
||||||
contentWindow.removeEventListener("socialChatActivity", chatActivity);
|
|
||||||
});
|
|
||||||
}, true);
|
}, true);
|
||||||
if (this.src)
|
if (this.src)
|
||||||
this.setAttribute("src", this.src);
|
this.setAttribute("src", this.src);
|
||||||
]]></constructor>
|
]]></constructor>
|
||||||
|
|
||||||
|
<field name="_deferredChatLoaded" readonly="true">
|
||||||
|
Promise.defer();
|
||||||
|
</field>
|
||||||
|
|
||||||
|
<property name="promiseChatLoaded">
|
||||||
|
<getter>
|
||||||
|
return this._deferredChatLoaded.promise;
|
||||||
|
</getter>
|
||||||
|
</property>
|
||||||
|
|
||||||
<field name="content" readonly="true">
|
<field name="content" readonly="true">
|
||||||
document.getAnonymousElementByAttribute(this, "anonid", "content");
|
document.getAnonymousElementByAttribute(this, "anonid", "content");
|
||||||
</field>
|
</field>
|
||||||
@ -133,7 +126,7 @@
|
|||||||
this.contentDocument.documentElement.dispatchEvent(evt);
|
this.contentDocument.documentElement.dispatchEvent(evt);
|
||||||
</setter>
|
</setter>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
<method name="showNotifications">
|
<method name="showNotifications">
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
PopupNotifications._reshowNotifications(this.content.popupnotificationanchor,
|
PopupNotifications._reshowNotifications(this.content.popupnotificationanchor,
|
||||||
@ -148,15 +141,7 @@
|
|||||||
aTarget.src = this.src;
|
aTarget.src = this.src;
|
||||||
aTarget.content.setAttribute("origin", this.content.getAttribute("origin"));
|
aTarget.content.setAttribute("origin", this.content.getAttribute("origin"));
|
||||||
aTarget.content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
|
aTarget.content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
|
||||||
this.content.socialErrorListener.remove();
|
|
||||||
aTarget.content.socialErrorListener.remove();
|
|
||||||
this.content.swapDocShells(aTarget.content);
|
this.content.swapDocShells(aTarget.content);
|
||||||
Social.setErrorListener(this.content, function(aBrowser) {}); // 'this' will be destroyed soon.
|
|
||||||
Social.setErrorListener(aTarget.content, function(aBrowser) {
|
|
||||||
aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
|
|
||||||
encodeURIComponent(aBrowser.getAttribute("origin")),
|
|
||||||
null, null, null, null);
|
|
||||||
});
|
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
@ -186,31 +171,40 @@
|
|||||||
|
|
||||||
<method name="swapWindows">
|
<method name="swapWindows">
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
let provider = Social._getProviderFromOrigin(this.content.getAttribute("origin"));
|
let deferred = Promise.defer();
|
||||||
|
let title = this.getAttribute("label");
|
||||||
if (this.chatbar) {
|
if (this.chatbar) {
|
||||||
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }, win => {
|
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }).then(
|
||||||
win.document.title = provider.name;
|
chatbox => {
|
||||||
});
|
chatbox.contentWindow.document.title = title;
|
||||||
|
deferred.resolve(chatbox);
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// attach this chatbox to the topmost browser window
|
// attach this chatbox to the topmost browser window
|
||||||
let findChromeWindowForChats = Cu.import("resource://gre/modules/MozSocialAPI.jsm").findChromeWindowForChats;
|
let Chat = Cu.import("resource:///modules/Chat.jsm").Chat;
|
||||||
let win = findChromeWindowForChats();
|
let win = Chat.findChromeWindowForChats();
|
||||||
let chatbar = win.SocialChatBar.chatbar;
|
let chatbar = win.document.getElementById("pinnedchats");
|
||||||
chatbar.openChat(provider, "about:blank", win => {
|
let origin = this.content.getAttribute("origin");
|
||||||
let cb = chatbar.selectedChat;
|
let cb = chatbar.openChat(origin, title, "about:blank");
|
||||||
this.swapDocShells(cb);
|
cb.promiseChatLoaded.then(
|
||||||
|
() => {
|
||||||
|
this.swapDocShells(cb);
|
||||||
|
|
||||||
// chatboxForURL is a map of URL -> chatbox used to avoid opening
|
// chatboxForURL is a map of URL -> chatbox used to avoid opening
|
||||||
// duplicate chat windows. Ensure reattached chat windows aren't
|
// duplicate chat windows. Ensure reattached chat windows aren't
|
||||||
// registered with about:blank as their URL, otherwise reattaching
|
// registered with about:blank as their URL, otherwise reattaching
|
||||||
// more than one chat window isn't possible.
|
// more than one chat window isn't possible.
|
||||||
chatbar.chatboxForURL.delete("about:blank");
|
chatbar.chatboxForURL.delete("about:blank");
|
||||||
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
|
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
|
||||||
|
|
||||||
chatbar.focus();
|
chatbar.focus();
|
||||||
this.close();
|
this.close();
|
||||||
});
|
deferred.resolve(cb);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return deferred.promise;
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
@ -510,7 +504,6 @@
|
|||||||
<method name="_remove">
|
<method name="_remove">
|
||||||
<parameter name="aChatbox"/>
|
<parameter name="aChatbox"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
aChatbox.content.socialErrorListener.remove();
|
|
||||||
this.removeChild(aChatbox);
|
this.removeChild(aChatbox);
|
||||||
// child might have been collapsed.
|
// child might have been collapsed.
|
||||||
let menuitem = this.menuitemMap.get(aChatbox);
|
let menuitem = this.menuitemMap.get(aChatbox);
|
||||||
@ -522,6 +515,11 @@
|
|||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
|
<!-- XXX - we should remove this - it is currently used only by tests.
|
||||||
|
No-one has any business removing all chats. Chat.jsm exposes a
|
||||||
|
method to close all chats from a specific origin which is all that
|
||||||
|
should be necessary.
|
||||||
|
-->
|
||||||
<method name="removeAll">
|
<method name="removeAll">
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
this.selectedChat = null;
|
this.selectedChat = null;
|
||||||
@ -534,10 +532,11 @@
|
|||||||
</method>
|
</method>
|
||||||
|
|
||||||
<method name="openChat">
|
<method name="openChat">
|
||||||
<parameter name="aProvider"/>
|
<parameter name="aOrigin"/>
|
||||||
|
<parameter name="aTitle"/>
|
||||||
<parameter name="aURL"/>
|
<parameter name="aURL"/>
|
||||||
<parameter name="aCallback"/>
|
|
||||||
<parameter name="aMode"/>
|
<parameter name="aMode"/>
|
||||||
|
<parameter name="aCallback"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
let cb = this.chatboxForURL.get(aURL);
|
let cb = this.chatboxForURL.get(aURL);
|
||||||
if (cb) {
|
if (cb) {
|
||||||
@ -546,30 +545,35 @@
|
|||||||
this.showChat(cb, aMode);
|
this.showChat(cb, aMode);
|
||||||
if (aCallback) {
|
if (aCallback) {
|
||||||
if (cb._callbacks == null) {
|
if (cb._callbacks == null) {
|
||||||
// DOMContentLoaded has already fired, so callback now.
|
// Chatbox has already been created, so callback now.
|
||||||
aCallback(cb.contentWindow);
|
aCallback(cb);
|
||||||
} else {
|
} else {
|
||||||
// DOMContentLoaded for this chat is yet to fire...
|
// Chatbox is yet to have bindings created...
|
||||||
cb._callbacks.push(aCallback);
|
cb._callbacks.push(aCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return cb;
|
||||||
}
|
}
|
||||||
this.chatboxForURL.delete(aURL);
|
this.chatboxForURL.delete(aURL);
|
||||||
}
|
}
|
||||||
cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
|
cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
|
||||||
// _callbacks is a javascript property instead of a <field> as it
|
cb._callbacks = [];
|
||||||
// must exist before the (possibly delayed) bindings are created.
|
if (aCallback) {
|
||||||
cb._callbacks = [aCallback];
|
// _callbacks is a javascript property instead of a <field> as it
|
||||||
|
// must exist before the (possibly delayed) bindings are created.
|
||||||
|
cb._callbacks.push(aCallback);
|
||||||
|
}
|
||||||
// src also a javascript property; the src attribute is set in the ctor.
|
// src also a javascript property; the src attribute is set in the ctor.
|
||||||
cb.src = aURL;
|
cb.src = aURL;
|
||||||
if (aMode == "minimized")
|
if (aMode == "minimized")
|
||||||
cb.setAttribute("minimized", "true");
|
cb.setAttribute("minimized", "true");
|
||||||
cb.setAttribute("origin", aProvider.origin);
|
cb.setAttribute("origin", aOrigin);
|
||||||
|
cb.setAttribute("label", aTitle);
|
||||||
this.insertBefore(cb, this.firstChild);
|
this.insertBefore(cb, this.firstChild);
|
||||||
this.selectedChat = cb;
|
this.selectedChat = cb;
|
||||||
this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
|
this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
|
||||||
this.resize();
|
this.resize();
|
||||||
|
return cb;
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
@ -648,12 +652,14 @@
|
|||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
<!-- Moves a chatbox to a new window. -->
|
<!-- Moves a chatbox to a new window. Returns a promise that is resolved
|
||||||
|
once the move to the other window is complete.
|
||||||
|
-->
|
||||||
<method name="detachChatbox">
|
<method name="detachChatbox">
|
||||||
<parameter name="aChatbox"/>
|
<parameter name="aChatbox"/>
|
||||||
<parameter name="aOptions"/>
|
<parameter name="aOptions"/>
|
||||||
<parameter name="aCallback"/>
|
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
|
let deferred = Promise.defer();
|
||||||
let options = "";
|
let options = "";
|
||||||
for (let name in aOptions)
|
for (let name in aOptions)
|
||||||
options += "," + name + "=" + aOptions[name];
|
options += "," + name + "=" + aOptions[name];
|
||||||
@ -669,9 +675,9 @@
|
|||||||
let otherChatbox = otherWin.document.getElementById("chatter");
|
let otherChatbox = otherWin.document.getElementById("chatter");
|
||||||
aChatbox.swapDocShells(otherChatbox);
|
aChatbox.swapDocShells(otherChatbox);
|
||||||
aChatbox.close();
|
aChatbox.close();
|
||||||
if (aCallback)
|
deferred.resolve(otherChatbox);
|
||||||
aCallback(otherWin);
|
|
||||||
}, true);
|
}, true);
|
||||||
|
return deferred.promise;
|
||||||
]]></body>
|
]]></body>
|
||||||
</method>
|
</method>
|
||||||
|
|
||||||
@ -750,11 +756,12 @@
|
|||||||
let top = Math.min(Math.max(eY, sY.value),
|
let top = Math.min(Math.max(eY, sY.value),
|
||||||
sY.value + sHeight.value - winHeight);
|
sY.value + sHeight.value - winHeight);
|
||||||
|
|
||||||
let provider = Social._getProviderFromOrigin(draggedChat.content.getAttribute("origin"));
|
let title = draggedChat.content.getAttribute("title");
|
||||||
this.detachChatbox(draggedChat, { screenX: left, screenY: top }, win => {
|
this.detachChatbox(draggedChat, { screenX: left, screenY: top }).then(
|
||||||
win.document.title = provider.name;
|
chatbox => {
|
||||||
});
|
chatbox.contentWindow.document.title = title;
|
||||||
|
}
|
||||||
|
);
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
]]></handler>
|
]]></handler>
|
||||||
</handlers>
|
</handlers>
|
||||||
|
@ -44,6 +44,11 @@ function openChat(provider, callback) {
|
|||||||
gURLsNotRemembered.push(url);
|
gURLsNotRemembered.push(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function windowHasChats(win) {
|
||||||
|
let chatbar = win.document.getElementById("pinnedchats");
|
||||||
|
return !!chatbar.firstElementChild;
|
||||||
|
}
|
||||||
|
|
||||||
function test() {
|
function test() {
|
||||||
requestLongerTimeout(2); // only debug builds seem to need more time...
|
requestLongerTimeout(2); // only debug builds seem to need more time...
|
||||||
waitForExplicitFinish();
|
waitForExplicitFinish();
|
||||||
@ -341,61 +346,6 @@ var tests = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
testActivity: function(next) {
|
|
||||||
let port = SocialSidebar.provider.getWorkerPort();
|
|
||||||
port.postMessage({topic: "test-init"});
|
|
||||||
get3ChatsForCollapsing("normal", function(first, second, third) {
|
|
||||||
let chatbar = window.SocialChatBar.chatbar;
|
|
||||||
is(chatbar.selectedChat, third, "third chat should be selected");
|
|
||||||
ok(!chatbar.selectedChat.hasAttribute("activity"), "third chat should have no activity");
|
|
||||||
// send an activity message to the second.
|
|
||||||
ok(!second.hasAttribute("activity"), "second chat should have no activity");
|
|
||||||
let chat2 = second.content;
|
|
||||||
let evt = chat2.contentDocument.createEvent("CustomEvent");
|
|
||||||
evt.initCustomEvent("socialChatActivity", true, true, {});
|
|
||||||
chat2.contentDocument.documentElement.dispatchEvent(evt);
|
|
||||||
// second should have activity.
|
|
||||||
ok(second.hasAttribute("activity"), "second chat should now have activity");
|
|
||||||
// select the second - it should lose "activity"
|
|
||||||
chatbar.selectedChat = second;
|
|
||||||
ok(!second.hasAttribute("activity"), "second chat should no longer have activity");
|
|
||||||
// Now try the first - it is collapsed, so the 'nub' also gets activity attr.
|
|
||||||
ok(!first.hasAttribute("activity"), "first chat should have no activity");
|
|
||||||
let chat1 = first.content;
|
|
||||||
let evt = chat1.contentDocument.createEvent("CustomEvent");
|
|
||||||
evt.initCustomEvent("socialChatActivity", true, true, {});
|
|
||||||
chat1.contentDocument.documentElement.dispatchEvent(evt);
|
|
||||||
ok(first.hasAttribute("activity"), "first chat should now have activity");
|
|
||||||
ok(chatbar.nub.hasAttribute("activity"), "nub should also have activity");
|
|
||||||
// first is collapsed, so use openChat to get it.
|
|
||||||
chatbar.openChat(SocialSidebar.provider, first.getAttribute("src"));
|
|
||||||
ok(!first.hasAttribute("activity"), "first chat should no longer have activity");
|
|
||||||
// The nub should lose the activity flag here too
|
|
||||||
todo(!chatbar.nub.hasAttribute("activity"), "Bug 806266 - nub should no longer have activity");
|
|
||||||
// TODO: tests for bug 806266 should arrange to have 2 chats collapsed
|
|
||||||
// then open them checking the nub is updated correctly.
|
|
||||||
// Now we will go and change the embedded browser in the second chat and
|
|
||||||
// ensure the activity magic still works (ie, check that the unload for
|
|
||||||
// the browser didn't cause our event handlers to be removed.)
|
|
||||||
ok(!second.hasAttribute("activity"), "second chat should have no activity");
|
|
||||||
let subiframe = chat2.contentDocument.getElementById("iframe");
|
|
||||||
subiframe.contentWindow.addEventListener("unload", function subunload() {
|
|
||||||
subiframe.contentWindow.removeEventListener("unload", subunload);
|
|
||||||
// ensure all other unload listeners have fired.
|
|
||||||
executeSoon(function() {
|
|
||||||
let evt = chat2.contentDocument.createEvent("CustomEvent");
|
|
||||||
evt.initCustomEvent("socialChatActivity", true, true, {});
|
|
||||||
chat2.contentDocument.documentElement.dispatchEvent(evt);
|
|
||||||
ok(second.hasAttribute("activity"), "second chat still has activity after unloading sub-iframe");
|
|
||||||
closeAllChats();
|
|
||||||
port.close();
|
|
||||||
next();
|
|
||||||
})
|
|
||||||
})
|
|
||||||
subiframe.setAttribute("src", "data:text/plain:new location for iframe");
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
testOnlyOneCallback: function(next) {
|
testOnlyOneCallback: function(next) {
|
||||||
let chats = document.getElementById("pinnedchats");
|
let chats = document.getElementById("pinnedchats");
|
||||||
let port = SocialSidebar.provider.getWorkerPort();
|
let port = SocialSidebar.provider.getWorkerPort();
|
||||||
@ -448,31 +398,31 @@ var tests = {
|
|||||||
// Tests that when a worker creates a chat, it is opened in the correct
|
// Tests that when a worker creates a chat, it is opened in the correct
|
||||||
// window.
|
// window.
|
||||||
// open a chat (it will open in the main window)
|
// open a chat (it will open in the main window)
|
||||||
ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
|
ok(!windowHasChats(window), "first window should start with no chats");
|
||||||
openChat(SocialSidebar.provider, function() {
|
openChat(SocialSidebar.provider, function() {
|
||||||
ok(window.SocialChatBar.hasChats, "first window has the chat");
|
ok(windowHasChats(window), "first window has the chat");
|
||||||
// create a second window - this will be the "most recent" and will
|
// create a second window - this will be the "most recent" and will
|
||||||
// therefore be the window that hosts the new chat (see bug 835111)
|
// therefore be the window that hosts the new chat (see bug 835111)
|
||||||
let secondWindow = OpenBrowserWindow();
|
let secondWindow = OpenBrowserWindow();
|
||||||
secondWindow.addEventListener("load", function loadListener() {
|
secondWindow.addEventListener("load", function loadListener() {
|
||||||
secondWindow.removeEventListener("load", loadListener);
|
secondWindow.removeEventListener("load", loadListener);
|
||||||
ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
|
ok(!windowHasChats(secondWindow), "second window has no chats");
|
||||||
openChat(SocialSidebar.provider, function() {
|
openChat(SocialSidebar.provider, function() {
|
||||||
ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
|
ok(windowHasChats(secondWindow), "second window now has chats");
|
||||||
is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
|
is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
|
||||||
window.SocialChatBar.chatbar.removeAll();
|
window.SocialChatBar.chatbar.removeAll();
|
||||||
// now open another chat - it should still open in the second.
|
// now open another chat - it should still open in the second.
|
||||||
openChat(SocialSidebar.provider, function() {
|
openChat(SocialSidebar.provider, function() {
|
||||||
ok(!window.SocialChatBar.hasChats, "first window has no chats");
|
ok(!windowHasChats(window), "first window has no chats");
|
||||||
ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
|
ok(windowHasChats(secondWindow), "second window has a chat");
|
||||||
|
|
||||||
// focus the first window, and open yet another chat - it
|
// focus the first window, and open yet another chat - it
|
||||||
// should open in the first window.
|
// should open in the first window.
|
||||||
waitForFocus(function() {
|
waitForFocus(function() {
|
||||||
openChat(SocialSidebar.provider, function() {
|
openChat(SocialSidebar.provider, function() {
|
||||||
ok(window.SocialChatBar.hasChats, "first window has chats");
|
ok(windowHasChats(window), "first window has chats");
|
||||||
window.SocialChatBar.chatbar.removeAll();
|
window.SocialChatBar.chatbar.removeAll();
|
||||||
ok(!window.SocialChatBar.hasChats, "first window has no chats");
|
ok(!windowHasChats(window), "first window has no chats");
|
||||||
|
|
||||||
let privateWindow = OpenBrowserWindow({private: true});
|
let privateWindow = OpenBrowserWindow({private: true});
|
||||||
privateWindow.addEventListener("load", function loadListener() {
|
privateWindow.addEventListener("load", function loadListener() {
|
||||||
@ -486,7 +436,7 @@ var tests = {
|
|||||||
let os = Services.appinfo.OS;
|
let os = Services.appinfo.OS;
|
||||||
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
|
const BROKEN_WM_Z_ORDER = os != "WINNT" && os != "Darwin";
|
||||||
let fn = BROKEN_WM_Z_ORDER ? todo : ok;
|
let fn = BROKEN_WM_Z_ORDER ? todo : ok;
|
||||||
fn(window.SocialChatBar.hasChats, "first window has a chat");
|
fn(windowHasChats(window), "first window has a chat");
|
||||||
window.SocialChatBar.chatbar.removeAll();
|
window.SocialChatBar.chatbar.removeAll();
|
||||||
|
|
||||||
privateWindow.close();
|
privateWindow.close();
|
||||||
|
@ -174,5 +174,36 @@ var tests = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
testChatWindowAfterTearOff: function(next) {
|
||||||
|
// Ensure that the error listener survives the chat window being detached.
|
||||||
|
let url = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||||
|
let panelCallbackCount = 0;
|
||||||
|
// open a chat while we are still online.
|
||||||
|
openChat(
|
||||||
|
url,
|
||||||
|
null,
|
||||||
|
function() { // the "load" callback.
|
||||||
|
executeSoon(function() {
|
||||||
|
let chat = SocialChatBar.chatbar.selectedChat;
|
||||||
|
is(chat.contentDocument.location.href, url, "correct url loaded");
|
||||||
|
// toggle to a detached window.
|
||||||
|
chat.swapWindows().then(
|
||||||
|
chat => {
|
||||||
|
// now go offline and reload the chat - about:socialerror should be loaded.
|
||||||
|
goOffline();
|
||||||
|
chat.contentDocument.location.reload();
|
||||||
|
waitForCondition(function() chat.contentDocument.location.href.indexOf("about:socialerror?")==0,
|
||||||
|
function() {
|
||||||
|
chat.close();
|
||||||
|
next();
|
||||||
|
},
|
||||||
|
"error page didn't appear");
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -237,8 +237,6 @@ function checkSocialUI(win) {
|
|||||||
_is(!!a, !!b, msg);
|
_is(!!a, !!b, msg);
|
||||||
}
|
}
|
||||||
isbool(win.SocialSidebar.canShow, sidebarEnabled, "social sidebar active?");
|
isbool(win.SocialSidebar.canShow, sidebarEnabled, "social sidebar active?");
|
||||||
isbool(win.SocialChatBar.isAvailable, enabled, "chatbar available?");
|
|
||||||
isbool(!win.SocialChatBar.chatbar.hidden, enabled, "chatbar visible?");
|
|
||||||
|
|
||||||
let contextMenus = [
|
let contextMenus = [
|
||||||
{
|
{
|
||||||
@ -279,8 +277,6 @@ function checkSocialUI(win) {
|
|||||||
// and for good measure, check all the social commands.
|
// and for good measure, check all the social commands.
|
||||||
isbool(!doc.getElementById("Social:ToggleSidebar").hidden, sidebarEnabled, "Social:ToggleSidebar visible?");
|
isbool(!doc.getElementById("Social:ToggleSidebar").hidden, sidebarEnabled, "Social:ToggleSidebar visible?");
|
||||||
isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
|
isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Social:ToggleNotifications visible?");
|
||||||
isbool(!doc.getElementById("Social:FocusChat").hidden, enabled, "Social:FocusChat visible?");
|
|
||||||
isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
|
|
||||||
|
|
||||||
// and report on overall success of failure of the various checks here.
|
// and report on overall success of failure of the various checks here.
|
||||||
is(numGoodTests, numTests, "The Social UI tests succeeded.")
|
is(numGoodTests, numTests, "The Social UI tests succeeded.")
|
||||||
|
191
browser/modules/Chat.jsm
Normal file
191
browser/modules/Chat.jsm
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/* 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 chat windows.
|
||||||
|
|
||||||
|
this.EXPORTED_SYMBOLS = ["Chat"];
|
||||||
|
|
||||||
|
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, "PrivateBrowsingUtils",
|
||||||
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||||
|
|
||||||
|
// 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") ||
|
||||||
|
docElem.getAttribute('chromehidden').contains("toolbar");
|
||||||
|
return chromeless;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isWindowGoodForChats(win) {
|
||||||
|
return !win.closed &&
|
||||||
|
!!win.document.getElementById("pinnedchats") &&
|
||||||
|
!isWindowChromeless(win) &&
|
||||||
|
!PrivateBrowsingUtils.isWindowPrivate(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getChromeWindow(contentWin) {
|
||||||
|
return contentWin.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIWebNavigation)
|
||||||
|
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||||
|
.rootTreeItem
|
||||||
|
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The exported Chat object
|
||||||
|
*/
|
||||||
|
|
||||||
|
let Chat = {
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
chromeWindow.getAttention();
|
||||||
|
// 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)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
|
focus = dwu.isHandlingUserInput;
|
||||||
|
}
|
||||||
|
if (focus) {
|
||||||
|
chatbar.focus();
|
||||||
|
}
|
||||||
|
return chatbox;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close all chats from the specified origin.
|
||||||
|
*
|
||||||
|
* @param origin
|
||||||
|
* The origin from which all chats should be closed.
|
||||||
|
*/
|
||||||
|
closeAll: function(origin) {
|
||||||
|
// close all attached chat windows
|
||||||
|
let winEnum = Services.wm.getEnumerator("navigator:browser");
|
||||||
|
while (winEnum.hasMoreElements()) {
|
||||||
|
let win = winEnum.getNext();
|
||||||
|
let chatbar = win.document.getElementById("pinnedchats");
|
||||||
|
if (!chatbar)
|
||||||
|
continue;
|
||||||
|
let chats = [c for (c of chatbar.children) if (c.content.getAttribute("origin") == origin)];
|
||||||
|
[c.close() for (c of chats)];
|
||||||
|
}
|
||||||
|
|
||||||
|
// close all standalone chat windows
|
||||||
|
winEnum = Services.wm.getEnumerator("Social:Chat");
|
||||||
|
while (winEnum.hasMoreElements()) {
|
||||||
|
let win = winEnum.getNext();
|
||||||
|
if (win.closed)
|
||||||
|
continue;
|
||||||
|
let chatOrigin = win.document.getElementById("chatter").content.getAttribute("origin");
|
||||||
|
if (origin == chatOrigin)
|
||||||
|
win.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focus the chatbar associated with a window
|
||||||
|
*
|
||||||
|
* @param window
|
||||||
|
*/
|
||||||
|
focus: function(win) {
|
||||||
|
let chatbar = win.document.getElementById("pinnedchats");
|
||||||
|
if (chatbar && !chatbar.hidden) {
|
||||||
|
chatbar.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
},
|
||||||
|
}
|
@ -9,6 +9,7 @@ TEST_DIRS += ['test']
|
|||||||
EXTRA_JS_MODULES += [
|
EXTRA_JS_MODULES += [
|
||||||
'BrowserNewTabPreloader.jsm',
|
'BrowserNewTabPreloader.jsm',
|
||||||
'BrowserUITelemetry.jsm',
|
'BrowserUITelemetry.jsm',
|
||||||
|
'Chat.jsm',
|
||||||
'ContentClick.jsm',
|
'ContentClick.jsm',
|
||||||
'ContentLinkHandler.jsm',
|
'ContentLinkHandler.jsm',
|
||||||
'ContentSearch.jsm',
|
'ContentSearch.jsm',
|
||||||
|
@ -8,6 +8,8 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "SocialService", "resource://gre/modules/SocialService.jsm");
|
XPCOMUtils.defineLazyModuleGetter(this, "SocialService", "resource://gre/modules/SocialService.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Social", "resource:///modules/Social.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm");
|
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["MozSocialAPI", "openChatWindow", "findChromeWindowForChats", "closeAllChatWindows"];
|
this.EXPORTED_SYMBOLS = ["MozSocialAPI", "openChatWindow", "findChromeWindowForChats", "closeAllChatWindows"];
|
||||||
@ -125,7 +127,7 @@ function attachToWindow(provider, targetWindow) {
|
|||||||
writable: true,
|
writable: true,
|
||||||
value: function(toURL, callback) {
|
value: function(toURL, callback) {
|
||||||
let url = targetWindow.document.documentURIObject.resolve(toURL);
|
let url = targetWindow.document.documentURIObject.resolve(toURL);
|
||||||
openChatWindow(getChromeWindow(targetWindow), provider, url, callback);
|
openChatWindow(targetWindow, provider, url, callback);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openPanel: {
|
openPanel: {
|
||||||
@ -270,92 +272,31 @@ function getChromeWindow(contentWin) {
|
|||||||
.getInterface(Ci.nsIDOMWindow);
|
.getInterface(Ci.nsIDOMWindow);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isWindowGoodForChats(win) {
|
|
||||||
return win.SocialChatBar
|
|
||||||
&& win.SocialChatBar.isAvailable
|
|
||||||
&& !PrivateBrowsingUtils.isWindowPrivate(win);
|
|
||||||
}
|
|
||||||
|
|
||||||
function findChromeWindowForChats(preferredWindow) {
|
|
||||||
if (preferredWindow && 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.openChatWindow =
|
this.openChatWindow =
|
||||||
function openChatWindow(chromeWindow, provider, url, callback, mode) {
|
function openChatWindow(contentWindow, provider, url, callback, mode) {
|
||||||
chromeWindow = findChromeWindowForChats(chromeWindow);
|
|
||||||
if (!chromeWindow) {
|
|
||||||
Cu.reportError("Failed to open a social chat window - no host window could be found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let fullURI = provider.resolveUri(url);
|
let fullURI = provider.resolveUri(url);
|
||||||
if (!provider.isSameOrigin(fullURI)) {
|
if (!provider.isSameOrigin(fullURI)) {
|
||||||
Cu.reportError("Failed to open a social chat window - the requested URL is not the same origin as the provider.");
|
Cu.reportError("Failed to open a social chat window - the requested URL is not the same origin as the provider.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!chromeWindow.SocialChatBar.openChat(provider, fullURI.spec, callback, mode)) {
|
|
||||||
Cu.reportError("Failed to open a social chat window - the chatbar is not available in the target window.");
|
let thisCallback = function(chatbox) {
|
||||||
return;
|
// All social chat windows get a special error listener.
|
||||||
|
Social.setErrorListener(chatbox.content, function(aBrowser) {
|
||||||
|
aBrowser.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
|
||||||
|
encodeURIComponent(aBrowser.getAttribute("origin")),
|
||||||
|
null, null, null, null);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let chatbox = Chat.open(contentWindow, provider.origin, provider.name,
|
||||||
|
fullURI.spec, mode, undefined, thisCallback);
|
||||||
|
if (callback) {
|
||||||
|
chatbox.promiseChatLoaded.then(() => {
|
||||||
|
callback(chatbox.contentWindow);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// getAttention is ignored if the target window is already foreground, so
|
|
||||||
// we can call it unconditionally.
|
|
||||||
chromeWindow.getAttention();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.closeAllChatWindows =
|
this.closeAllChatWindows = function closeAllChatWindows(provider) {
|
||||||
function closeAllChatWindows(provider) {
|
return Chat.closeAll(provider.origin);
|
||||||
// close all attached chat windows
|
|
||||||
let winEnum = Services.wm.getEnumerator("navigator:browser");
|
|
||||||
while (winEnum.hasMoreElements()) {
|
|
||||||
let win = winEnum.getNext();
|
|
||||||
if (!win.SocialChatBar)
|
|
||||||
continue;
|
|
||||||
let chats = [c for (c of win.SocialChatBar.chatbar.children) if (c.content.getAttribute("origin") == provider.origin)];
|
|
||||||
[c.close() for (c of chats)];
|
|
||||||
}
|
|
||||||
|
|
||||||
// close all standalone chat windows
|
|
||||||
winEnum = Services.wm.getEnumerator("Social:Chat");
|
|
||||||
while (winEnum.hasMoreElements()) {
|
|
||||||
let win = winEnum.getNext();
|
|
||||||
if (win.closed)
|
|
||||||
continue;
|
|
||||||
let origin = win.document.getElementById("chatter").content.getAttribute("origin");
|
|
||||||
if (provider.origin == origin)
|
|
||||||
win.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user