mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1154277: Part 2 - support running Social API documents to run in a remote browser, i.e. the content process. f=mixedpuppy, r=Standard8
This commit is contained in:
parent
6c7fdd37b9
commit
f72d46f1cc
@ -370,7 +370,7 @@ SocialFlyout = {
|
||||
// the xbl bindings for the iframe probably don't exist yet, so we can't
|
||||
// access iframe.messageManager directly - but can get at it with this dance.
|
||||
let mm = iframe.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||
mm.sendAsyncMessage("Social:SetErrorURL", null,
|
||||
mm.sendAsyncMessage("Social:SetErrorURL",
|
||||
{ template: "about:socialerror?mode=compactInfo&origin=%{origin}" });
|
||||
},
|
||||
|
||||
@ -512,7 +512,7 @@ SocialShare = {
|
||||
iframe.setAttribute("messagemanagergroup", "social");
|
||||
panel.lastChild.appendChild(iframe);
|
||||
let mm = iframe.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||
mm.sendAsyncMessage("Social:SetErrorURL", null,
|
||||
mm.sendAsyncMessage("Social:SetErrorURL",
|
||||
{ template: "about:socialerror?mode=compactInfo&origin=%{origin}&url=%{url}" });
|
||||
|
||||
this.populateProviderMenu();
|
||||
|
@ -118,6 +118,10 @@ panelview:not([mainview]):not([current]) {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
browser[frameType="social"][remote="true"] {
|
||||
-moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
|
||||
}
|
||||
|
||||
tabbrowser {
|
||||
-moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser");
|
||||
}
|
||||
|
@ -14,10 +14,26 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
// social frames are always treated as app tabs
|
||||
docShell.isAppTab = true;
|
||||
|
||||
var gDOMContentLoaded = false;
|
||||
addEventListener("DOMContentLoaded", function() {
|
||||
gDOMContentLoaded = true;
|
||||
sendAsyncMessage("DOMContentLoaded");
|
||||
});
|
||||
var gDOMTitleChangedByUs = false;
|
||||
addEventListener("DOMTitleChanged", function(e) {
|
||||
if (!gDOMTitleChangedByUs) {
|
||||
sendAsyncMessage("DOMTitleChanged", {
|
||||
title: e.target.title
|
||||
});
|
||||
gDOMTitleChangedByUs = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Error handling class used to listen for network errors in the social frames
|
||||
// and replace them with a social-specific error page
|
||||
SocialErrorListener = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
|
||||
Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsISupports]),
|
||||
|
||||
@ -25,21 +41,132 @@ SocialErrorListener = {
|
||||
urlTemplate: null,
|
||||
|
||||
init() {
|
||||
addMessageListener("Loop:MonitorPeerConnectionLifecycle", this);
|
||||
addMessageListener("Loop:GetAllWebrtcStats", this);
|
||||
addMessageListener("Social:CustomEvent", this);
|
||||
addMessageListener("Social:EnsureFocus", this);
|
||||
addMessageListener("Social:EnsureFocusElement", this);
|
||||
addMessageListener("Social:HookWindowCloseForPanelClose", this);
|
||||
addMessageListener("Social:ListenForEvents", this);
|
||||
addMessageListener("Social:SetDocumentTitle", this);
|
||||
addMessageListener("Social:SetErrorURL", this);
|
||||
addMessageListener("Social:WaitForDocumentVisible", this);
|
||||
addMessageListener("WaitForDOMContentLoaded", this);
|
||||
let webProgress = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIWebProgress);
|
||||
webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
|
||||
Ci.nsIWebProgress.NOTIFY_LOCATION);
|
||||
},
|
||||
|
||||
receiveMessage(message) {
|
||||
switch(message.name) {
|
||||
case "Social:SetErrorURL": {
|
||||
// either a url or null to reset to default template
|
||||
this.urlTemplate = message.objects.template;
|
||||
}
|
||||
let document = content.document;
|
||||
|
||||
switch (message.name) {
|
||||
case "Loop:GetAllWebrtcStats":
|
||||
content.WebrtcGlobalInformation.getAllStats(allStats => {
|
||||
content.WebrtcGlobalInformation.getLogging("", logs => {
|
||||
sendAsyncMessage("Loop:GetAllWebrtcStats", {
|
||||
allStats: allStats,
|
||||
logs: logs
|
||||
});
|
||||
});
|
||||
}, message.data.peerConnectionID);
|
||||
break;
|
||||
case "Loop:MonitorPeerConnectionLifecycle":
|
||||
let ourID = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
||||
|
||||
let onPCLifecycleChange = (pc, winID, type) => {
|
||||
if (winID != ourID) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendAsyncMessage("Loop:PeerConnectionLifecycleChange", {
|
||||
iceConnectionState: pc.iceConnectionState,
|
||||
locationHash: content.location.hash,
|
||||
peerConnectionID: pc.id,
|
||||
type: type
|
||||
});
|
||||
};
|
||||
|
||||
let pc_static = new content.RTCPeerConnectionStatic();
|
||||
pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange);
|
||||
break;
|
||||
case "Social:CustomEvent":
|
||||
let ev = new content.CustomEvent(message.data.name, message.data.detail ?
|
||||
{ detail: message.data.detail } : null);
|
||||
content.dispatchEvent(ev);
|
||||
break;
|
||||
case "Social:EnsureFocus":
|
||||
Services.focus.focusedWindow = content;
|
||||
break;
|
||||
case "Social:EnsureFocusElement":
|
||||
let fm = Services.focus;
|
||||
fm.moveFocus(document.defaultView, null, fm.MOVEFOCUS_FIRST, fm.FLAG_NOSCROLL);
|
||||
break;
|
||||
case "Social:HookWindowCloseForPanelClose":
|
||||
// We allow window.close() to close the panel, so add an event handler for
|
||||
// this, then cancel the event (so the window itself doesn't die) and
|
||||
// close the panel instead.
|
||||
// However, this is typically affected by the dom.allow_scripts_to_close_windows
|
||||
// preference, but we can avoid that check by setting a flag on the window.
|
||||
let dwu = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
dwu.allowScriptsToClose();
|
||||
|
||||
content.addEventListener("DOMWindowClose", function _mozSocialDOMWindowClose(evt) {
|
||||
sendAsyncMessage("DOMWindowClose");
|
||||
// preventDefault stops the default window.close() function being called,
|
||||
// which doesn't actually close anything but causes things to get into
|
||||
// a bad state (an internal 'closed' flag is set and debug builds start
|
||||
// asserting as the window is used.).
|
||||
// None of the windows we inject this API into are suitable for this
|
||||
// default close behaviour, so even if we took no action above, we avoid
|
||||
// the default close from doing anything.
|
||||
evt.preventDefault();
|
||||
}, true);
|
||||
break;
|
||||
case "Social:ListenForEvents":
|
||||
for (let eventName of message.data.eventNames) {
|
||||
content.addEventListener(eventName, this);
|
||||
}
|
||||
break;
|
||||
case "Social:SetDocumentTitle":
|
||||
let title = message.data.title;
|
||||
if (title && (title = title.trim())) {
|
||||
gDOMTitleChangedByUs = true;
|
||||
document.title = title;
|
||||
}
|
||||
break;
|
||||
case "Social:SetErrorURL":
|
||||
// Either a url or null to reset to default template.
|
||||
this.urlTemplate = message.data.template;
|
||||
break;
|
||||
case "Social:WaitForDocumentVisible":
|
||||
if (!document.hidden) {
|
||||
sendAsyncMessage("Social:DocumentVisible");
|
||||
break;
|
||||
}
|
||||
|
||||
document.addEventListener("visibilitychange", function onVisibilityChanged() {
|
||||
document.removeEventListener("visibilitychange", onVisibilityChanged);
|
||||
sendAsyncMessage("Social:DocumentVisible");
|
||||
});
|
||||
break;
|
||||
case "WaitForDOMContentLoaded":
|
||||
if (gDOMContentLoaded) {
|
||||
sendAsyncMessage("DOMContentLoaded");
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function(event) {
|
||||
sendAsyncMessage("Social:CustomEvent", {
|
||||
name: event.type
|
||||
});
|
||||
},
|
||||
|
||||
setErrorPage() {
|
||||
// if this is about:providerdirectory, use the directory iframe
|
||||
let frame = docShell.chromeEventHandler;
|
||||
|
@ -27,33 +27,51 @@
|
||||
<xul:toolbarbutton anonid="close" class="chat-close-button chat-toolbarbutton"
|
||||
oncommand="document.getBindingParent(this).close();"/>
|
||||
</xul:hbox>
|
||||
<xul:browser anonid="remote-content" class="chat-frame" flex="1"
|
||||
context="contentAreaContextMenu"
|
||||
disableglobalhistory="true"
|
||||
frameType="social"
|
||||
message="true"
|
||||
messagemanagergroup="social"
|
||||
tooltip="aHTMLTooltip"
|
||||
remote="true"
|
||||
xbl:inherits="src,origin"
|
||||
type="content"/>
|
||||
|
||||
<xul:browser anonid="content" class="chat-frame" flex="1"
|
||||
context="contentAreaContextMenu"
|
||||
disableglobalhistory="true"
|
||||
message="true"
|
||||
messagemanagergroup="social"
|
||||
tooltip="aHTMLTooltip"
|
||||
xbl:inherits="src,origin" type="content"/>
|
||||
context="contentAreaContextMenu"
|
||||
disableglobalhistory="true"
|
||||
message="true"
|
||||
messagemanagergroup="social"
|
||||
tooltip="aHTMLTooltip"
|
||||
xbl:inherits="src,origin"
|
||||
type="content"/>
|
||||
</content>
|
||||
|
||||
<implementation implements="nsIDOMEventListener">
|
||||
<implementation implements="nsIDOMEventListener, nsIMessageListener">
|
||||
<constructor><![CDATA[
|
||||
const kAnchorMap = new Map([
|
||||
["", "notification-"],
|
||||
["webRTC-shareScreen-", ""],
|
||||
["webRTC-sharingScreen-", ""]
|
||||
]);
|
||||
for (let [getterPrefix, idPrefix] of kAnchorMap) {
|
||||
let getter = getterPrefix + "popupnotificationanchor";
|
||||
let anonid = (idPrefix || getterPrefix) + "icon";
|
||||
this.content.__defineGetter__(getter, () => {
|
||||
delete this.content[getter];
|
||||
return this.content[getter] = document.getAnonymousElementByAttribute(
|
||||
this, "anonid", anonid);
|
||||
});
|
||||
const kBrowsers = [
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "content"),
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "remote-content")
|
||||
];
|
||||
for (let content of kBrowsers) {
|
||||
for (let [getterPrefix, idPrefix] of kAnchorMap) {
|
||||
let getter = getterPrefix + "popupnotificationanchor";
|
||||
let anonid = (idPrefix || getterPrefix) + "icon";
|
||||
content.__defineGetter__(getter, () => {
|
||||
delete content[getter];
|
||||
return content[getter] = document.getAnonymousElementByAttribute(
|
||||
this, "anonid", anonid);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let contentWindow = this.contentWindow;
|
||||
let mm = this.content.messageManager;
|
||||
// process this._callbacks, then set to null so the chatbox creator
|
||||
// knows to make new callbacks immediately.
|
||||
if (this._callbacks) {
|
||||
@ -62,17 +80,18 @@
|
||||
}
|
||||
this._callbacks = null;
|
||||
}
|
||||
this.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) {
|
||||
if (event.target != this.contentDocument)
|
||||
return;
|
||||
this.removeEventListener("DOMContentLoaded", DOMContentLoaded, true);
|
||||
|
||||
mm.addMessageListener("DOMTitleChanged", this);
|
||||
|
||||
mm.sendAsyncMessage("WaitForDOMContentLoaded");
|
||||
mm.addMessageListener("DOMContentLoaded", function DOMContentLoaded(event) {
|
||||
mm.removeMessageListener("DOMContentLoaded", DOMContentLoaded);
|
||||
this.isActive = !this.minimized;
|
||||
this._chat.loadButtonSet(this, this.getAttribute("buttonSet"));
|
||||
this._deferredChatLoaded.resolve(this);
|
||||
}, true);
|
||||
}.bind(this));
|
||||
|
||||
if (this.src)
|
||||
this.setAttribute("src", this.src);
|
||||
this.setActiveBrowser();
|
||||
]]></constructor>
|
||||
|
||||
<field name="_deferredChatLoaded" readonly="true">
|
||||
@ -85,26 +104,17 @@
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<field name="content" readonly="true">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "content");
|
||||
</field>
|
||||
<property name="content">
|
||||
<getter>
|
||||
return document.getAnonymousElementByAttribute(this, "anonid",
|
||||
(this.remote ? "remote-" : "") + "content");
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<field name="_chat" readonly="true">
|
||||
Cu.import("resource:///modules/Chat.jsm", {}).Chat;
|
||||
</field>
|
||||
|
||||
<property name="contentWindow">
|
||||
<getter>
|
||||
return this.content.contentWindow;
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="contentDocument">
|
||||
<getter>
|
||||
return this.content.contentDocument;
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<property name="minimized">
|
||||
<getter>
|
||||
return this.getAttribute("minimized") == "true";
|
||||
@ -143,12 +153,41 @@
|
||||
this.content.docShellIsActive = !!val;
|
||||
|
||||
// let the chat frame know if it is being shown or hidden
|
||||
let evt = this.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(val ? "socialFrameShow" : "socialFrameHide", true, true, {});
|
||||
this.contentDocument.documentElement.dispatchEvent(evt);
|
||||
this.content.messageManager.sendAsyncMessage("Social:CustomEvent", {
|
||||
name: val ? "socialFrameShow" : "socialFrameHide"
|
||||
});
|
||||
</setter>
|
||||
</property>
|
||||
|
||||
<field name="_remote">false</field>
|
||||
<property name="remote" onget="return this._remote;">
|
||||
<setter><![CDATA[
|
||||
this._remote = !!val;
|
||||
|
||||
this.setActiveBrowser();
|
||||
]]></setter>
|
||||
</property>
|
||||
|
||||
<method name="setActiveBrowser">
|
||||
<body><![CDATA[
|
||||
// Make sure we only show one browser element at a time.
|
||||
let content = document.getAnonymousElementByAttribute(this, "anonid", "content");
|
||||
let remoteContent = document.getAnonymousElementByAttribute(this, "anonid", "remote-content");
|
||||
remoteContent.setAttribute("hidden", !this.remote);
|
||||
content.setAttribute("hidden", this.remote);
|
||||
remoteContent.removeAttribute("src");
|
||||
content.removeAttribute("src");
|
||||
|
||||
if (this.src) {
|
||||
this.setAttribute("src", this.src);
|
||||
|
||||
// Stop loading of the document - that is set before this method was
|
||||
// called - in the now hidden browser.
|
||||
(this.remote ? content : remoteContent).setAttribute("src", "about:blank");
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="showNotifications">
|
||||
<parameter name="aAnchor"/>
|
||||
<body><![CDATA[
|
||||
@ -160,12 +199,14 @@
|
||||
<method name="swapDocShells">
|
||||
<parameter name="aTarget"/>
|
||||
<body><![CDATA[
|
||||
aTarget.setAttribute("label", this.contentDocument.title);
|
||||
aTarget.setAttribute("label", this.content.contentTitle);
|
||||
|
||||
aTarget.remote = this.remote;
|
||||
aTarget.src = this.src;
|
||||
aTarget.content.setAttribute("origin", this.content.getAttribute("origin"));
|
||||
aTarget.content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
|
||||
aTarget.content.swapDocShells(this.content);
|
||||
let content = aTarget.content;
|
||||
content.setAttribute("origin", this.content.getAttribute("origin"));
|
||||
content.popupnotificationanchor.className = this.content.popupnotificationanchor.className;
|
||||
content.swapDocShells(this.content);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -209,7 +250,9 @@
|
||||
if (this.chatbar) {
|
||||
this.chatbar.detachChatbox(this, { "centerscreen": "yes" }).then(
|
||||
chatbox => {
|
||||
chatbox.contentWindow.document.title = title;
|
||||
chatbox.content.messageManager.sendAsyncMessage("Social:SetDocumentTitle", {
|
||||
title: title
|
||||
});
|
||||
deferred.resolve(chatbox);
|
||||
}
|
||||
);
|
||||
@ -219,11 +262,16 @@
|
||||
let win = Chat.findChromeWindowForChats();
|
||||
let chatbar = win.document.getElementById("pinnedchats");
|
||||
let origin = this.content.getAttribute("origin");
|
||||
let cb = chatbar.openChat(origin, title, "about:blank");
|
||||
this.setDecorationAttributes(cb);
|
||||
let cb = chatbar.openChat({
|
||||
origin: origin,
|
||||
title: title,
|
||||
url: "about:blank"
|
||||
});
|
||||
|
||||
cb.promiseChatLoaded.then(
|
||||
() => {
|
||||
this.setDecorationAttributes(cb);
|
||||
|
||||
this.swapDocShells(cb);
|
||||
|
||||
chatbar.focus();
|
||||
@ -236,11 +284,9 @@
|
||||
chatbar.chatboxForURL.delete("about:blank");
|
||||
chatbar.chatboxForURL.set(this.src, Cu.getWeakReference(cb));
|
||||
|
||||
let attachEvent = new cb.contentWindow.CustomEvent("socialFrameAttached", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
cb.content.messageManager.sendAsyncMessage("Social:CustomEvent", {
|
||||
name: "socialFrameAttached"
|
||||
});
|
||||
cb.contentDocument.dispatchEvent(attachEvent);
|
||||
|
||||
deferred.resolve(cb);
|
||||
}
|
||||
@ -255,6 +301,27 @@
|
||||
this.minimized = !this.minimized;
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="setTitle">
|
||||
<body><![CDATA[
|
||||
try {
|
||||
this.setAttribute("label", this.content.contentTitle);
|
||||
} catch (ex) {}
|
||||
if (this.chatbar)
|
||||
this.chatbar.updateTitlebar(this);
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
<method name="receiveMessage">
|
||||
<parameter name="aMessage" />
|
||||
<body><![CDATA[
|
||||
switch (aMessage.name) {
|
||||
case "DOMTitleChanged":
|
||||
this.setTitle();
|
||||
break;
|
||||
}
|
||||
]]></body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
@ -262,14 +329,13 @@
|
||||
if (this.chatbar)
|
||||
this.chatbar.selectedChat = this;
|
||||
</handler>
|
||||
<handler event="DOMTitleChanged"><![CDATA[
|
||||
this.setAttribute('label', this.contentDocument.title);
|
||||
if (this.chatbar)
|
||||
this.chatbar.updateTitlebar(this);
|
||||
]]></handler>
|
||||
<handler event="DOMTitleChanged">
|
||||
this.setTitle();
|
||||
</handler>
|
||||
<handler event="DOMLinkAdded"><![CDATA[
|
||||
// much of this logic is from DOMLinkHandler in browser.js
|
||||
// this sets the presence icon for a chat user, we simply use favicon style updating
|
||||
// Much of this logic is from DOMLinkHandler in browser.js.
|
||||
// This sets the presence icon for a chat user, we simply use favicon
|
||||
// style updating.
|
||||
let link = event.originalTarget;
|
||||
let rel = link.rel && link.rel.toLowerCase();
|
||||
if (!link || !link.ownerDocument || !rel || !link.href)
|
||||
@ -277,15 +343,17 @@
|
||||
if (link.rel.indexOf("icon") < 0)
|
||||
return;
|
||||
|
||||
let ContentLinkHandler = Cu.import("resource:///modules/ContentLinkHandler.jsm", {}).ContentLinkHandler;
|
||||
let ContentLinkHandler = Cu.import("resource:///modules/ContentLinkHandler.jsm", {})
|
||||
.ContentLinkHandler;
|
||||
let uri = ContentLinkHandler.getLinkIconURI(link);
|
||||
if (!uri)
|
||||
return;
|
||||
|
||||
// we made it this far, use it
|
||||
this.setAttribute('image', uri.spec);
|
||||
// We made it this far, use it.
|
||||
this.setAttribute("image", uri.spec);
|
||||
if (this.chatbar)
|
||||
this.chatbar.updateTitlebar(this);
|
||||
break;
|
||||
]]></handler>
|
||||
<handler event="transitionend">
|
||||
if (this.isActive == this.minimized)
|
||||
@ -328,7 +396,7 @@
|
||||
<body><![CDATA[
|
||||
if (!this.selectedChat)
|
||||
return;
|
||||
Services.focus.focusedWindow = this.selectedChat.contentWindow;
|
||||
this.selectedChat.content.messageManager.sendAsyncMessage("Social:EnsureFocus");
|
||||
]]></body>
|
||||
</method>
|
||||
|
||||
@ -499,7 +567,7 @@
|
||||
aChatbox.isActive = false;
|
||||
let menu = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
|
||||
menu.setAttribute("class", "menuitem-iconic");
|
||||
menu.setAttribute("label", aChatbox.contentDocument.title);
|
||||
menu.setAttribute("label", aChatbox.content.contentTitle);
|
||||
menu.setAttribute("image", aChatbox.getAttribute("image"));
|
||||
menu.chat = aChatbox;
|
||||
this.menuitemMap.set(aChatbox, menu);
|
||||
@ -568,18 +636,16 @@
|
||||
</method>
|
||||
|
||||
<method name="openChat">
|
||||
<parameter name="aOrigin"/>
|
||||
<parameter name="aTitle"/>
|
||||
<parameter name="aURL"/>
|
||||
<parameter name="aMode"/>
|
||||
<parameter name="aOptions"/>
|
||||
<parameter name="aCallback"/>
|
||||
<body><![CDATA[
|
||||
let cb = this.chatboxForURL.get(aURL);
|
||||
let {origin, title, url, mode} = aOptions;
|
||||
let cb = this.chatboxForURL.get(url);
|
||||
if (cb && (cb = cb.get())) {
|
||||
// A chatbox is still alive to us when it's parented and still has
|
||||
// content.
|
||||
if (cb.parentNode && cb.contentWindow) {
|
||||
this.showChat(cb, aMode);
|
||||
if (cb.parentNode) {
|
||||
this.showChat(cb, mode);
|
||||
if (aCallback) {
|
||||
if (cb._callbacks == null) {
|
||||
// Chatbox has already been created, so callback now.
|
||||
@ -591,7 +657,7 @@
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
this.chatboxForURL.delete(aURL);
|
||||
this.chatboxForURL.delete(url);
|
||||
}
|
||||
cb = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "chatbox");
|
||||
cb._callbacks = [];
|
||||
@ -600,15 +666,17 @@
|
||||
// must exist before the (possibly delayed) bindings are created.
|
||||
cb._callbacks.push(aCallback);
|
||||
}
|
||||
|
||||
cb.remote = !!aOptions.remote;
|
||||
// src also a javascript property; the src attribute is set in the ctor.
|
||||
cb.src = aURL;
|
||||
if (aMode == "minimized")
|
||||
cb.src = url;
|
||||
if (mode == "minimized")
|
||||
cb.setAttribute("minimized", "true");
|
||||
cb.setAttribute("origin", aOrigin);
|
||||
cb.setAttribute("label", aTitle);
|
||||
cb.setAttribute("origin", origin);
|
||||
cb.setAttribute("label", title);
|
||||
this.insertBefore(cb, this.firstChild);
|
||||
this.selectedChat = cb;
|
||||
this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
|
||||
this.chatboxForURL.set(url, Cu.getWeakReference(cb));
|
||||
this.resize();
|
||||
return cb;
|
||||
]]></body>
|
||||
@ -714,21 +782,18 @@
|
||||
setAttribute("customSize", aChatbox.getAttribute("customSize"));
|
||||
}
|
||||
|
||||
let document = aChatbox.contentDocument;
|
||||
let detachEvent = new aChatbox.contentWindow.CustomEvent("socialFrameDetached", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
|
||||
otherWin.removeEventListener("load", _chatLoad, true);
|
||||
let otherChatbox = otherWin.document.getElementById("chatter");
|
||||
aChatbox.setDecorationAttributes(otherChatbox);
|
||||
aChatbox.swapDocShells(otherChatbox);
|
||||
|
||||
aChatbox.close();
|
||||
chatbar.chatboxForURL.set(aChatbox.src, Cu.getWeakReference(otherChatbox));
|
||||
|
||||
// All processing is done, now we can fire the event.
|
||||
document.dispatchEvent(detachEvent);
|
||||
otherChatbox.content.messageManager.sendAsyncMessage("Social:CustomEvent", {
|
||||
name: "socialFrameDetached"
|
||||
});
|
||||
|
||||
deferred.resolve(otherChatbox);
|
||||
}, true);
|
||||
|
@ -28,7 +28,13 @@ function promiseOpenChat(url, mode, focus, buttonSet = null) {
|
||||
deferred.resolve(chatbox);
|
||||
}, true);
|
||||
}
|
||||
let chatbox = Chat.open(null, origin, title, url, mode, focus, callback);
|
||||
let chatbox = Chat.open(null, {
|
||||
origin: origin,
|
||||
title: title,
|
||||
url: url,
|
||||
mode: mode,
|
||||
focus: focus
|
||||
}, callback);
|
||||
if (buttonSet) {
|
||||
chatbox.setAttribute("buttonSet", buttonSet);
|
||||
}
|
||||
@ -42,7 +48,12 @@ function promiseOpenChatCallback(url, mode) {
|
||||
let title = origin;
|
||||
let deferred = Promise.defer();
|
||||
let callback = deferred.resolve;
|
||||
Chat.open(null, origin, title, url, mode, undefined, callback);
|
||||
Chat.open(null, {
|
||||
origin: origin,
|
||||
title: title,
|
||||
url: url,
|
||||
mode: mode
|
||||
}, callback);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
|
@ -40,13 +40,12 @@ function openChatViaWorkerMessage(port, data, callback) {
|
||||
// so the child has been added, but we don't know if it
|
||||
// has been intialized - re-request it and the callback
|
||||
// means it's done. Minimized, same as the worker.
|
||||
chatbar.openChat(SocialSidebar.provider.origin,
|
||||
SocialSidebar.provider.name,
|
||||
data,
|
||||
"minimized",
|
||||
function() {
|
||||
callback();
|
||||
});
|
||||
chatbar.openChat({
|
||||
origin: SocialSidebar.provider.origin,
|
||||
title: SocialSidebar.provider.name,
|
||||
url: data,
|
||||
mode: "minimized"
|
||||
}, function() { callback(); });
|
||||
},
|
||||
"No new chat appeared");
|
||||
}
|
||||
|
@ -92,27 +92,35 @@ var Chat = {
|
||||
*
|
||||
* @param contentWindow [optional]
|
||||
* The content window that requested this chat. May be null.
|
||||
* @param origin
|
||||
* @param options
|
||||
* Object that may contain the following properties:
|
||||
* - origin
|
||||
* The origin for the chat. This is primarily used as an identifier
|
||||
* to help identify all chats from the same provider.
|
||||
* @param title
|
||||
* - title
|
||||
* The title to be used if a new chat window is created.
|
||||
* @param url
|
||||
* - 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]
|
||||
* - mode [optional]
|
||||
* May be undefined or 'minimized'
|
||||
* @param focus [optional]
|
||||
* - 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)
|
||||
|
||||
* - remote [optional]
|
||||
* Indicates if the chatbox browser should use the remote bindings
|
||||
* to run in the content process when TRUE.
|
||||
* @param callback
|
||||
* Function to be invoked once the chat constructed. The chatbox binding
|
||||
* is passed as the first argument.
|
||||
*
|
||||
* @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) {
|
||||
open: function(contentWindow, options, callback) {
|
||||
let chromeWindow = this.findChromeWindowForChats(contentWindow);
|
||||
if (!chromeWindow) {
|
||||
Cu.reportError("Failed to open a chat window - no host window could be found.");
|
||||
@ -121,18 +129,25 @@ var Chat = {
|
||||
|
||||
let chatbar = chromeWindow.document.getElementById("pinnedchats");
|
||||
chatbar.hidden = false;
|
||||
let chatbox = chatbar.openChat(origin, title, url, mode, callback);
|
||||
if (options.remote) {
|
||||
// Double check that current window can handle remote browser elements.
|
||||
let browser = chromeWindow.gBrowser && chromeWindow.gBrowser.selectedBrowser;
|
||||
if (!browser || browser.getAttribute("remote") != "true") {
|
||||
options.remote = false;
|
||||
}
|
||||
}
|
||||
let chatbox = chatbar.openChat(options, 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) {
|
||||
if (!("focus" in options)) {
|
||||
let dwu = chromeWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
focus = dwu.isHandlingUserInput;
|
||||
options.focus = dwu.isHandlingUserInput;
|
||||
}
|
||||
if (focus) {
|
||||
if (options.focus) {
|
||||
chatbar.focus();
|
||||
}
|
||||
return chatbox;
|
||||
|
@ -81,6 +81,11 @@ var PanelFrameInternal = {
|
||||
attrs["message"] = "true";
|
||||
attrs["messagemanagergroup"] = aType;
|
||||
}
|
||||
if (aType == "loop") {
|
||||
attrs.message = true;
|
||||
attrs.messagemanagergroup = "social";
|
||||
attrs.autocompletepopup = "PopupAutoComplete";
|
||||
}
|
||||
for (let [k, v] of Iterator(attrs)) {
|
||||
frame.setAttribute(k, v);
|
||||
}
|
||||
@ -127,6 +132,9 @@ var PanelFrame = {
|
||||
let notificationFrameId = aToolbarButton.getAttribute("notificationFrameId");
|
||||
let notificationFrame = aWindow.document.getElementById(notificationFrameId);
|
||||
|
||||
// the xbl bindings for the iframe probably don't exist yet, so we can't
|
||||
// access iframe.messageManager directly - but can get at it with this dance.
|
||||
let mm = notificationFrame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
|
||||
|
||||
// Clear dimensions on all browsers so the panel size will
|
||||
// only use the selected browser.
|
||||
@ -137,9 +145,7 @@ var PanelFrame = {
|
||||
}
|
||||
|
||||
function dispatchPanelEvent(name) {
|
||||
let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
|
||||
evt.initCustomEvent(name, true, true, {});
|
||||
notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
|
||||
mm.sendAsyncMessage("Social:CustomEvent", { name: name });
|
||||
}
|
||||
|
||||
// we only use a dynamic resizer when we're located the toolbar.
|
||||
@ -152,35 +158,28 @@ var PanelFrame = {
|
||||
anchorBtn.removeAttribute("open");
|
||||
if (dynamicResizer)
|
||||
dynamicResizer.stop();
|
||||
notificationFrame.docShell.isActive = false;
|
||||
notificationFrame.docShellIsActive = 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);
|
||||
}
|
||||
|
||||
mm.sendAsyncMessage("WaitForDOMContentLoaded");
|
||||
mm.addMessageListener("DOMContentLoaded", function onloaded() {
|
||||
mm.removeMessageListener("DOMContentLoaded", onloaded);
|
||||
mm = notificationFrame.messageManager;
|
||||
notificationFrame.docShellIsActive = true;
|
||||
if (dynamicResizer)
|
||||
dynamicResizer.start(panel, notificationFrame);
|
||||
dispatchPanelEvent(aType + "FrameShow");
|
||||
});
|
||||
});
|
||||
|
||||
let anchor = aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-icon");
|
||||
|
@ -242,6 +242,17 @@ function attachToWindow(provider, targetWindow) {
|
||||
}
|
||||
|
||||
function hookWindowCloseForPanelClose(targetWindow) {
|
||||
let _mozSocialDOMWindowClose;
|
||||
|
||||
if ("messageManager" in targetWindow) {
|
||||
let mm = targetWindow.messageManager;
|
||||
mm.sendAsyncMessage("Social:HookWindowCloseForPanelClose");
|
||||
mm.addMessageListener("DOMWindowClose", _mozSocialDOMWindowClose = function() {
|
||||
closePanel(targetWindow);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// We allow window.close() to close the panel, so add an event handler for
|
||||
// this, then cancel the event (so the window itself doesn't die) and
|
||||
// close the panel instead.
|
||||
@ -251,21 +262,12 @@ function hookWindowCloseForPanelClose(targetWindow) {
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
dwu.allowScriptsToClose();
|
||||
|
||||
targetWindow.addEventListener("DOMWindowClose", function _mozSocialDOMWindowClose(evt) {
|
||||
targetWindow.addEventListener("DOMWindowClose", _mozSocialDOMWindowClose = function(evt) {
|
||||
let elt = targetWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.chromeEventHandler;
|
||||
while (elt) {
|
||||
if (elt.localName == "panel") {
|
||||
elt.hidePopup();
|
||||
break;
|
||||
} else if (elt.localName == "chatbox") {
|
||||
elt.close();
|
||||
break;
|
||||
}
|
||||
elt = elt.parentNode;
|
||||
}
|
||||
closePanel(elt);
|
||||
// preventDefault stops the default window.close() function being called,
|
||||
// which doesn't actually close anything but causes things to get into
|
||||
// a bad state (an internal 'closed' flag is set and debug builds start
|
||||
@ -277,6 +279,19 @@ function hookWindowCloseForPanelClose(targetWindow) {
|
||||
}, true);
|
||||
}
|
||||
|
||||
function closePanel(elt) {
|
||||
while (elt) {
|
||||
if (elt.localName == "panel") {
|
||||
elt.hidePopup();
|
||||
break;
|
||||
} else if (elt.localName == "chatbox") {
|
||||
elt.close();
|
||||
break;
|
||||
}
|
||||
elt = elt.parentNode;
|
||||
}
|
||||
}
|
||||
|
||||
function schedule(callback) {
|
||||
Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
@ -298,11 +313,15 @@ this.openChatWindow =
|
||||
return;
|
||||
}
|
||||
|
||||
let chatbox = Chat.open(contentWindow, provider.origin, provider.name,
|
||||
fullURI.spec, mode);
|
||||
let chatbox = Chat.open(contentWindow, {
|
||||
origin: provider.origin,
|
||||
title: provider.name,
|
||||
url: fullURI.spec,
|
||||
mode: mode
|
||||
});
|
||||
if (callback) {
|
||||
chatbox.promiseChatLoaded.then(() => {
|
||||
callback(chatbox.contentWindow);
|
||||
callback(chatbox);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -599,7 +599,7 @@ var outerWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
var initData = sendSyncMessage("Browser:Init", {outerWindowID: outerWindowID});
|
||||
if (initData.length) {
|
||||
if (initData.length && initData[0]) {
|
||||
docShell.useGlobalHistory = initData[0].useGlobalHistory;
|
||||
if (initData[0].initPopup) {
|
||||
setTimeout(() => AutoCompletePopup.init(), 0);
|
||||
|
@ -1207,8 +1207,13 @@
|
||||
if (window.PopupNotifications)
|
||||
PopupNotifications._swapBrowserNotifications(aOtherBrowser, this);
|
||||
|
||||
this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner)
|
||||
.swapFrameLoaders(aOtherBrowser);
|
||||
try {
|
||||
this.QueryInterface(Components.interfaces.nsIFrameLoaderOwner)
|
||||
.swapFrameLoaders(aOtherBrowser);
|
||||
} catch(ex) {
|
||||
// This may not be implemented for browser elements that are not
|
||||
// attached to a BrowserDOMWindow.
|
||||
}
|
||||
|
||||
// Before we swap the actual docShell property we need to detach the
|
||||
// form fill controller from those docShells.
|
||||
@ -1237,8 +1242,10 @@
|
||||
this._remoteWebNavigationImpl.swapBrowser(this);
|
||||
aOtherBrowser._remoteWebNavigationImpl.swapBrowser(aOtherBrowser);
|
||||
|
||||
this._remoteWebProgressManager.swapBrowser(this);
|
||||
aOtherBrowser._remoteWebProgressManager.swapBrowser(aOtherBrowser);
|
||||
if (this._remoteWebProgressManager && aOtherBrowser._remoteWebProgressManager) {
|
||||
this._remoteWebProgressManager.swapBrowser(this);
|
||||
aOtherBrowser._remoteWebProgressManager.swapBrowser(aOtherBrowser);
|
||||
}
|
||||
|
||||
if (this._remoteFinder)
|
||||
this._remoteFinder.swapBrowser(this);
|
||||
|
@ -390,7 +390,12 @@
|
||||
return;
|
||||
this.mDestroyed = true;
|
||||
|
||||
this.controllers.removeController(this._controller);
|
||||
try {
|
||||
this.controllers.removeController(this._controller);
|
||||
} catch (ex) {
|
||||
// This can fail when this browser element is not attached to a
|
||||
// BrowserDOMWindow.
|
||||
}
|
||||
|
||||
if (!this.hasAttribute("disablehistory")) {
|
||||
try {
|
||||
|
Loading…
Reference in New Issue
Block a user