gecko-dev/layout/generic/DetailsFrame.cpp
Bobby Holley 8fb4fb3d6c Bug 1393791 - Stop unbinding native-anonymous content off a script runner. r=emilio
The failure mode in the attached crashtest is an inconsistency in the flattened
tree. Specifically, we null out mVideoControls in an nsVideoFrame, but defer
the UnbindFromTree call on that NAC element, which measn that its mParent still
points to the nsVideoFrame's mContent. Because all this stuff runs off of script
runners, and the anonymous content destroyer is not guaranteed to run before
other potential script runners, we end up running arbitrary script while the
tree mismatch exists. This script calls back into ProcessPendingRestyles, which
causes trouble.

We could build a separate deferral mechanism, but it's not clear that we actually
need to defer the unbind anymore. The deferred unbind was added in bug 489008,
which predated a lot of simplifications in layout/dom interaction.

MozReview-Commit-ID: 1JYAhiXKVJC
2017-08-27 15:29:36 -07:00

139 lines
3.8 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DetailsFrame.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/HTMLDetailsElement.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "nsContentUtils.h"
#include "nsPlaceholderFrame.h"
#include "nsTextNode.h"
using namespace mozilla;
using namespace mozilla::dom;
NS_IMPL_FRAMEARENA_HELPERS(DetailsFrame)
NS_QUERYFRAME_HEAD(DetailsFrame)
NS_QUERYFRAME_ENTRY(DetailsFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
nsBlockFrame*
NS_NewDetailsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) DetailsFrame(aContext);
}
namespace mozilla {
DetailsFrame::DetailsFrame(nsStyleContext* aContext)
: nsBlockFrame(aContext, kClassID)
{
}
DetailsFrame::~DetailsFrame()
{
}
void
DetailsFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList)
{
#ifdef DEBUG
if (aListID == kPrincipalList) {
CheckValidMainSummary(aChildList);
}
#endif
nsBlockFrame::SetInitialChildList(aListID, aChildList);
}
#ifdef DEBUG
bool
DetailsFrame::CheckValidMainSummary(const nsFrameList& aFrameList) const
{
for (nsIFrame* child : aFrameList) {
HTMLSummaryElement* summary =
HTMLSummaryElement::FromContent(child->GetContent());
if (child == aFrameList.FirstChild()) {
if (summary && summary->IsMainSummary()) {
return true;
} else if (child->GetContent() == GetContent()) {
// The child frame's content is the same as our content, which means
// it's a kind of wrapper frame. Descend into its child list to find
// main summary.
if (CheckValidMainSummary(child->PrincipalChildList())) {
return true;
}
}
} else {
NS_ASSERTION(!summary || !summary->IsMainSummary(),
"Rest of the children are either not summary element "
"or are not the main summary!");
}
}
return false;
}
#endif
void
DetailsFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
DestroyAnonymousContent(mDefaultSummary.forget());
nsBlockFrame::DestroyFrom(aDestructRoot);
}
nsresult
DetailsFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
{
auto* details = HTMLDetailsElement::FromContent(GetContent());
if (details->GetFirstSummary()) {
return NS_OK;
}
// The <details> element lacks any direct <summary> child. Create a default
// <summary> element as an anonymous content.
nsNodeInfoManager* nodeInfoManager =
GetContent()->NodeInfo()->NodeInfoManager();
already_AddRefed<NodeInfo> nodeInfo =
nodeInfoManager->GetNodeInfo(nsGkAtoms::summary, nullptr, kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);
mDefaultSummary = new HTMLSummaryElement(nodeInfo);
nsAutoString defaultSummaryText;
nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
"DefaultSummary", defaultSummaryText);
RefPtr<nsTextNode> description = new nsTextNode(nodeInfoManager);
description->SetText(defaultSummaryText, false);
mDefaultSummary->AppendChildTo(description, false);
aElements.AppendElement(mDefaultSummary);
return NS_OK;
}
void
DetailsFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter)
{
if (mDefaultSummary) {
aElements.AppendElement(mDefaultSummary);
}
}
bool
DetailsFrame::HasMainSummaryFrame(nsIFrame* aSummaryFrame)
{
nsIFrame* firstChild =
nsPlaceholderFrame::GetRealFrameFor(mFrames.FirstChild());
return aSummaryFrame == firstChild;
}
} // namespace mozilla