Bug 1679645 - Cache the selection state in nsTextFrame rather than in nsDisplayText r=emilio,jfkthame

Differential Revision: https://phabricator.services.mozilla.com/D108646
This commit is contained in:
Miko Mynttinen 2021-03-25 21:28:52 +00:00
Родитель c210b10b1c
Коммит f30a7d057b
4 изменённых файлов: 48 добавлений и 33 удалений

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

@ -4655,6 +4655,8 @@ nsTextFrame* nsTextFrame::LastContinuation() const {
void nsTextFrame::InvalidateFrame(uint32_t aDisplayItemKey,
bool aRebuildDisplayItems) {
InvalidateSelectionState();
if (SVGUtils::IsInSVGTextSubtree(this)) {
nsIFrame* svgTextFrame = nsLayoutUtils::GetClosestFrameOfType(
GetParent(), LayoutFrameType::SVGText);
@ -4667,6 +4669,8 @@ void nsTextFrame::InvalidateFrame(uint32_t aDisplayItemKey,
void nsTextFrame::InvalidateFrameWithRect(const nsRect& aRect,
uint32_t aDisplayItemKey,
bool aRebuildDisplayItems) {
InvalidateSelectionState();
if (SVGUtils::IsInSVGTextSubtree(this)) {
nsIFrame* svgTextFrame = nsLayoutUtils::GetClosestFrameOfType(
GetParent(), LayoutFrameType::SVGText);
@ -4899,12 +4903,10 @@ void nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
bool isTextTransparent =
NS_GET_A(st->mWebkitTextFillColor.CalcColor(this)) == 0 &&
NS_GET_A(st->mWebkitTextStrokeColor.CalcColor(this)) == 0;
Maybe<bool> isSelected;
if ((HasAnyStateBits(TEXT_NO_RENDERED_GLYPHS) ||
(isTextTransparent && !StyleText()->HasTextShadow())) &&
aBuilder->IsForPainting() && !SVGUtils::IsInSVGTextSubtree(this)) {
isSelected.emplace(IsSelected());
if (!isSelected.value()) {
if (!IsSelected()) {
TextDecorations textDecs;
GetTextDecorations(PresContext(), eResolvedColors, textDecs);
if (!textDecs.HasDecorationLines()) {
@ -4916,7 +4918,7 @@ void nsTextFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
}
}
aLists.Content()->AppendNewToTop<nsDisplayText>(aBuilder, this, isSelected);
aLists.Content()->AppendNewToTop<nsDisplayText>(aBuilder, this);
}
UniquePtr<SelectionDetails> nsTextFrame::GetSelectionDetails() {
@ -7430,7 +7432,22 @@ bool nsTextFrame::CombineSelectionUnderlineRect(nsPresContext* aPresContext,
bool nsTextFrame::IsFrameSelected() const {
NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),
"use the public IsSelected() instead");
return GetContent()->IsSelected(GetContentOffset(), GetContentEnd());
if (mIsSelected == nsTextFrame::SelectionState::Unknown) {
const bool isSelected =
GetContent()->IsSelected(GetContentOffset(), GetContentEnd());
mIsSelected = isSelected ? nsTextFrame::SelectionState::Selected
: nsTextFrame::SelectionState::NotSelected;
} else {
#ifdef DEBUG
// Assert that the selection caching works.
const bool isReallySelected =
GetContent()->IsSelected(GetContentOffset(), GetContentEnd());
MOZ_ASSERT((mIsSelected == nsTextFrame::SelectionState::Selected) ==
isReallySelected);
#endif
}
return mIsSelected == nsTextFrame::SelectionState::Selected;
}
void nsTextFrame::SelectionStateChanged(uint32_t aStart, uint32_t aEnd,
@ -7440,6 +7457,8 @@ void nsTextFrame::SelectionStateChanged(uint32_t aStart, uint32_t aEnd,
"Should only be called for primary frame");
DEBUG_VERIFY_NOT_DIRTY(mState);
InvalidateSelectionState();
// Selection is collapsed, which can't affect text frame rendering
if (aStart == aEnd) return;
@ -9005,6 +9024,8 @@ void nsTextFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aMetrics,
DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
InvalidateSelectionState();
// XXX If there's no line layout, we shouldn't even have created this
// frame. This may happen if, for example, this is text inside a table
// but not inside a cell. For now, just don't reflow.
@ -9082,6 +9103,8 @@ void nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
// content; gfxTextRun construction logic will ensure that we take priority.
int32_t maxContentLength = GetInFlowContentLength();
InvalidateSelectionState();
// We don't need to reflow if there is no content.
if (!maxContentLength) {
ClearMetrics(aMetrics);

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

@ -202,7 +202,8 @@ class nsTextFrame : public nsIFrame {
mNextContinuation(nullptr),
mContentOffset(0),
mContentLengthHint(0),
mAscent(0) {}
mAscent(0),
mIsSelected(SelectionState::Unknown) {}
NS_DECL_FRAMEARENA_HELPERS(nsTextFrame)
@ -801,12 +802,22 @@ class nsTextFrame : public nsIFrame {
int32_t mContentLengthHint;
nscoord mAscent;
// Cached selection state.
enum class SelectionState : uint8_t {
Unknown,
Selected,
NotSelected,
};
mutable SelectionState mIsSelected;
/**
* Return true if the frame is part of a Selection.
* Helper method to implement the public IsSelected() API.
*/
bool IsFrameSelected() const final;
void InvalidateSelectionState() { mIsSelected = SelectionState::Unknown; }
mozilla::UniquePtr<SelectionDetails> GetSelectionDetails();
void UnionAdditionalOverflow(nsPresContext* aPresContext, nsIFrame* aBlock,

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

@ -8750,25 +8750,24 @@ bool nsDisplayPerspective::ComputeVisibility(nsDisplayListBuilder* aBuilder,
}
nsDisplayText::nsDisplayText(nsDisplayListBuilder* aBuilder,
nsTextFrame* aFrame,
const Maybe<bool>& aIsSelected)
nsTextFrame* aFrame)
: nsPaintedDisplayItem(aBuilder, aFrame),
mOpacity(1.0f),
mVisIStartEdge(0),
mVisIEndEdge(0) {
MOZ_COUNT_CTOR(nsDisplayText);
mIsFrameSelected = aIsSelected;
mBounds = mFrame->InkOverflowRectRelativeToSelf() + ToReferenceFrame();
// Bug 748228
mBounds.Inflate(mFrame->PresContext()->AppUnitsPerDevPixel());
}
bool nsDisplayText::CanApplyOpacity() const {
if (IsSelected()) {
auto* f = static_cast<nsTextFrame*>(mFrame);
if (f->IsSelected()) {
return false;
}
nsTextFrame* f = static_cast<nsTextFrame*>(mFrame);
const nsStyleText* textStyle = f->StyleText();
if (textStyle->HasTextShadow()) {
return false;
@ -8839,7 +8838,7 @@ bool nsDisplayText::CreateWebRenderCommands(
// view. Also if we're selected we might have some shadows from the
// ::selected and ::inctive-selected pseudo-selectors. So don't do this
// optimization if we have shadows or a selection.
if (!(IsSelected() || f->StyleText()->HasTextShadow())) {
if (!(f->IsSelected() || f->StyleText()->HasTextShadow())) {
nsRect visible = GetPaintRect();
visible.Inflate(3 * appUnitsPerDevPixel);
bounds = bounds.Intersect(visible);
@ -8925,23 +8924,13 @@ void nsDisplayText::RenderToContext(gfxContext* aCtx,
}
f->PaintText(params, mVisIStartEdge, mVisIEndEdge, ToReferenceFrame(),
IsSelected(), mOpacity);
f->IsSelected(), mOpacity);
if (willClip) {
aCtx->PopClip();
}
}
bool nsDisplayText::IsSelected() const {
if (mIsFrameSelected.isNothing()) {
MOZ_ASSERT((nsTextFrame*)do_QueryFrame(mFrame));
auto* f = static_cast<nsTextFrame*>(mFrame);
mIsFrameSelected.emplace(f->IsSelected());
}
return mIsFrameSelected.value();
}
class nsDisplayTextGeometry : public nsDisplayItemGenericGeometry {
public:
nsDisplayTextGeometry(nsDisplayText* aItem, nsDisplayListBuilder* aBuilder)

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

@ -7119,19 +7119,16 @@ class nsDisplayPerspective : public nsPaintedDisplayItem {
*/
class nsDisplayText final : public nsPaintedDisplayItem {
public:
nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame,
const mozilla::Maybe<bool>& aIsSelected);
nsDisplayText(nsDisplayListBuilder* aBuilder, nsTextFrame* aFrame);
MOZ_COUNTED_DTOR_OVERRIDE(nsDisplayText)
NS_DISPLAY_DECL_NAME("Text", TYPE_TEXT)
bool RestoreState() final {
if (!nsPaintedDisplayItem::RestoreState() && mIsFrameSelected.isNothing() &&
mOpacity == 1.0f) {
if (!nsPaintedDisplayItem::RestoreState() && mOpacity == 1.0f) {
return false;
}
mIsFrameSelected.reset();
mOpacity = 1.0f;
return true;
}
@ -7195,8 +7192,6 @@ class nsDisplayText final : public nsPaintedDisplayItem {
: nullptr;
}
bool IsSelected() const;
struct ClipEdges {
ClipEdges(const nsIFrame* aFrame, const nsPoint& aToReferenceFrame,
nscoord aVisIStartEdge, nscoord aVisIEndEdge) {
@ -7237,9 +7232,6 @@ class nsDisplayText final : public nsPaintedDisplayItem {
// regardless of bidi directionality; top and bottom in vertical modes).
nscoord mVisIStartEdge;
nscoord mVisIEndEdge;
// Cached result of mFrame->IsSelected(). Only initialized when needed.
mutable mozilla::Maybe<bool> mIsFrameSelected;
};
/**