зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
92b6ff7e1b
Коммит
f6ef0ea044
|
@ -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>
|
||||
|
|
Загрузка…
Ссылка в новой задаче