diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 475dabee605..154d76f36a3 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -288,6 +288,7 @@ NS_INTERFACE_MAP_END nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated) : mOwnerContent(aOwner) + , mDetachedSubdocViews(nullptr) , mDepthTooGreat(false) , mIsTopLevelContent(false) , mDestroyCalled(false) @@ -2377,3 +2378,18 @@ nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent) ShowRemoteFrame(nsIntSize(0, 0)); } +void +nsFrameLoader::SetDetachedSubdocView(nsIView* aDetachedViews, + nsIDocument* aContainerDoc) +{ + mDetachedSubdocViews = aDetachedViews; + mContainerDocWhileDetached = aContainerDoc; +} + +nsIView* +nsFrameLoader::GetDetachedSubdocView(nsIDocument** aContainerDoc) const +{ + NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached); + return mDetachedSubdocViews; +} + diff --git a/content/base/src/nsFrameLoader.h b/content/base/src/nsFrameLoader.h index 7a3e497da31..3a83efcfb56 100644 --- a/content/base/src/nsFrameLoader.h +++ b/content/base/src/nsFrameLoader.h @@ -268,6 +268,25 @@ public: */ void SetRemoteBrowser(nsITabParent* aTabParent); + /** + * Stashes a detached view on the frame loader. We do this when we're + * destroying the nsSubDocumentFrame. If the nsSubdocumentFrame is + * being reframed we'll restore the detached view when it's recreated, + * otherwise we'll discard the old presentation and set the detached + * subdoc view to null. aContainerDoc is the document containing the + * the subdoc frame. This enables us to detect when the containing + * document has changed during reframe, so we can discard the presentation + * in that case. + */ + void SetDetachedSubdocView(nsIView* aDetachedView, + nsIDocument* aContainerDoc); + + /** + * Retrieves the detached view and the document containing the view, + * as set by SetDetachedSubdocView(). + */ + nsIView* GetDetachedSubdocView(nsIDocument** aContainerDoc) const; + private: void SetOwnerContent(mozilla::dom::Element* aContent); @@ -326,6 +345,16 @@ public: nsRefPtr mMessageManager; nsCOMPtr mChildMessageManager; private: + // Stores the root view of the subdocument while the subdocument is being + // reframed. Used to restore the presentation after reframing. + nsIView* mDetachedSubdocViews; + // Stores the containing document of the frame corresponding to this + // frame loader. This is reference is kept valid while the subframe's + // presentation is detached and stored in mDetachedSubdocViews. This + // enables us to detect whether the frame has moved documents during + // a reframe, so that we know not to restore the presentation. + nsCOMPtr mContainerDocWhileDetached; + bool mDepthTooGreat : 1; bool mIsTopLevelContent : 1; bool mDestroyCalled : 1; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 2c4b8eb7144..8b39ab038f6 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -738,32 +738,26 @@ void nsObjectFrame::SetInstanceOwner(nsPluginInstanceOwner* aOwner) { mInstanceOwner = aOwner; - if (!mInstanceOwner) { - nsRootPresContext* rpc = PresContext()->GetRootPresContext(); - if (rpc) { - if (mWidget) { - if (mInnerView) { - mInnerView->DetachWidgetEventHandler(mWidget); + if (mInstanceOwner) { + return; + } + nsRootPresContext* rpc = PresContext()->GetRootPresContext(); + if (rpc) { + rpc->UnregisterPluginForGeometryUpdates(mContent); + } + if (mWidget && mInnerView) { + mInnerView->DetachWidgetEventHandler(mWidget); + // Make sure the plugin is hidden in case an update of plugin geometry + // hasn't happened since this plugin became hidden. + nsIWidget* parent = mWidget->GetParent(); + if (parent) { + nsTArray configurations; + this->GetEmptyClipConfiguration(&configurations); + parent->ConfigureChildren(configurations); - rpc->UnregisterPluginForGeometryUpdates(mContent); - // Make sure the plugin is hidden in case an update of plugin geometry - // hasn't happened since this plugin became hidden. - nsIWidget* parent = mWidget->GetParent(); - if (parent) { - nsTArray configurations; - this->GetEmptyClipConfiguration(&configurations); - parent->ConfigureChildren(configurations); - - mWidget->Show(false); - mWidget->Enable(false); - mWidget->SetParent(nullptr); - } - } - } else { -#ifndef XP_MACOSX - rpc->UnregisterPluginForGeometryUpdates(mContent); -#endif - } + mWidget->Show(false); + mWidget->Enable(false); + mWidget->SetParent(nullptr); } } } diff --git a/layout/generic/nsSubDocumentFrame.cpp b/layout/generic/nsSubDocumentFrame.cpp index 47dcce3a298..f867f782f39 100644 --- a/layout/generic/nsSubDocumentFrame.cpp +++ b/layout/generic/nsSubDocumentFrame.cpp @@ -117,6 +117,12 @@ private: nsWeakFrame mFrame; }; +static void +InsertViewsInReverseOrder(nsIView* aSibling, nsIView* aParent); + +static void +EndSwapDocShellsForViews(nsIView* aView); + NS_IMETHODIMP nsSubDocumentFrame::Init(nsIContent* aContent, nsIFrame* aParent, @@ -146,6 +152,28 @@ nsSubDocumentFrame::Init(nsIContent* aContent, } EnsureInnerView(); + // If we have a detached subdoc's root view on our frame loader, re-insert + // it into the view tree. This happens when we've been reframed, and + // ensures the presentation persists across reframes. If the frame element + // has changed documents however, we blow away the presentation. + nsRefPtr frameloader = FrameLoader(); + if (frameloader) { + nsCOMPtr oldContainerDoc; + nsIView* detachedViews = + frameloader->GetDetachedSubdocView(getter_AddRefs(oldContainerDoc)); + if (detachedViews) { + if (oldContainerDoc == aContent->OwnerDoc()) { + // Restore stashed presentation. + ::InsertViewsInReverseOrder(detachedViews, mInnerView); + ::EndSwapDocShellsForViews(mInnerView->GetFirstChild()); + } else { + // Presentation is for a different document, don't restore it. + frameloader->Hide(); + } + } + frameloader->SetDetachedSubdocView(nullptr, nullptr); + } + // Set the primary frame now so that // DocumentViewerImpl::FindContainerView called by ShowViewer below // can find it if necessary. @@ -758,6 +786,49 @@ NS_NewSubDocumentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame) +class nsHideViewer : public nsRunnable { +public: + nsHideViewer(nsIContent* aFrameElement, + nsFrameLoader* aFrameLoader, + nsIPresShell* aPresShell, + bool aHideViewerIfFrameless) + : mFrameElement(aFrameElement), + mFrameLoader(aFrameLoader), + mPresShell(aPresShell), + mHideViewerIfFrameless(aHideViewerIfFrameless) + { + NS_ASSERTION(mFrameElement, "Must have a frame element"); + NS_ASSERTION(mFrameLoader, "Must have a frame loader"); + NS_ASSERTION(mPresShell, "Must have a presshell"); + } + + NS_IMETHOD Run() + { + // Flush frames, to ensure any pending display:none changes are made. + // Note it can be unsafe to flush if we've destroyed the presentation + // for some other reason, like if we're shutting down. + if (!mPresShell->IsDestroying()) { + mPresShell->FlushPendingNotifications(Flush_Frames); + } + nsIFrame* frame = mFrameElement->GetPrimaryFrame(); + if (!frame && mHideViewerIfFrameless) { + // The frame element has no nsIFrame. Hide the nsFrameLoader, + // which destroys the presentation. + mFrameLoader->SetDetachedSubdocView(nullptr, nullptr); + mFrameLoader->Hide(); + } + return NS_OK; + } +private: + nsCOMPtr mFrameElement; + nsRefPtr mFrameLoader; + nsCOMPtr mPresShell; + bool mHideViewerIfFrameless; +}; + +static nsIView* +BeginSwapDocShellsForViews(nsIView* aSibling); + void nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot) { @@ -765,19 +836,27 @@ nsSubDocumentFrame::DestroyFrom(nsIFrame* aDestructRoot) PresContext()->PresShell()->CancelReflowCallback(this); mPostedReflowCallback = false; } - - HideViewer(); + + // Detach the subdocument's views and stash them in the frame loader. + // We can then reattach them if we're being reframed (for example if + // the frame has been made position:fixed). + nsFrameLoader* frameloader = FrameLoader(); + if (frameloader) { + nsIView* detachedViews = ::BeginSwapDocShellsForViews(mInnerView->GetFirstChild()); + frameloader->SetDetachedSubdocView(detachedViews, mContent->OwnerDoc()); + + // We call nsFrameLoader::HideViewer() in a script runner so that we can + // safely determine whether the frame is being reframed or destroyed. + nsContentUtils::AddScriptRunner( + new nsHideViewer(mContent, + mFrameLoader, + PresContext()->PresShell(), + (mDidCreateDoc || mCallingShow))); + } nsLeafFrame::DestroyFrom(aDestructRoot); } -void -nsSubDocumentFrame::HideViewer() -{ - if (mFrameLoader && (mDidCreateDoc || mCallingShow)) - mFrameLoader->Hide(); -} - nsIntSize nsSubDocumentFrame::GetMarginAttributes() { diff --git a/layout/generic/nsSubDocumentFrame.h b/layout/generic/nsSubDocumentFrame.h index 0a7bfd8683f..67b2a64055c 100644 --- a/layout/generic/nsSubDocumentFrame.h +++ b/layout/generic/nsSubDocumentFrame.h @@ -119,8 +119,9 @@ protected: virtual int GetSkipSides() const; - // Hide or show our document viewer - void HideViewer(); + // Show our document viewer. The document viewer is hidden via a script + // runner, so that we can save and restore the presentation if we're + // being reframed. void ShowViewer(); /* Obtains the frame we should use for intrinsic size information if we are diff --git a/layout/generic/test/Makefile.in b/layout/generic/test/Makefile.in index fb820609fbe..b27aa6c7de0 100644 --- a/layout/generic/test/Makefile.in +++ b/layout/generic/test/Makefile.in @@ -14,6 +14,7 @@ include $(DEPTH)/config/autoconf.mk # in the list below, we make sure that the tests that require focus # run before test_plugin_clipping, which can steal focus for its window. MOCHITEST_FILES = \ + test_contained_plugin_transplant.html \ bug344830_testembed.svg \ plugin_clipping_helper.xhtml \ plugin_clipping_helper2.xhtml \ diff --git a/layout/generic/test/test_contained_plugin_transplant.html b/layout/generic/test/test_contained_plugin_transplant.html new file mode 100644 index 00000000000..6901cff1007 --- /dev/null +++ b/layout/generic/test/test_contained_plugin_transplant.html @@ -0,0 +1,40 @@ + + + + + + Test for Bug 775965 + + + + +Mozilla Bug 775965 +

+
+ + + + +
+
+
+
+ +