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 {