Bug 1683748 - Support Grid/Flex/Table/Column layout for the rendered legend of a fieldset. r=emilio

Differential Revision: https://phabricator.services.mozilla.com/D101555
This commit is contained in:
Mats Palmgren 2021-01-30 13:47:10 +00:00
Родитель 92b6ff7e1b
Коммит f6ef0ea044
24 изменённых файлов: 209 добавлений и 280 удалений

Просмотреть файл

@ -103,6 +103,26 @@ bool HTMLLegendElement::PerformAccesskey(bool aKeyCausesActivation,
return NS_SUCCEEDED(rv.StealNSResult());
}
HTMLLegendElement::LegendAlignValue HTMLLegendElement::LogicalAlign(
mozilla::WritingMode aCBWM) const {
const nsAttrValue* attr = GetParsedAttr(nsGkAtoms::align);
if (!attr || attr->Type() != nsAttrValue::eEnum) {
return LegendAlignValue::InlineStart;
}
auto value = static_cast<LegendAlignValue>(attr->GetEnumValue());
switch (value) {
case LegendAlignValue::Left:
return aCBWM.IsBidiLTR() ? LegendAlignValue::InlineStart
: LegendAlignValue::InlineEnd;
case LegendAlignValue::Right:
return aCBWM.IsBidiLTR() ? LegendAlignValue::InlineEnd
: LegendAlignValue::InlineStart;
default:
return value;
}
}
already_AddRefed<HTMLFormElement> HTMLLegendElement::GetForm() {
return do_AddRef(GetFormElement());
}

Просмотреть файл

@ -57,6 +57,16 @@ class HTMLLegendElement final : public nsGenericHTMLElement {
InlineStart,
InlineEnd,
};
/**
* Return the align value to use for the given fieldset writing-mode.
* (This method resolves Left/Right to the appropriate InlineStart/InlineEnd).
* @param aCBWM the fieldset writing-mode
* @note we only parse left/right/center, so this method returns Center,
* InlineStart or InlineEnd.
*/
LegendAlignValue LogicalAlign(mozilla::WritingMode aCBWM) const;
/**
* WebIDL Interface
*/

Просмотреть файл

@ -702,6 +702,10 @@ class MOZ_STACK_CLASS nsFrameConstructorState {
// mode).
bool mCreatingExtraFrames;
// This keeps track of whether we have found a "rendered legend" for
// the current FieldSetFrame.
bool mHasRenderedLegend;
nsTArray<RefPtr<nsIContent>> mGeneratedContentWithInitializer;
// Constructor
@ -851,7 +855,8 @@ nsFrameConstructorState::nsFrameConstructorState(
// frames.
mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock),
mHavePendingPopupgroup(false),
mCreatingExtraFrames(false) {
mCreatingExtraFrames(false),
mHasRenderedLegend(false) {
#ifdef MOZ_XUL
nsIPopupContainer* popupContainer =
nsIPopupContainer::GetPopupContainer(aPresShell);
@ -3048,35 +3053,16 @@ nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
absoluteSaveState);
}
ProcessChildren(aState, content, computedStyle, contentFrame, true, childList,
true);
{
AutoRestore<bool> savedHasRenderedLegend(aState.mHasRenderedLegend);
aState.mHasRenderedLegend = false;
ProcessChildren(aState, content, computedStyle, contentFrame, true,
childList, true);
}
nsFrameList fieldsetKids;
fieldsetKids.AppendFrame(nullptr,
scrollFrame ? scrollFrame : contentFrameTop);
for (nsFrameList::Enumerator e(childList); !e.AtEnd(); e.Next()) {
nsIFrame* child = e.get();
nsContainerFrame* cif = child->GetContentInsertionFrame();
if (cif && cif->IsLegendFrame()) {
// We want the legend to be the first frame in the fieldset child list.
// That way the EventStateManager will do the right thing when tabbing
// from a selection point within the legend (bug 236071), which is
// used for implementing legend access keys (bug 81481).
// GetAdjustedParentFrame() below depends on this frame order.
childList.RemoveFrame(child);
// Make sure to reparent the legend so it has the fieldset as the parent.
fieldsetKids.InsertFrame(fieldsetFrame, nullptr, child);
// Legend is no longer in the multicol container. Remove the bit.
child->RemoveStateBits(NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR);
if (scrollFrame) {
StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary(
child, contentFrame);
}
break;
}
}
if (!MayNeedToCreateColumnSpanSiblings(contentFrame, childList)) {
// Set the inner frame's initial child lists.
contentFrame->SetInitialChildList(kPrincipalList, childList);
@ -3102,10 +3088,10 @@ nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame(
FinishBuildingScrollFrame(scrollFrame, contentFrameTop);
}
// Set the outer frame's initial child list
fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids);
// We use AppendFrames here because the rendered legend will already
// be present in the principal child list if it exists.
fieldsetFrame->AppendFrames(nsIFrame::kNoReflowPrincipalList, fieldsetKids);
// Our new frame returned is the outer frame, which is the fieldset frame.
return fieldsetFrame;
}
@ -3329,25 +3315,11 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
nsIFrame* aParentFrame,
ComputedStyle& aStyle) {
MOZ_ASSERT(aElement.IsHTMLElement());
nsAtom* tag = aElement.NodeInfo()->NameAtom();
NS_ASSERTION(!aParentFrame ||
aParentFrame->Style()->GetPseudoType() !=
PseudoStyleType::fieldsetContent ||
aParentFrame->GetParent()->IsFieldSetFrame(),
"Unexpected parent for fieldset content anon box");
if (tag == nsGkAtoms::legend &&
(!aParentFrame || !IsFrameForFieldSet(aParentFrame) ||
aStyle.StyleDisplay()->IsFloatingStyle() ||
aStyle.StyleDisplay()->IsAbsolutelyPositionedStyle())) {
// <legend> is only special inside fieldset, we only check the frame tree
// parent because the content tree parent may not be a <fieldset> due to
// display:contents, or Shadow DOM. For floated or absolutely positioned
// legends we want to construct by display type and not do special legend
// stuff.
return nullptr;
}
static const FrameConstructionDataByTag sHTMLData[] = {
SIMPLE_TAG_CHAIN(img, nsCSSFrameConstructor::FindImgData),
SIMPLE_TAG_CHAIN(mozgeneratedcontentimage,
@ -3363,9 +3335,6 @@ nsCSSFrameConstructor::FindHTMLData(const Element& aElement,
SIMPLE_TAG_CHAIN(embed, nsCSSFrameConstructor::FindObjectData),
COMPLEX_TAG_CREATE(fieldset,
&nsCSSFrameConstructor::ConstructFieldSetFrame),
{nsGkAtoms::legend,
FCDATA_DECL(FCDATA_ALLOW_BLOCK_STYLES | FCDATA_MAY_NEED_SCROLLFRAME,
NS_NewLegendFrame)},
SIMPLE_TAG_CREATE(frameset, NS_NewHTMLFramesetFrame),
SIMPLE_TAG_CREATE(iframe, NS_NewSubDocumentFrame),
{nsGkAtoms::button,
@ -5248,6 +5217,17 @@ nsCSSFrameConstructor::FindElementData(const Element& aElement,
return &sImgData;
}
if (aFlags.contains(ItemFlag::IsForRenderedLegend) &&
!aStyle.StyleDisplay()->IsBlockOutsideStyle()) {
// Make a temp copy of StyleDisplay and blockify its mDisplay value.
auto display = *aStyle.StyleDisplay();
bool isRootElement = false;
uint16_t rawDisplayValue =
Servo_ComputedValues_BlockifiedDisplay(&aStyle, isRootElement);
display.mDisplay = StyleDisplay(rawDisplayValue);
return FindDisplayData(display, aElement);
}
const auto& display = *aStyle.StyleDisplay();
return FindDisplayData(display, aElement);
}
@ -5359,6 +5339,14 @@ void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(
return;
}
if (aContent->IsHTMLElement(nsGkAtoms::legend) && aParentFrame &&
IsFrameForFieldSet(aParentFrame) && !aState.mHasRenderedLegend &&
!aComputedStyle->StyleDisplay()->IsFloatingStyle() &&
!aComputedStyle->StyleDisplay()->IsAbsolutelyPositionedStyle()) {
aState.mHasRenderedLegend = true;
aFlags += ItemFlag::IsForRenderedLegend;
}
const FrameConstructionData* data =
FindDataForContent(*aContent, *aComputedStyle, aParentFrame, aFlags);
if (!data || data->mBits & FCDATA_SUPPRESS_FRAME) {
@ -5411,6 +5399,9 @@ void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(
if (!item) {
item = aItems.AppendItem(this, data, aContent, do_AddRef(aComputedStyle),
aSuppressWhiteSpaceOptimizations);
if (aFlags.contains(ItemFlag::IsForRenderedLegend)) {
item->mIsRenderedLegend = true;
}
}
item->mIsText = !aContent->IsElement();
item->mIsGeneratedContent = isGeneratedContent;
@ -5969,20 +5960,6 @@ bool nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
// below.
}
if (IsFrameForFieldSet(parentFrame)) {
// Legends can be sibling of legends but not of other content in the
// fieldset
if (nsContainerFrame* cif = aSibling->GetContentInsertionFrame()) {
aSibling = cif;
}
LayoutFrameType sibType = aSibling->Type();
bool legendContent = aContent->IsHTMLElement(nsGkAtoms::legend);
if ((legendContent && (LayoutFrameType::Legend != sibType)) ||
(!legendContent && (LayoutFrameType::Legend == sibType)))
return false;
}
return true;
}
@ -6070,6 +6047,10 @@ nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame(
return nullptr;
}
if (aSibling->IsRenderedLegend()) {
return nullptr;
}
if (aSibling->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
aSibling = aSibling->GetPlaceholderFrame();
MOZ_ASSERT(aSibling);
@ -7984,9 +7965,6 @@ nsIFrame* nsCSSFrameConstructor::CreateContinuingFrame(
} else if (LayoutFrameType::FieldSet == frameType) {
newFrame = NS_NewFieldSetFrame(mPresShell, computedStyle);
newFrame->Init(content, aParentFrame, aFrame);
} else if (LayoutFrameType::Legend == frameType) {
newFrame = NS_NewLegendFrame(mPresShell, computedStyle);
newFrame->Init(content, aParentFrame, aFrame);
} else if (LayoutFrameType::FlexContainer == frameType) {
newFrame = NS_NewFlexContainerFrame(mPresShell, computedStyle);
newFrame->Init(content, aParentFrame, aFrame);
@ -8245,12 +8223,9 @@ bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval(
return true;
}
nsContainerFrame* insertionFrame = aFrame->GetContentInsertionFrame();
if (insertionFrame && insertionFrame->IsLegendFrame() &&
aFrame->GetParent()->IsFieldSetFrame()) {
if (inFlowFrame->IsRenderedLegend()) {
TRACE("Fieldset / Legend");
RecreateFramesForContent(aFrame->GetParent()->GetContent(),
InsertionKind::Async);
RecreateFramesForContent(parent->GetContent(), InsertionKind::Async);
return true;
}
@ -9355,6 +9330,34 @@ inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
// that information offhand in many cases.
MOZ_ASSERT(ParentIsWrapperAnonBox(aParentFrame) == aParentIsWrapperAnonBox);
if (!aParentIsWrapperAnonBox && aState.mHasRenderedLegend &&
aParentFrame->GetContent()->IsHTMLElement(nsGkAtoms::fieldset)) {
DebugOnly<bool> found = false;
for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
if (iter.item().mIsRenderedLegend) {
// This makes the rendered legend the first frame in the fieldset child
// list which makes keyboard traversal follow the visual order.
nsContainerFrame* fieldSetFrame = aParentFrame->GetParent();
while (!fieldSetFrame->IsFieldSetFrame()) {
fieldSetFrame = fieldSetFrame->GetParent();
}
nsFrameList renderedLegend;
ConstructFramesFromItem(aState, iter, fieldSetFrame, renderedLegend);
MOZ_ASSERT(
renderedLegend.FirstChild() &&
renderedLegend.FirstChild() == renderedLegend.LastChild(),
"a rendered legend should have exactly one frame");
fieldSetFrame->SetInitialChildList(kPrincipalList, renderedLegend);
FCItemIterator next = iter;
next.Next();
iter.DeleteItemsTo(this, next);
found = true;
break;
}
}
MOZ_ASSERT(found, "should have found our rendered legend");
}
CreateNeededPseudoContainers(aState, aItems, aParentFrame);
CreateNeededAnonFlexOrGridItems(aState, aItems, aParentFrame);
CreateNeededPseudoInternalRubyBoxes(aState, aItems, aParentFrame);
@ -9362,6 +9365,9 @@ inline void nsCSSFrameConstructor::ConstructFramesFromItemList(
bool listItemListIsDirty = false;
for (FCItemIterator iter(aItems); !iter.IsDone(); iter.Next()) {
MOZ_ASSERT(!iter.item().mIsRenderedLegend,
"Only one item can be the rendered legend, "
"and it should've been handled above");
NS_ASSERTION(iter.item().DesiredParentType() == GetParentType(aParentFrame),
"Needed pseudos didn't get created; expect bad things");
// display:list-item boxes affects the start value of the "list-item"

Просмотреть файл

@ -375,6 +375,8 @@ class nsCSSFrameConstructor final : public nsFrameManager {
AllowTextPathChild,
// The item is content created by an nsIAnonymousContentCreator frame.
IsAnonymousContentCreatorContent,
// The item will be the rendered legend of a <fieldset>.
IsForRenderedLegend,
};
using ItemFlags = mozilla::EnumSet<ItemFlag>;
@ -1086,7 +1088,8 @@ class nsCSSFrameConstructor final : public nsFrameManager {
mIsAllInline(false),
mIsBlock(false),
mIsPopup(false),
mIsLineParticipant(false) {
mIsLineParticipant(false),
mIsRenderedLegend(false) {
MOZ_COUNT_CTOR(FrameConstructionItem);
}
@ -1160,6 +1163,8 @@ class nsCSSFrameConstructor final : public nsFrameManager {
bool mIsPopup : 1;
// Whether this item should be treated as a line participant
bool mIsLineParticipant : 1;
// Whether this item is the rendered legend of a <fieldset>
bool mIsRenderedLegend : 1;
private:
// Not allocated from the general heap - instead, use the new/Delete APIs

Просмотреть файл

@ -27,7 +27,6 @@ UNIFIED_SOURCES += [
"nsGfxButtonControlFrame.cpp",
"nsHTMLButtonControlFrame.cpp",
"nsImageControlFrame.cpp",
"nsLegendFrame.cpp",
"nsListControlFrame.cpp",
"nsMeterFrame.cpp",
"nsNumberControlFrame.cpp",

Просмотреть файл

@ -14,6 +14,7 @@
#include "mozilla/PresShell.h"
#include "mozilla/Maybe.h"
#include "mozilla/webrender/WebRenderAPI.h"
#include "nsBlockFrame.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSFrameConstructor.h"
#include "nsCSSRendering.h"
@ -21,7 +22,6 @@
#include "nsGkAtoms.h"
#include "nsIFrameInlines.h"
#include "nsLayoutUtils.h"
#include "nsLegendFrame.h"
#include "nsStyleConsts.h"
using namespace mozilla;
@ -445,8 +445,15 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
if (legend) {
const auto legendWM = legend->GetWritingMode();
LogicalSize legendAvailSize = availSize.ConvertTo(legendWM, wm);
ComputeSizeFlags sizeFlags;
if (legend->StylePosition()->ISize(wm).IsAuto()) {
sizeFlags = ComputeSizeFlag::ShrinkWrap;
}
ReflowInput::InitFlags initFlags; // intentionally empty
StyleSizeOverrides sizeOverrides; // intentionally empty
legendReflowInput.emplace(aPresContext, aReflowInput, legend,
legendAvailSize);
legendAvailSize, Nothing(), initFlags,
sizeOverrides, sizeFlags);
}
const bool avoidBreakInside = ShouldAvoidBreakInside(aReflowInput);
if (reflowLegend) {
@ -675,11 +682,10 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
if (innerContentRect.ISize(wm) > mLegendRect.ISize(wm)) {
// NOTE legend @align values are: left/right/center
// GetLogicalAlign converts left/right to start/end for the given WM.
// @see HTMLLegendElement::ParseAttribute, nsLegendFrame::GetLogicalAlign
LegendAlignValue align =
static_cast<nsLegendFrame*>(legend->GetContentInsertionFrame())
->GetLogicalAlign(wm);
switch (align) {
// @see HTMLLegendElement::ParseAttribute/LogicalAlign
auto* legendElement =
dom::HTMLLegendElement::FromNode(legend->GetContent());
switch (legendElement->LogicalAlign(wm)) {
case LegendAlignValue::InlineEnd:
mLegendRect.IStart(wm) =
innerContentRect.IEnd(wm) - mLegendRect.ISize(wm);
@ -789,18 +795,29 @@ void nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
}
#ifdef DEBUG
void nsFieldSetFrame::SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList) {
nsContainerFrame::SetInitialChildList(aListID, aChildList);
MOZ_ASSERT(aListID != kPrincipalList || GetInner(),
"Setting principal child list should populate our inner frame");
if (nsBlockFrame* legend = do_QueryFrame(GetLegend())) {
// A rendered legend always establish a new formatting context.
// https://html.spec.whatwg.org/multipage/rendering.html#rendered-legend
legend->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
}
MOZ_ASSERT(aListID != kPrincipalList || GetInner() || GetLegend(),
"Setting principal child list should populate our inner frame "
"or our rendered legend");
}
void nsFieldSetFrame::AppendFrames(ChildListID aListID,
nsFrameList& aFrameList) {
MOZ_CRASH("nsFieldSetFrame::AppendFrames not supported");
MOZ_ASSERT(aListID == kNoReflowPrincipalList &&
HasAnyStateBits(NS_FRAME_FIRST_REFLOW),
"AppendFrames should only be used from "
"nsCSSFrameConstructor::ConstructFieldSetFrame");
nsContainerFrame::AppendFrames(aListID, aFrameList);
MOZ_ASSERT(GetInner(), "at this point we should have an inner frame");
}
#ifdef DEBUG
void nsFieldSetFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
const nsLineList::iterator* aPrevFrameLine,
nsFrameList& aFrameList) {

Просмотреть файл

@ -49,11 +49,11 @@ class nsFieldSetFrame final : public nsContainerFrame {
gfxContext& aRenderingContext, nsPoint aPt,
const nsRect& aDirtyRect);
#ifdef DEBUG
virtual void SetInitialChildList(ChildListID aListID,
nsFrameList& aChildList) override;
virtual void AppendFrames(ChildListID aListID,
nsFrameList& aFrameList) override;
#ifdef DEBUG
virtual void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
const nsLineList::iterator* aPrevFrameLine,
nsFrameList& aFrameList) override;
@ -92,9 +92,8 @@ class nsFieldSetFrame final : public nsContainerFrame {
nsContainerFrame* GetInner() const;
/**
* Return the frame that represents the legend if any. This may be
* a nsLegendFrame or a nsHTMLScrollFrame with the nsLegendFrame as the
* scrolled frame (aka content insertion frame).
* Return the frame that represents the rendered legend if any.
* https://html.spec.whatwg.org/multipage/rendering.html#rendered-legend
*/
nsIFrame* GetLegend() const;

Просмотреть файл

@ -1,93 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "nsLegendFrame.h"
#include "mozilla/dom/HTMLLegendElement.h"
#include "mozilla/PresShell.h"
#include "ComputedStyle.h"
#include "nsIContent.h"
#include "nsGenericHTMLElement.h"
#include "nsAttrValueInlines.h"
#include "nsHTMLParts.h"
#include "nsGkAtoms.h"
#include "nsStyleConsts.h"
#include "nsCheckboxRadioFrame.h"
#include "WritingModes.h"
using namespace mozilla;
nsIFrame* NS_NewLegendFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
#ifdef DEBUG
const nsStyleDisplay* disp = aStyle->StyleDisplay();
NS_ASSERTION(!disp->IsAbsolutelyPositionedStyle() && !disp->IsFloatingStyle(),
"Legends should not be positioned and should not float");
#endif
nsIFrame* f =
new (aPresShell) nsLegendFrame(aStyle, aPresShell->GetPresContext());
f->AddStateBits(NS_BLOCK_FORMATTING_CONTEXT_STATE_BITS);
return f;
}
NS_IMPL_FRAMEARENA_HELPERS(nsLegendFrame)
void nsLegendFrame::DestroyFrom(nsIFrame* aDestructRoot,
PostDestroyData& aPostDestroyData) {
nsCheckboxRadioFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
nsBlockFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
}
NS_QUERYFRAME_HEAD(nsLegendFrame)
NS_QUERYFRAME_ENTRY(nsLegendFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
void nsLegendFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) {
DO_GLOBAL_REFLOW_COUNT("nsLegendFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
if (mState & NS_FRAME_FIRST_REFLOW) {
nsCheckboxRadioFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
}
return nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput,
aStatus);
}
dom::HTMLLegendElement::LegendAlignValue nsLegendFrame::GetLogicalAlign(
WritingMode aCBWM) {
using LegendAlignValue = dom::HTMLLegendElement::LegendAlignValue;
auto* element = nsGenericHTMLElement::FromNode(mContent);
if (!element) {
return LegendAlignValue::InlineStart;
}
const nsAttrValue* attr = element->GetParsedAttr(nsGkAtoms::align);
if (!attr || attr->Type() != nsAttrValue::eEnum) {
return LegendAlignValue::InlineStart;
}
auto value = static_cast<LegendAlignValue>(attr->GetEnumValue());
switch (value) {
case LegendAlignValue::Left:
return aCBWM.IsBidiLTR() ? LegendAlignValue::InlineStart
: LegendAlignValue::InlineEnd;
case LegendAlignValue::Right:
return aCBWM.IsBidiLTR() ? LegendAlignValue::InlineEnd
: LegendAlignValue::InlineStart;
default:
return value;
}
}
#ifdef DEBUG_FRAME_DUMP
nsresult nsLegendFrame::GetFrameName(nsAString& aResult) const {
return MakeFrameName(u"Legend"_ns, aResult);
}
#endif

Просмотреть файл

@ -1,37 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
#ifndef nsLegendFrame_h___
#define nsLegendFrame_h___
#include "mozilla/Attributes.h"
#include "mozilla/dom/HTMLLegendElement.h"
#include "nsBlockFrame.h"
class nsLegendFrame final : public nsBlockFrame {
public:
NS_DECL_QUERYFRAME
NS_DECL_FRAMEARENA_HELPERS(nsLegendFrame)
explicit nsLegendFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
: nsBlockFrame(aStyle, aPresContext, kClassID) {}
virtual void Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) override;
virtual void DestroyFrom(nsIFrame* aDestructRoot,
PostDestroyData& aPostDestroyData) override;
#ifdef DEBUG_FRAME_DUMP
virtual nsresult GetFrameName(nsAString& aResult) const override;
#endif
mozilla::dom::HTMLLegendElement::LegendAlignValue GetLogicalAlign(
mozilla::WritingMode aCBWM);
};
#endif // guard

Просмотреть файл

@ -45,7 +45,6 @@ FRAME_CLASSES = [
Frame("nsImageFrame", "Image", LEAF),
Frame("nsInlineFrame", "Inline", NOT_LEAF),
Frame("nsLeafBoxFrame", "LeafBox", LEAF),
Frame("nsLegendFrame", "Legend", NOT_LEAF),
Frame("nsListControlFrame", "ListControl", NOT_LEAF),
Frame("nsMathMLFrame", "None", NOT_LEAF),
Frame("nsMathMLmactionFrame", "None", NOT_LEAF),

Просмотреть файл

@ -2314,17 +2314,9 @@ void ReflowInput::InitConstraints(
mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap;
}
} else {
// Make sure legend frames with display:block and width:auto still
// shrink-wrap.
// Also shrink-wrap blocks that are orthogonal to their container.
if (isBlockLevel &&
((aFrameType == LayoutFrameType::Legend &&
mFrame->Style()->GetPseudoType() !=
PseudoStyleType::scrolledContent) ||
(aFrameType == LayoutFrameType::Scroll &&
mFrame->GetContentInsertionFrame()->IsLegendFrame()) ||
(mCBReflowInput &&
mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)))) {
// Shrink-wrap blocks that are orthogonal to their container.
if (isBlockLevel && mCBReflowInput &&
mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) {
mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap;
}

Просмотреть файл

@ -138,8 +138,6 @@ nsIFrame* NS_NewFileControlFrame(mozilla::PresShell* aPresShell,
mozilla::ComputedStyle* aStyle);
nsIFrame* NS_NewColorControlFrame(mozilla::PresShell* aPresShell,
mozilla::ComputedStyle* aStyle);
nsIFrame* NS_NewLegendFrame(mozilla::PresShell* aPresShell,
mozilla::ComputedStyle* aStyle);
nsIFrame* NS_NewTextControlFrame(mozilla::PresShell* aPresShell,
mozilla::ComputedStyle* aStyle);
nsContainerFrame* NS_NewListControlFrame(mozilla::PresShell* aPresShell,

Просмотреть файл

@ -38,6 +38,7 @@
#include "mozilla/ViewportUtils.h"
#include "nsCOMPtr.h"
#include "nsFieldSetFrame.h"
#include "nsFlexContainerFrame.h"
#include "nsFrameList.h"
#include "nsPlaceholderFrame.h"
@ -629,6 +630,13 @@ bool nsIFrame::IsPrimaryFrameOfRootOrBodyElement() const {
content == document->GetBodyElement();
}
bool nsIFrame::IsRenderedLegend() const {
if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) {
return static_cast<nsFieldSetFrame*>(parent)->GetLegend() == this;
}
return false;
}
void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
nsIFrame* aPrevInFlow) {
MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId());

Просмотреть файл

@ -2377,6 +2377,11 @@ class nsIFrame : public nsQueryFrame {
bool IsPrimaryFrameOfRootOrBodyElement() const;
/**
* @return true if this frame is used as a fieldset's rendered legend.
*/
bool IsRenderedLegend() const;
/**
* This call is invoked on the primary frame for a character data content
* node, when it is changed in the content tree.

Просмотреть файл

@ -838,7 +838,9 @@ static bool IsNonReplacedInline(nsIFrame* aFrame) {
// FIXME: this should be IsInlineInsideStyle() since width/height
// doesn't apply to ruby boxes.
return aFrame->StyleDisplay()->IsInlineFlow() &&
!aFrame->IsFrameOfType(nsIFrame::eReplaced);
!aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
!aFrame->IsBlockFrame() && !aFrame->IsScrollFrame() &&
!aFrame->IsColumnSetWrapperFrame();
}
static Side SideForPaddingOrMarginOrInsetProperty(nsCSSPropertyID aPropID) {

Просмотреть файл

@ -55,10 +55,6 @@
/* Miscellaneous form elements */
fieldset > legend {
inline-size: -moz-fit-content;
}
legend {
display: block;
padding-inline: 2px;

Просмотреть файл

@ -265,6 +265,12 @@ impl Display {
.unwrap()
}
/// Returns the raw underlying u16 value.
#[inline]
pub const fn to_u16(&self) -> u16 {
self.0
}
/// Whether this is `display: inline` (or `inline list-item`).
#[inline]
pub fn is_inline_flow(&self) -> bool {

Просмотреть файл

@ -4024,6 +4024,16 @@ pub extern "C" fn Servo_ComputedValues_EqualForCachedAnonymousContentStyle(
differing_properties.is_empty()
}
#[no_mangle]
pub extern "C" fn Servo_ComputedValues_BlockifiedDisplay(
style: &ComputedValues,
is_root_element : bool,
) -> u16 {
let display = style.get_box().clone_display();
let blockified_display = display.equivalent_block_display(is_root_element);
blockified_display.to_u16()
}
#[no_mangle]
pub extern "C" fn Servo_StyleSet_Init(doc: &structs::Document) -> *mut RawServoStyleSet {
let data = Box::new(PerDocumentStyleData::new(doc));

Просмотреть файл

@ -1,2 +0,0 @@
[nested-at-outer-boundary-as-legend.html]
expected: FAIL

Просмотреть файл

@ -1,7 +0,0 @@
[legend-block-formatting-context.html]
[in-fieldset-second-child]
expected: FAIL
[in-fieldset-descendant]
expected: FAIL

Просмотреть файл

@ -1,10 +1,12 @@
[legend-display.html]
[rendered legend with display: flow]
expected: FAIL
[rendered legend with display: run-in]
expected: FAIL
[rendered legend with display: inline]
[rendered legend with display: run-in; overflow:hidden]
expected: FAIL
[rendered legend with display: run-in; columns:1]
expected: FAIL
[rendered legend with display: run-in; overflow:hidden;columns:1]
expected: FAIL

Просмотреть файл

@ -1,16 +1,5 @@
[legend-grid-flex-multicol.html]
[flex]
expected: FAIL
[multicol]
expected: FAIL
[inline-grid]
expected: FAIL
[grid]
expected: FAIL
[inline-flex]
expected: FAIL
bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1689619
expected:
if os == "mac": FAIL

Просмотреть файл

@ -1,4 +0,0 @@
[legend.html]
[in-fieldset-second-child: width]
expected: FAIL

Просмотреть файл

@ -2,6 +2,9 @@
<title>rendered legend and CSS display</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<style>
legend { width:initial; }
</style>
<fieldset><legend id="ref">x</legend></fieldset>
<fieldset><legend id="test">x</legend></fieldset>
<script>
@ -10,22 +13,28 @@
const testElm = document.querySelector('#test');
const values = ['block', 'table', 'table-row-group', 'table-header-group', 'table-footer-group', 'table-row', 'table-cell',
'table-column-group', 'table-column', 'table-caption', 'list-item', 'flow', 'flow-root','run-in','inline',
'inline-block', 'inline-table', 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', 'ruby-text-container',
'inline-block', 'inline-table', 'block ruby', 'ruby', 'ruby-base', 'ruby-text', 'ruby-base-container', 'ruby-text-container',
'grid', 'inline-grid', 'flex', 'inline-flex'];
const extraStyle = ['', 'overflow:hidden', 'columns:1', 'overflow:hidden;columns:1'];
for (const val of values) {
test(() => {
testElm.style.removeProperty('display');
testElm.style.display = val;
const computed = getComputedStyle(testElm);
// Note that computed value is different from the used value.
// E.g., if ruby is not supported, the following assertion will
// fail as the computed value of display will be block.
// If ruby is supported, computed.display will return "ruby",
// but the used value is supposed to be "block".
assert_equals(computed.display, val, `display: ${val} is not supported`);
assert_equals(computed.width, refStyle.width, 'width');
assert_equals(testElm.offsetLeft, refElm.offsetLeft, 'offsetLeft');
}, `rendered legend with display: ${val}`);
for (const style of extraStyle) {
for (const val of values) {
test(() => {
testElm.style.removeProperty('display');
testElm.style = style;
testElm.style.display = val;
const computed = getComputedStyle(testElm);
// Note that computed value is different from the used value.
// E.g., if ruby is not supported, the following assertion will
// fail as the computed value of display will be block.
// If ruby is supported, computed.display will return "ruby",
// but the used value is supposed to be "block ruby".
// Also, 'flow' is serialized as 'block' for legacy reasons.
let expected = val == 'flow' ? 'block' : val;
assert_equals(computed.display, expected, `display: ${val} is not supported`);
assert_equals(computed.width, refStyle.width, 'width');
assert_equals(testElm.offsetLeft, refElm.offsetLeft, 'offsetLeft');
}, `rendered legend with display: ${val}` + (style == '' ? '' : "; " + style));
}
}
</script>