fune/layout/forms/nsMeterFrame.cpp
Ting-Yu Lin f101e2077d Bug 1464761 Part 4 - Remove nsReflowStatus::mTruncated bit. r=dholbert
In the description of the mTruncated bit, its purpose is the same as calling
SetInlineLineBreakBeforeAndReset(). We've removed all its usages in previous
patches, so the bit is no longer needed.

Differential Revision: https://phabricator.services.mozilla.com/D151461
2022-07-27 21:55:18 +00:00

223 lines
8.2 KiB
C++

/* -*- 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 "nsMeterFrame.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLMeterElement.h"
#include "nsIContent.h"
#include "nsLayoutUtils.h"
#include "nsPresContext.h"
#include "nsGkAtoms.h"
#include "nsNameSpaceManager.h"
#include "nsNodeInfoManager.h"
#include "nsContentCreatorFunctions.h"
#include "nsFontMetrics.h"
#include "nsCSSPseudoElements.h"
#include "nsStyleConsts.h"
#include <algorithm>
using namespace mozilla;
using mozilla::dom::Document;
using mozilla::dom::Element;
using mozilla::dom::HTMLMeterElement;
nsIFrame* NS_NewMeterFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
return new (aPresShell) nsMeterFrame(aStyle, aPresShell->GetPresContext());
}
NS_IMPL_FRAMEARENA_HELPERS(nsMeterFrame)
nsMeterFrame::nsMeterFrame(ComputedStyle* aStyle, nsPresContext* aPresContext)
: nsContainerFrame(aStyle, aPresContext, kClassID), mBarDiv(nullptr) {}
nsMeterFrame::~nsMeterFrame() = default;
void nsMeterFrame::DestroyFrom(nsIFrame* aDestructRoot,
PostDestroyData& aPostDestroyData) {
NS_ASSERTION(!GetPrevContinuation(),
"nsMeterFrame should not have continuations; if it does we "
"need to call RegUnregAccessKey only for the first.");
aPostDestroyData.AddAnonymousContent(mBarDiv.forget());
nsContainerFrame::DestroyFrom(aDestructRoot, aPostDestroyData);
}
nsresult nsMeterFrame::CreateAnonymousContent(
nsTArray<ContentInfo>& aElements) {
// Get the NodeInfoManager and tag necessary to create the meter bar div.
nsCOMPtr<Document> doc = mContent->GetComposedDoc();
// Create the div.
mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div);
// Associate ::-moz-meter-bar pseudo-element to the anonymous child.
mBarDiv->SetPseudoElementType(PseudoStyleType::mozMeterBar);
aElements.AppendElement(mBarDiv);
return NS_OK;
}
void nsMeterFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
uint32_t aFilter) {
if (mBarDiv) {
aElements.AppendElement(mBarDiv);
}
}
NS_QUERYFRAME_HEAD(nsMeterFrame)
NS_QUERYFRAME_ENTRY(nsMeterFrame)
NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
void nsMeterFrame::Reflow(nsPresContext* aPresContext,
ReflowOutput& aDesiredSize,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) {
MarkInReflow();
DO_GLOBAL_REFLOW_COUNT("nsMeterFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
NS_ASSERTION(mBarDiv, "Meter bar div must exist!");
NS_ASSERTION(!GetPrevContinuation(),
"nsMeterFrame should not have continuations; if it does we "
"need to call RegUnregAccessKey only for the first.");
nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
NS_ASSERTION(barFrame, "The meter frame should have a child with a frame!");
ReflowBarFrame(barFrame, aPresContext, aReflowInput, aStatus);
const auto wm = aReflowInput.GetWritingMode();
aDesiredSize.SetSize(wm, aReflowInput.ComputedSizeWithBorderPadding(wm));
aDesiredSize.SetOverflowAreasToDesiredBounds();
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame);
FinishAndStoreOverflow(&aDesiredSize);
aStatus.Reset(); // This type of frame can't be split.
}
void nsMeterFrame::ReflowBarFrame(nsIFrame* aBarFrame,
nsPresContext* aPresContext,
const ReflowInput& aReflowInput,
nsReflowStatus& aStatus) {
bool vertical = ResolvedOrientationIsVertical();
WritingMode wm = aBarFrame->GetWritingMode();
LogicalSize availSize = aReflowInput.ComputedSize(wm);
availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
ReflowInput reflowInput(aPresContext, aReflowInput, aBarFrame, availSize);
nscoord size =
vertical ? aReflowInput.ComputedHeight() : aReflowInput.ComputedWidth();
nscoord xoffset = aReflowInput.ComputedPhysicalBorderPadding().left;
nscoord yoffset = aReflowInput.ComputedPhysicalBorderPadding().top;
auto* meterElement = static_cast<HTMLMeterElement*>(GetContent());
size = NSToCoordRound(size * meterElement->Position());
if (!vertical && wm.IsPhysicalRTL()) {
xoffset += aReflowInput.ComputedWidth() - size;
}
// The bar position is *always* constrained.
if (vertical) {
// We want the bar to begin at the bottom.
yoffset += aReflowInput.ComputedHeight() - size;
size -= reflowInput.ComputedPhysicalMargin().TopBottom() +
reflowInput.ComputedPhysicalBorderPadding().TopBottom();
size = std::max(size, 0);
reflowInput.SetComputedHeight(size);
} else {
size -= reflowInput.ComputedPhysicalMargin().LeftRight() +
reflowInput.ComputedPhysicalBorderPadding().LeftRight();
size = std::max(size, 0);
reflowInput.SetComputedWidth(size);
}
xoffset += reflowInput.ComputedPhysicalMargin().left;
yoffset += reflowInput.ComputedPhysicalMargin().top;
ReflowOutput barDesiredSize(reflowInput);
ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowInput, xoffset,
yoffset, ReflowChildFlags::Default, aStatus);
FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowInput,
xoffset, yoffset, ReflowChildFlags::Default);
}
nsresult nsMeterFrame::AttributeChanged(int32_t aNameSpaceID,
nsAtom* aAttribute, int32_t aModType) {
NS_ASSERTION(mBarDiv, "Meter bar div must exist!");
if (aNameSpaceID == kNameSpaceID_None &&
(aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max ||
aAttribute == nsGkAtoms::min)) {
nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
NS_ASSERTION(barFrame, "The meter frame should have a child with a frame!");
PresShell()->FrameNeedsReflow(barFrame, IntrinsicDirty::Resize,
NS_FRAME_IS_DIRTY);
InvalidateFrame();
}
return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
}
LogicalSize nsMeterFrame::ComputeAutoSize(
gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
nscoord aAvailableISize, const LogicalSize& aMargin,
const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
ComputeSizeFlags aFlags) {
RefPtr<nsFontMetrics> fontMet =
nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
const WritingMode wm = GetWritingMode();
LogicalSize autoSize(wm);
autoSize.BSize(wm) = autoSize.ISize(wm) =
fontMet->Font().size.ToAppUnits(); // 1em
if (ResolvedOrientationIsVertical() == wm.IsVertical()) {
autoSize.ISize(wm) *= 5; // 5em
} else {
autoSize.BSize(wm) *= 5; // 5em
}
return autoSize.ConvertTo(aWM, wm);
}
nscoord nsMeterFrame::GetMinISize(gfxContext* aRenderingContext) {
RefPtr<nsFontMetrics> fontMet =
nsLayoutUtils::GetFontMetricsForFrame(this, 1.0f);
nscoord minISize = fontMet->Font().size.ToAppUnits(); // 1em
if (ResolvedOrientationIsVertical() == GetWritingMode().IsVertical()) {
// The orientation is inline
minISize *= 5; // 5em
}
return minISize;
}
nscoord nsMeterFrame::GetPrefISize(gfxContext* aRenderingContext) {
return GetMinISize(aRenderingContext);
}
bool nsMeterFrame::ShouldUseNativeStyle() const {
nsIFrame* barFrame = mBarDiv->GetPrimaryFrame();
// Use the native style if these conditions are satisfied:
// - both frames use the native appearance;
// - neither frame has author specified rules setting the border or the
// background.
return StyleDisplay()->EffectiveAppearance() == StyleAppearance::Meter &&
!Style()->HasAuthorSpecifiedBorderOrBackground() && barFrame &&
barFrame->StyleDisplay()->EffectiveAppearance() ==
StyleAppearance::Meterchunk &&
!barFrame->Style()->HasAuthorSpecifiedBorderOrBackground();
}