HTML - support for elements: <details> and <summary> (basic)

This commit is contained in:
janekptacijarabaci
2017-04-13 19:23:21 +02:00
committed by roytam1
parent 4363da021c
commit 3e466656b8
119 changed files with 3027 additions and 190 deletions
+141 -4
View File
@@ -13,7 +13,9 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/HTMLDetailsElement.h"
#include "mozilla/dom/HTMLSelectElement.h"
#include "mozilla/dom/HTMLSummaryElement.h"
#include "mozilla/EventStates.h"
#include "mozilla/Likely.h"
#include "mozilla/LinkedList.h"
@@ -87,6 +89,8 @@
#include "nsSimplePageSequenceFrame.h"
#include "nsTableOuterFrame.h"
#include "nsIScrollableFrame.h"
#include "DetailsFrame.h"
#include "SummaryFrame.h"
#ifdef MOZ_XUL
#include "nsIRootBox.h"
@@ -938,7 +942,7 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShe
mAdditionalStateBits(nsFrameState(0)),
// If the fixed-pos containing block is equal to the abs-pos containing
// block, use the abs-pos containing block's abs-pos list for fixed-pos
// frames.
// frames.
mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
mHavePendingPopupgroup(false),
mCreatingExtraFrames(false),
@@ -972,7 +976,7 @@ nsFrameConstructorState::nsFrameConstructorState(nsIPresShell* aPresShell,
mAdditionalStateBits(nsFrameState(0)),
// If the fixed-pos containing block is equal to the abs-pos containing
// block, use the abs-pos containing block's abs-pos list for fixed-pos
// frames.
// frames.
mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
mHavePendingPopupgroup(false),
mCreatingExtraFrames(false),
@@ -3298,6 +3302,60 @@ nsCSSFrameConstructor::ConstructFieldSetFrame(nsFrameConstructorState& aState,
return fieldsetFrame;
}
nsIFrame*
nsCSSFrameConstructor::ConstructDetailsFrame(nsFrameConstructorState& aState,
FrameConstructionItem& aItem,
nsContainerFrame* aParentFrame,
const nsStyleDisplay* aStyleDisplay,
nsFrameItems& aFrameItems)
{
nsIContent* const content = aItem.mContent;
nsStyleContext* const styleContext = aItem.mStyleContext;
nsContainerFrame* geometricParent =
aState.GetGeometricParent(aStyleDisplay, aParentFrame);
nsContainerFrame* detailsFrame = NS_NewDetailsFrame(mPresShell, styleContext);
nsIFrame* frameToReturn = nullptr;
// Build a scroll frame to wrap details frame if necessary.
if (aStyleDisplay->IsScrollableOverflow()) {
nsContainerFrame* scrollFrame = nullptr;
nsRefPtr<nsStyleContext> detailsStyle =
BeginBuildingScrollFrame(aState, content, styleContext, geometricParent ,
nsCSSAnonBoxes::scrolledContent, false,
scrollFrame);
aState.AddChild(scrollFrame, aFrameItems, content, styleContext,
aParentFrame);
nsFrameItems scrollFrameItems;
ConstructBlock(aState, aStyleDisplay, content,
scrollFrame, scrollFrame,
detailsStyle, &detailsFrame, scrollFrameItems,
aStyleDisplay->IsPositioned(scrollFrame) ?
scrollFrame : nullptr,
aItem.mPendingBinding);
MOZ_ASSERT(scrollFrameItems.OnlyChild() == detailsFrame);
FinishBuildingScrollFrame(scrollFrame, detailsFrame);
frameToReturn = scrollFrame;
} else {
ConstructBlock(aState, aStyleDisplay, content, geometricParent,
aParentFrame, styleContext,
&detailsFrame, aFrameItems,
aStyleDisplay->IsPositioned(detailsFrame) ?
detailsFrame : nullptr,
aItem.mPendingBinding);
frameToReturn = detailsFrame;
}
return frameToReturn;
}
static nsIFrame*
FindAncestorWithGeneratedContentPseudo(nsIFrame* aFrame)
{
@@ -3489,6 +3547,19 @@ nsCSSFrameConstructor::FindHTMLData(Element* aElement,
return nullptr;
}
if (aTag == nsGkAtoms::details || aTag == nsGkAtoms::summary) {
if (!HTMLDetailsElement::IsDetailsEnabled()) {
return nullptr;
}
}
if (aTag == nsGkAtoms::summary &&
(!aParentFrame || aParentFrame->GetType() != nsGkAtoms::detailsFrame)) {
// <summary> is special only if it is a direct child of <details>. If it
// isn't, construct it as a normal block frame instead of a summary frame.
return nullptr;
}
static const FrameConstructionDataByTag sHTMLData[] = {
SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
@@ -3518,7 +3589,11 @@ nsCSSFrameConstructor::FindHTMLData(Element* aElement,
SIMPLE_TAG_CREATE(video, NS_NewHTMLVideoFrame),
SIMPLE_TAG_CREATE(audio, NS_NewHTMLVideoFrame),
SIMPLE_TAG_CREATE(progress, NS_NewProgressFrame),
SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame)
SIMPLE_TAG_CREATE(meter, NS_NewMeterFrame),
COMPLEX_TAG_CREATE(details, &nsCSSFrameConstructor::ConstructDetailsFrame),
{ &nsGkAtoms::summary,
FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME,
NS_NewSummaryFrame) }
};
return FindDataByTag(aTag, aElement, aStyleContext, sHTMLData,
@@ -5544,6 +5619,18 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState
}
}
// When constructing a child of a non-open <details>, create only the frame
// for the main <summary> element, and skip other elements.
auto* details = HTMLDetailsElement::FromContentOrNull(parent);
if (details && !details->Open()) {
auto* summary = HTMLSummaryElement::FromContentOrNull(aContent);
if (!summary || !summary->IsMainSummary()) {
SetAsUndisplayedContent(aState, aItems, aContent, styleContext,
isGeneratedContent);
return;
}
}
bool isPopup = false;
// Try to find frame construction data for this content
const FrameConstructionData* data;
@@ -6605,6 +6692,14 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion,
// before the AdjustAppendParentForAfterContent call.
aInsertion->mParentFrame =
nsLayoutUtils::LastContinuationWithChild(aInsertion->mParentFrame);
// We should never get here with fieldsets or details, since they have
// multiple insertion points.
MOZ_ASSERT(aInsertion->mParentFrame->GetType()
!= nsGkAtoms::fieldSetFrame &&
aInsertion->mParentFrame->GetType() != nsGkAtoms::detailsFrame,
"Parent frame should not be fieldset or details!");
// Deal with fieldsets
aInsertion->mParentFrame =
::GetAdjustedParentFrame(aInsertion->mParentFrame,
@@ -7605,6 +7700,22 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
return rv;
}
// We should only get here with details when doing a single insertion because
// we treat details frame as if it has multiple insertion points.
MOZ_ASSERT(isSingleInsert || frameType != nsGkAtoms::detailsFrame);
if (frameType == nsGkAtoms::detailsFrame) {
// When inserting an element into <details>, just reframe the details frame
// and let it figure out where the element should be laid out. It might seem
// expensive to recreate the entire details frame, but it's the simplest way
// to handle the insertion.
LAYOUT_PHASE_TEMP_EXIT();
nsresult rv =
RecreateFramesForContent(insertion.mParentFrame->GetContent(), false,
REMOVE_FOR_RECONSTRUCTION, nullptr);
LAYOUT_PHASE_TEMP_REENTER();
return rv;
}
// Don't construct kids of leaves
if (insertion.mParentFrame->IsLeaf()) {
// Clear lazy bits so we don't try to construct again.
@@ -8760,6 +8871,12 @@ nsCSSFrameConstructor::CreateContinuingFrame(nsPresContext* aPresContext,
} else if (nsGkAtoms::rubyTextContainerFrame == frameType) {
newFrame = NS_NewRubyTextContainerFrame(shell, styleContext);
newFrame->Init(content, aParentFrame, aFrame);
} else if (nsGkAtoms::detailsFrame == frameType) {
newFrame = NS_NewDetailsFrame(shell, styleContext);
newFrame->Init(content, aParentFrame, aFrame);
} else if (nsGkAtoms::summaryFrame == frameType) {
newFrame = NS_NewSummaryFrame(shell, styleContext);
newFrame->Init(content, aParentFrame, aFrame);
} else {
NS_RUNTIMEABORT("unexpected frame type");
}
@@ -8928,6 +9045,13 @@ nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer,
insertion.mMultiple = true;
}
// A details frame moves the first summary frame to be its first child, so we
// treat it as if it has multiple content insertion points.
if (insertion.mParentFrame &&
insertion.mParentFrame->GetType() == nsGkAtoms::detailsFrame) {
insertion.mMultiple = true;
}
return insertion;
}
@@ -9079,6 +9203,17 @@ nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(nsIFrame* aFrame,
return true;
}
if (insertionFrame && insertionFrame->GetType() == nsGkAtoms::summaryFrame &&
aFrame->GetParent()->GetType() == nsGkAtoms::detailsFrame) {
// When removing a summary frame, we should reframe the parent details frame
// to ensure that another summary is used or the default summary is
// generated.
*aResult = RecreateFramesForContent(aFrame->GetParent()->GetContent(),
false, REMOVE_FOR_RECONSTRUCTION,
aDestroyedFramesFor);
return true;
}
// Now check for possibly needing to reconstruct due to a pseudo parent
nsIFrame* inFlowFrame =
(aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) ?
@@ -11399,7 +11534,9 @@ nsCSSFrameConstructor::ConstructBlock(nsFrameConstructorState& aState,
{
// Create column wrapper if necessary
nsContainerFrame* blockFrame = *aNewFrame;
NS_ASSERTION(blockFrame->GetType() == nsGkAtoms::blockFrame, "not a block frame?");
NS_ASSERTION((blockFrame->GetType() == nsGkAtoms::blockFrame ||
blockFrame->GetType() == nsGkAtoms::detailsFrame),
"not a block frame nor a details frame?");
nsContainerFrame* parent = aParentFrame;
nsRefPtr<nsStyleContext> blockStyle = aStyleContext;
const nsStyleColumn* columns = aStyleContext->StyleColumn();