diff --git a/browser/base/content/browser-social.js b/browser/base/content/browser-social.js
index fac49738e0b..0e39b1c7d94 100644
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -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();
diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css
index 21fdd61b508..4218427ade0 100644
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -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");
}
diff --git a/browser/base/content/social-content.js b/browser/base/content/social-content.js
index 01d6e0ae299..3aebcc2f6e6 100644
--- a/browser/base/content/social-content.js
+++ b/browser/base/content/social-content.js
@@ -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;
diff --git a/browser/base/content/socialchat.xml b/browser/base/content/socialchat.xml
index 81bbb80aa89..f025cb29d99 100644
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -27,33 +27,51 @@
+
+
+ context="contentAreaContextMenu"
+ disableglobalhistory="true"
+ message="true"
+ messagemanagergroup="social"
+ tooltip="aHTMLTooltip"
+ xbl:inherits="src,origin"
+ type="content"/>
-
+
{
- 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();
]]>
@@ -85,26 +104,17 @@
-
- document.getAnonymousElementByAttribute(this, "anonid", "content");
-
+
+
+ return document.getAnonymousElementByAttribute(this, "anonid",
+ (this.remote ? "remote-" : "") + "content");
+
+
Cu.import("resource:///modules/Chat.jsm", {}).Chat;
-
-
- return this.content.contentWindow;
-
-
-
-
-
- return this.content.contentDocument;
-
-
-
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"
+ });
+ false
+
+
+
+
+
+
+
+
@@ -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;
]]>
+
+
+
+
+
+
+
+
+
@@ -262,14 +329,13 @@
if (this.chatbar)
this.chatbar.selectedChat = this;
-
+
+ this.setTitle();
+
if (this.isActive == this.minimized)
@@ -328,7 +396,7 @@
@@ -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 @@
-
-
-
-
+
@@ -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);
diff --git a/browser/base/content/test/chat/head.js b/browser/base/content/test/chat/head.js
index 87c331b1664..908e79b2c80 100644
--- a/browser/base/content/test/chat/head.js
+++ b/browser/base/content/test/chat/head.js
@@ -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;
}
diff --git a/browser/base/content/test/social/browser_social_chatwindowfocus.js b/browser/base/content/test/social/browser_social_chatwindowfocus.js
index ad69fe9033c..3261776f5d8 100644
--- a/browser/base/content/test/social/browser_social_chatwindowfocus.js
+++ b/browser/base/content/test/social/browser_social_chatwindowfocus.js
@@ -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");
}
diff --git a/browser/modules/Chat.jsm b/browser/modules/Chat.jsm
index 6a827c1e026..bd8744f4566 100644
--- a/browser/modules/Chat.jsm
+++ b/browser/modules/Chat.jsm
@@ -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;
diff --git a/browser/modules/PanelFrame.jsm b/browser/modules/PanelFrame.jsm
index ddb3fd14d53..5c95cd92e86 100644
--- a/browser/modules/PanelFrame.jsm
+++ b/browser/modules/PanelFrame.jsm
@@ -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");
diff --git a/toolkit/components/social/MozSocialAPI.jsm b/toolkit/components/social/MozSocialAPI.jsm
index c7c4b397ed0..b8eb469d334 100644
--- a/toolkit/components/social/MozSocialAPI.jsm
+++ b/toolkit/components/social/MozSocialAPI.jsm
@@ -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);
});
}
}
diff --git a/toolkit/content/browser-child.js b/toolkit/content/browser-child.js
index afde520b39e..bbf5223203c 100644
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -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);
diff --git a/toolkit/content/widgets/browser.xml b/toolkit/content/widgets/browser.xml
index 14d7b64f4d9..cfdba987976 100644
--- a/toolkit/content/widgets/browser.xml
+++ b/toolkit/content/widgets/browser.xml
@@ -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);
diff --git a/toolkit/content/widgets/remote-browser.xml b/toolkit/content/widgets/remote-browser.xml
index 2f926eaf5ce..9c74bfce504 100644
--- a/toolkit/content/widgets/remote-browser.xml
+++ b/toolkit/content/widgets/remote-browser.xml
@@ -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 {