diff --git a/dom/html/HTMLLegendElement.cpp b/dom/html/HTMLLegendElement.cpp index 253c9bdff5b3..93b5200f96e7 100644 --- a/dom/html/HTMLLegendElement.cpp +++ b/dom/html/HTMLLegendElement.cpp @@ -103,26 +103,6 @@ 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(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 HTMLLegendElement::GetForm() { return do_AddRef(GetFormElement()); } diff --git a/dom/html/HTMLLegendElement.h b/dom/html/HTMLLegendElement.h index b239a7183475..2a8230c112e3 100644 --- a/dom/html/HTMLLegendElement.h +++ b/dom/html/HTMLLegendElement.h @@ -57,16 +57,6 @@ 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 */ diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 9b77941596ff..6e394bb22972 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -702,10 +702,6 @@ 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> mGeneratedContentWithInitializer; // Constructor @@ -855,8 +851,7 @@ nsFrameConstructorState::nsFrameConstructorState( // frames. mFixedPosIsAbsPos(aFixedContainingBlock == aAbsoluteContainingBlock), mHavePendingPopupgroup(false), - mCreatingExtraFrames(false), - mHasRenderedLegend(false) { + mCreatingExtraFrames(false) { #ifdef MOZ_XUL nsIPopupContainer* popupContainer = nsIPopupContainer::GetPopupContainer(aPresShell); @@ -3053,16 +3048,35 @@ nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame( absoluteSaveState); } - { - AutoRestore savedHasRenderedLegend(aState.mHasRenderedLegend); - aState.mHasRenderedLegend = false; - ProcessChildren(aState, content, computedStyle, contentFrame, true, - childList, true); - } + 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); @@ -3088,10 +3102,10 @@ nsIFrame* nsCSSFrameConstructor::ConstructFieldSetFrame( FinishBuildingScrollFrame(scrollFrame, contentFrameTop); } - // 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); + // Set the outer frame's initial child list + fieldsetFrame->SetInitialChildList(kPrincipalList, fieldsetKids); + // Our new frame returned is the outer frame, which is the fieldset frame. return fieldsetFrame; } @@ -3315,11 +3329,25 @@ 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())) { + // is only special inside fieldset, we only check the frame tree + // parent because the content tree parent may not be a
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, @@ -3335,6 +3363,9 @@ 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, @@ -5217,17 +5248,6 @@ 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); } @@ -5339,14 +5359,6 @@ 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) { @@ -5399,9 +5411,6 @@ 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; @@ -5960,6 +5969,20 @@ 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; } @@ -6047,10 +6070,6 @@ nsIFrame* nsCSSFrameConstructor::AdjustSiblingFrame( return nullptr; } - if (aSibling->IsRenderedLegend()) { - return nullptr; - } - if (aSibling->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { aSibling = aSibling->GetPlaceholderFrame(); MOZ_ASSERT(aSibling); @@ -7965,6 +7984,9 @@ 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); @@ -8223,9 +8245,12 @@ bool nsCSSFrameConstructor::MaybeRecreateContainerForFrameRemoval( return true; } - if (inFlowFrame->IsRenderedLegend()) { + nsContainerFrame* insertionFrame = aFrame->GetContentInsertionFrame(); + if (insertionFrame && insertionFrame->IsLegendFrame() && + aFrame->GetParent()->IsFieldSetFrame()) { TRACE("Fieldset / Legend"); - RecreateFramesForContent(parent->GetContent(), InsertionKind::Async); + RecreateFramesForContent(aFrame->GetParent()->GetContent(), + InsertionKind::Async); return true; } @@ -9330,34 +9355,6 @@ 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 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); @@ -9365,9 +9362,6 @@ 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" diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index a316a1fdef9b..b0c0c0755a38 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -375,8 +375,6 @@ 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
. - IsForRenderedLegend, }; using ItemFlags = mozilla::EnumSet; @@ -1088,8 +1086,7 @@ class nsCSSFrameConstructor final : public nsFrameManager { mIsAllInline(false), mIsBlock(false), mIsPopup(false), - mIsLineParticipant(false), - mIsRenderedLegend(false) { + mIsLineParticipant(false) { MOZ_COUNT_CTOR(FrameConstructionItem); } @@ -1163,8 +1160,6 @@ 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
- bool mIsRenderedLegend : 1; private: // Not allocated from the general heap - instead, use the new/Delete APIs diff --git a/layout/forms/moz.build b/layout/forms/moz.build index 6c3f829d2cd5..86d512257dc6 100644 --- a/layout/forms/moz.build +++ b/layout/forms/moz.build @@ -27,6 +27,7 @@ UNIFIED_SOURCES += [ "nsGfxButtonControlFrame.cpp", "nsHTMLButtonControlFrame.cpp", "nsImageControlFrame.cpp", + "nsLegendFrame.cpp", "nsListControlFrame.cpp", "nsMeterFrame.cpp", "nsNumberControlFrame.cpp", diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp index 3c0ce5ad738c..ba28f6f5a324 100644 --- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -14,7 +14,6 @@ #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" @@ -22,6 +21,7 @@ #include "nsGkAtoms.h" #include "nsIFrameInlines.h" #include "nsLayoutUtils.h" +#include "nsLegendFrame.h" #include "nsStyleConsts.h" using namespace mozilla; @@ -445,15 +445,8 @@ 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, Nothing(), initFlags, - sizeOverrides, sizeFlags); + legendAvailSize); } const bool avoidBreakInside = ShouldAvoidBreakInside(aReflowInput); if (reflowLegend) { @@ -682,10 +675,11 @@ 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/LogicalAlign - auto* legendElement = - dom::HTMLLegendElement::FromNode(legend->GetContent()); - switch (legendElement->LogicalAlign(wm)) { + // @see HTMLLegendElement::ParseAttribute, nsLegendFrame::GetLogicalAlign + LegendAlignValue align = + static_cast(legend->GetContentInsertionFrame()) + ->GetLogicalAlign(wm); + switch (align) { case LegendAlignValue::InlineEnd: mLegendRect.IStart(wm) = innerContentRect.IEnd(wm) - mLegendRect.ISize(wm); @@ -795,29 +789,18 @@ 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); - 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"); + MOZ_ASSERT(aListID != kPrincipalList || GetInner(), + "Setting principal child list should populate our inner frame"); } void nsFieldSetFrame::AppendFrames(ChildListID aListID, nsFrameList& aFrameList) { - 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"); + MOZ_CRASH("nsFieldSetFrame::AppendFrames not supported"); } -#ifdef DEBUG void nsFieldSetFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, const nsLineList::iterator* aPrevFrameLine, nsFrameList& aFrameList) { diff --git a/layout/forms/nsFieldSetFrame.h b/layout/forms/nsFieldSetFrame.h index fb823d1e913e..0915fce00ac3 100644 --- a/layout/forms/nsFieldSetFrame.h +++ b/layout/forms/nsFieldSetFrame.h @@ -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,8 +92,9 @@ class nsFieldSetFrame final : public nsContainerFrame { nsContainerFrame* GetInner() const; /** - * Return the frame that represents the rendered legend if any. - * https://html.spec.whatwg.org/multipage/rendering.html#rendered-legend + * 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). */ nsIFrame* GetLegend() const; diff --git a/layout/forms/nsLegendFrame.cpp b/layout/forms/nsLegendFrame.cpp new file mode 100644 index 000000000000..e42ed047382d --- /dev/null +++ b/layout/forms/nsLegendFrame.cpp @@ -0,0 +1,93 @@ +/* -*- 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(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(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(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 diff --git a/layout/forms/nsLegendFrame.h b/layout/forms/nsLegendFrame.h new file mode 100644 index 000000000000..3480a33c71e3 --- /dev/null +++ b/layout/forms/nsLegendFrame.h @@ -0,0 +1,37 @@ +/* -*- 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 diff --git a/layout/generic/FrameClasses.py b/layout/generic/FrameClasses.py index 6fe3904252db..40d22650bd68 100644 --- a/layout/generic/FrameClasses.py +++ b/layout/generic/FrameClasses.py @@ -45,6 +45,7 @@ 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), diff --git a/layout/generic/ReflowInput.cpp b/layout/generic/ReflowInput.cpp index d1b9c81b1ceb..c7f7827522d5 100644 --- a/layout/generic/ReflowInput.cpp +++ b/layout/generic/ReflowInput.cpp @@ -2314,9 +2314,17 @@ void ReflowInput::InitConstraints( mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; } } else { - // Shrink-wrap blocks that are orthogonal to their container. - if (isBlockLevel && mCBReflowInput && - mCBReflowInput->GetWritingMode().IsOrthogonalTo(mWritingMode)) { + // 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)))) { mComputeSizeFlags += ComputeSizeFlag::ShrinkWrap; } diff --git a/layout/generic/nsHTMLParts.h b/layout/generic/nsHTMLParts.h index 881b52a361df..2d53a8e077ca 100644 --- a/layout/generic/nsHTMLParts.h +++ b/layout/generic/nsHTMLParts.h @@ -138,6 +138,8 @@ 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, diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp index 0b9ba661997b..9fe39098ca97 100644 --- a/layout/generic/nsIFrame.cpp +++ b/layout/generic/nsIFrame.cpp @@ -38,7 +38,6 @@ #include "mozilla/ViewportUtils.h" #include "nsCOMPtr.h" -#include "nsFieldSetFrame.h" #include "nsFlexContainerFrame.h" #include "nsFrameList.h" #include "nsPlaceholderFrame.h" @@ -630,13 +629,6 @@ bool nsIFrame::IsPrimaryFrameOfRootOrBodyElement() const { content == document->GetBodyElement(); } -bool nsIFrame::IsRenderedLegend() const { - if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) { - return static_cast(parent)->GetLegend() == this; - } - return false; -} - void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent, nsIFrame* aPrevInFlow) { MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId()); diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index d024c7dec1bd..c55671f5875b 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -2377,11 +2377,6 @@ 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. diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index d96d6756a8e0..d9c2cd71b3e3 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -838,9 +838,7 @@ 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->IsBlockFrame() && !aFrame->IsScrollFrame() && - !aFrame->IsColumnSetWrapperFrame(); + !aFrame->IsFrameOfType(nsIFrame::eReplaced); } static Side SideForPaddingOrMarginOrInsetProperty(nsCSSPropertyID aPropID) { diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index daca7a7896d3..449c52edd35c 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -55,6 +55,10 @@ /* Miscellaneous form elements */ +fieldset > legend { + inline-size: -moz-fit-content; +} + legend { display: block; padding-inline: 2px; diff --git a/servo/components/style/values/specified/box.rs b/servo/components/style/values/specified/box.rs index 43f88bf4f412..4663143677c2 100644 --- a/servo/components/style/values/specified/box.rs +++ b/servo/components/style/values/specified/box.rs @@ -265,12 +265,6 @@ 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 { diff --git a/servo/ports/geckolib/glue.rs b/servo/ports/geckolib/glue.rs index 5c365fa8f7b9..d041b3099f27 100644 --- a/servo/ports/geckolib/glue.rs +++ b/servo/ports/geckolib/glue.rs @@ -4024,16 +4024,6 @@ 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)); diff --git a/testing/web-platform/meta/css/css-multicol/nested-at-outer-boundary-as-legend.html.ini b/testing/web-platform/meta/css/css-multicol/nested-at-outer-boundary-as-legend.html.ini new file mode 100644 index 000000000000..fd49a9fa26bc --- /dev/null +++ b/testing/web-platform/meta/css/css-multicol/nested-at-outer-boundary-as-legend.html.ini @@ -0,0 +1,2 @@ +[nested-at-outer-boundary-as-legend.html] + expected: FAIL diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-formatting-context.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-formatting-context.html.ini new file mode 100644 index 000000000000..17abce7a1835 --- /dev/null +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-block-formatting-context.html.ini @@ -0,0 +1,7 @@ +[legend-block-formatting-context.html] + [in-fieldset-second-child] + expected: FAIL + + [in-fieldset-descendant] + expected: FAIL + diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html.ini index 928d9ea5fb19..ae64aeef3487 100644 --- a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html.ini +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html.ini @@ -1,12 +1,10 @@ [legend-display.html] + [rendered legend with display: flow] + expected: FAIL + [rendered legend with display: run-in] expected: FAIL - [rendered legend with display: run-in; overflow:hidden] + [rendered legend with display: inline] expected: FAIL - [rendered legend with display: run-in; columns:1] - expected: FAIL - - [rendered legend with display: run-in; overflow:hidden;columns:1] - expected: FAIL diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html.ini index db1ce39b4863..695e44c944aa 100644 --- a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html.ini +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-grid-flex-multicol.html.ini @@ -1,5 +1,16 @@ [legend-grid-flex-multicol.html] - [offsetHeight] - expected: - if os == "mac": FAIL - bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1689619 + [flex] + expected: FAIL + + [multicol] + expected: FAIL + + [inline-grid] + expected: FAIL + + [grid] + expected: FAIL + + [inline-flex] + expected: FAIL + diff --git a/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend.html.ini b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend.html.ini new file mode 100644 index 000000000000..3f7d1c15fb21 --- /dev/null +++ b/testing/web-platform/meta/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend.html.ini @@ -0,0 +1,4 @@ +[legend.html] + [in-fieldset-second-child: width] + expected: FAIL + diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html index b6c57a67baf0..914547fc6cdd 100644 --- a/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html +++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/legend-display.html @@ -2,9 +2,6 @@ rendered legend and CSS display -
x
x