diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp index fddd62d689b..4d3e58e8b2e 100644 --- a/dom/base/nsFrameLoader.cpp +++ b/dom/base/nsFrameLoader.cpp @@ -275,6 +275,42 @@ nsFrameLoader::LoadURI(nsIURI* aURI) return rv; } +NS_IMETHODIMP +nsFrameLoader::SwitchProcessAndLoadURI(nsIURI* aURI) +{ + nsCOMPtr URIToLoad = aURI; + nsRefPtr tp = nullptr; + + MutableTabContext context; + nsCOMPtr ownApp = GetOwnApp(); + nsCOMPtr containingApp = GetContainingApp(); + + bool tabContextUpdated = true; + if (ownApp) { + tabContextUpdated = context.SetTabContextForAppFrame(ownApp, containingApp); + } else if (OwnerIsBrowserFrame()) { + // The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp. + tabContextUpdated = context.SetTabContextForBrowserFrame(containingApp); + } else { + tabContextUpdated = context.SetTabContextForNormalFrame(); + } + NS_ENSURE_STATE(tabContextUpdated); + + nsCOMPtr ownerElement = mOwnerContent; + tp = ContentParent::CreateBrowserOrApp(context, ownerElement, nullptr); + if (!tp) { + return NS_ERROR_FAILURE; + } + mRemoteBrowserShown = false; + + nsresult rv = SwapRemoteBrowser(tp); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + LoadURI(URIToLoad); + return NS_OK; +} + NS_IMETHODIMP nsFrameLoader::SetIsPrerendered() { @@ -2603,6 +2639,57 @@ nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent) ShowRemoteFrame(ScreenIntSize(0, 0)); } +nsresult +nsFrameLoader::SwapRemoteBrowser(nsITabParent* aTabParent) +{ + nsRefPtr newParent = TabParent::GetFrom(aTabParent); + if (!newParent || !mRemoteBrowser) { + return NS_ERROR_DOM_INVALID_STATE_ERR; + } + if (!IsRemoteFrame()) { + NS_WARNING("Switching from in-process to out-of-process is not supported."); + return NS_ERROR_NOT_IMPLEMENTED; + } + if (!OwnerIsBrowserOrAppFrame()) { + NS_WARNING("Switching process for non-mozbrowser/app frame is not supported."); + return NS_ERROR_NOT_IMPLEMENTED; + } + if (newParent == mRemoteBrowser) { + return NS_OK; + } + nsCOMPtr os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "frameloader-message-manager-will-change", nullptr); + } + + mRemoteBrowser->CacheFrameLoader(nullptr); + mRemoteBrowser->SetOwnerElement(nullptr); + mRemoteBrowser->Detach(); + mRemoteBrowser->Destroy(); + + if (mMessageManager) { + mMessageManager->Disconnect(); + mMessageManager = nullptr; + } + + mRemoteBrowser = newParent; + mRemoteBrowser->Attach(this); + mChildID = mRemoteBrowser->Manager()->ChildID(); + ReallyLoadFrameScripts(); + InitializeBrowserAPI(); + + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "frameloader-message-manager-changed", nullptr); + } + if (!mRemoteBrowserShown) { + ShowRemoteFrame(ScreenIntSize(0, 0)); + } + + return NS_OK; +} + void nsFrameLoader::SetDetachedSubdocView(nsView* aDetachedViews, nsIDocument* aContainerDoc) diff --git a/dom/base/nsFrameLoader.h b/dom/base/nsFrameLoader.h index fb0d7df54f6..80bccfaa4cc 100644 --- a/dom/base/nsFrameLoader.h +++ b/dom/base/nsFrameLoader.h @@ -181,6 +181,8 @@ public: */ void SetRemoteBrowser(nsITabParent* aTabParent); + nsresult SwapRemoteBrowser(nsITabParent* aTabParent); + /** * Stashes a detached view on the frame loader. We do this when we're * destroying the nsSubDocumentFrame. If the nsSubdocumentFrame is diff --git a/dom/base/nsIFrameLoader.idl b/dom/base/nsIFrameLoader.idl index 0d571309023..f993b8f07ae 100644 --- a/dom/base/nsIFrameLoader.idl +++ b/dom/base/nsIFrameLoader.idl @@ -16,7 +16,7 @@ interface nsIDOMElement; interface nsITabParent; interface nsILoadContext; -[scriptable, builtinclass, uuid(d24f9330-ae4e-11e4-ab27-0800200c9a66)] +[scriptable, builtinclass, uuid(c6e00815-b7a1-4544-b309-a85b86cb1747)] interface nsIFrameLoader : nsISupports { /** @@ -49,6 +49,14 @@ interface nsIFrameLoader : nsISupports */ void loadURI(in nsIURI aURI); + /** + * Loads the specified URI in this frame but using a different process. + * Behaves identically to loadURI, except that this method only works + * with remote frame. + * Throws an exception with non-remote frames. + */ + void switchProcessAndLoadURI(in nsIURI aURI); + /** * Puts the frameloader in prerendering mode. */ diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index e8023134bc8..47bbc6ca575 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -840,3 +840,5 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g' [test_postMessages.html] support-files = worker_postMessages.js [test_window_proto.html] +[test_frameLoader_switchProcess.html] +skip-if = e10s || os != 'linux' || buildapp != 'browser' diff --git a/dom/base/test/test_frameLoader_switchProcess.html b/dom/base/test/test_frameLoader_switchProcess.html new file mode 100644 index 00000000000..1b96d323d7f --- /dev/null +++ b/dom/base/test/test_frameLoader_switchProcess.html @@ -0,0 +1,84 @@ + + + + Test frameLoader SwitchProcessAndLoadURI + + + + + + + + diff --git a/dom/browser-element/BrowserElementParent.js b/dom/browser-element/BrowserElementParent.js index 4856bcfd338..3c5d9aa7d77 100644 --- a/dom/browser-element/BrowserElementParent.js +++ b/dom/browser-element/BrowserElementParent.js @@ -77,6 +77,8 @@ function BrowserElementParent() { Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true); Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true); Services.obs.addObserver(this, 'ask-children-to-execute-copypaste-command', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'frameloader-message-manager-will-change', /* ownsWeak = */ true); + Services.obs.addObserver(this, 'frameloader-message-manager-changed', /* ownsWeak = */ true); } BrowserElementParent.prototype = { @@ -161,10 +163,17 @@ BrowserElementParent.prototype = { _setupMessageListener: function() { this._mm = this._frameLoader.messageManager; - let self = this; - let isWidget = this._frameLoader - .QueryInterface(Ci.nsIFrameLoader) - .ownerIsWidget; + this._isWidget = this._frameLoader + .QueryInterface(Ci.nsIFrameLoader) + .ownerIsWidget; + this._mm.addMessageListener('browser-element-api:call', this); + this._mm.loadFrameScript("chrome://global/content/extensions.js", true); + }, + + receiveMessage: function(aMsg) { + if (!this._isAlive()) { + return; + } // Messages we receive are handed to functions which take a (data) argument, // where |data| is the message manager's data object. @@ -222,20 +231,15 @@ BrowserElementParent.prototype = { "opentab": this._fireEventFromMsg }; - this._mm.addMessageListener('browser-element-api:call', function(aMsg) { - if (!self._isAlive()) { - return; - } + if (aMsg.data.msg_name in mmCalls) { + return mmCalls[aMsg.data.msg_name].apply(this, arguments); + } else if (!this._isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) { + return mmSecuritySensitiveCalls[aMsg.data.msg_name].apply(this, arguments); + } + }, - if (aMsg.data.msg_name in mmCalls) { - return mmCalls[aMsg.data.msg_name].apply(self, arguments); - } else if (!isWidget && aMsg.data.msg_name in mmSecuritySensitiveCalls) { - return mmSecuritySensitiveCalls[aMsg.data.msg_name] - .apply(self, arguments); - } - }); - - this._mm.loadFrameScript("chrome://global/content/extensions.js", true); + _removeMessageListener: function() { + this._mm.removeMessageListener('browser-element-api:call', this); }, /** @@ -1105,6 +1109,16 @@ BrowserElementParent.prototype = { this._sendAsyncMsg('copypaste-do-command', { command: data }); } break; + case 'frameloader-message-manager-will-change': + if (this._isAlive() && subject == this._frameLoader) { + this._removeMessageListener(); + } + break; + case 'frameloader-message-manager-changed': + if (this._isAlive() && subject == this._frameLoader) { + this._setupMessageListener(); + } + break; default: debug('Unknown topic: ' + topic); break; diff --git a/dom/html/nsBrowserElement.cpp b/dom/html/nsBrowserElement.cpp index 0bf2a46e72b..49993758e5f 100644 --- a/dom/html/nsBrowserElement.cpp +++ b/dom/html/nsBrowserElement.cpp @@ -66,10 +66,13 @@ nsBrowserElement::InitBrowserElementAPI() return; } - mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1"); - if (mBrowserElementAPI) { - mBrowserElementAPI->SetFrameLoader(frameLoader); + if (!mBrowserElementAPI) { + mBrowserElementAPI = do_CreateInstance("@mozilla.org/dom/browser-element-api;1"); + if (NS_WARN_IF(!mBrowserElementAPI)) { + return; + } } + mBrowserElementAPI->SetFrameLoader(frameLoader); } void diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 51a42098b82..3ada18cdb47 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -278,6 +278,7 @@ TabParent::TabParent(nsIContentParent* aManager, , mManager(aManager) , mMarkedDestroying(false) , mIsDestroyed(false) + , mIsDetached(true) , mAppPackageFileDescriptorSent(false) , mSendOfflineStatus(true) , mChromeFlags(aChromeFlags) @@ -481,6 +482,35 @@ TabParent::Destroy() mMarkedDestroying = true; } +void +TabParent::Detach() +{ + if (mIsDetached) { + return; + } + RemoveWindowListeners(); + if (RenderFrameParent* frame = GetRenderFrame()) { + RemoveTabParentFromTable(frame->GetLayersId()); + } + mIsDetached = true; +} + +void +TabParent::Attach(nsFrameLoader* aFrameLoader) +{ + MOZ_ASSERT(mIsDetached); + if (!mIsDetached) { + return; + } + Element* ownerElement = aFrameLoader->GetOwnerContent(); + SetOwnerElement(ownerElement); + if (RenderFrameParent* frame = GetRenderFrame()) { + AddTabParentToTable(frame->GetLayersId(), this); + frame->OwnerContentChanged(ownerElement); + } + mIsDetached = false; +} + bool TabParent::Recv__delete__() { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index ec895b95d65..6c9c906bc3d 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -122,6 +122,8 @@ public: nsIXULBrowserWindow* GetXULBrowserWindow(); void Destroy(); + void Detach(); + void Attach(nsFrameLoader* aFrameLoader); void RemoveWindowListeners(); void AddWindowListeners(); @@ -516,6 +518,8 @@ private: bool mMarkedDestroying; // When true, the TabParent is invalid and we should not send IPC messages anymore. bool mIsDestroyed; + // When true, the TabParent is detached from the frame loader. + bool mIsDetached; // Whether we have already sent a FileDescriptor for the app package. bool mAppPackageFileDescriptorSent;