зеркало из https://github.com/mozilla/gecko-dev.git
Bug 775965 - Ensure presentation persists across nsSubDocumentFrame reframes. r=roc
This commit is contained in:
Родитель
d704c5b5a0
Коммит
469a28a015
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<nsFrameMessageManager> mMessageManager;
|
||||
nsCOMPtr<nsIInProcessContentFrameMessageManager> 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<nsIDocument> mContainerDocWhileDetached;
|
||||
|
||||
bool mDepthTooGreat : 1;
|
||||
bool mIsTopLevelContent : 1;
|
||||
bool mDestroyCalled : 1;
|
||||
|
|
|
@ -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<nsIWidget::Configuration> 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<nsIWidget::Configuration> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<nsFrameLoader> frameloader = FrameLoader();
|
||||
if (frameloader) {
|
||||
nsCOMPtr<nsIDocument> 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<nsIContent> mFrameElement;
|
||||
nsRefPtr<nsFrameLoader> mFrameLoader;
|
||||
nsCOMPtr<nsIPresShell> 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()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=775965
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 775965</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body onload="run();">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=775965">Mozilla Bug 775965</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
<iframe id="iframe1" src="data:text/html,<embed type='application/x-test' width='200' height='200'></embed>"></iframe>
|
||||
<iframe id="iframe2"></iframe>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 775965 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function run() {
|
||||
var iframe1 = document.getElementById("iframe1");
|
||||
var iframe2 = document.getElementById("iframe2");
|
||||
iframe1.parentNode.removeChild(iframe1);
|
||||
iframe1.style.position = "fixed";
|
||||
iframe2.contentDocument.body.appendChild(iframe1);
|
||||
setTimeout(function() { ok(true, "We didn't crash!"); SimpleTest.finish(); }, 0);
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче