Files
tubestation/layout/base/CaretAssociationHint.cpp
Masayuki Nakano 9fd27e7690 Bug 1937289 - Make AutoRangeArray::ExtendAnchorFocusRangeFor stop using nsFrameSelection r=m_kato,jjaschke,dom-core
`AutoRangeArray` is created for making some handlers of the editor classes free
from `Selection` and `nsFrameSelection` while handling the edit actions.
However, the method still depends on `nsFrameSelection` instance since its
callees are instance methods of `nsFrameSelection`.

However, `EditContext` requires completely free methods to compute target
ranges of `beforeinput`.  Therefore, we need to make it not depend on
`Selection` nor `nsFrameSelection`.

The common method, `nsFrameSelection::CreateRangeExtendedToSomewhere`, requires
`PresShell`, selection limiter which is set only when the selection is an
independent selection like in a selection for a text control, selection
ancestor limiter which is set only when an editing host has focus, caret
association hint to put caret to end of preceding line or start of following
line if selection range is collapsed at a line break and caret bidi level for
considering caret position around line break in bidi text.  They are now stored
by `nsFrameSelection` and modified when selection range is changed in some
cases.  Basically, the method is called without updating its ranges and if and
only if it's initialized with `Selection`.  So, simply caching the
`nsFrameSelection`'s values solves the issues in the most cases, but this
patch makes `AutoRangeArray` adjust the value only when its `Collapse` is
called because `Selection` automatically updates it and we can compute the
value without `Selection` nor `nsFrameSelection`.

After applying this patch, `AutoRangeArray` has two meanings, one is the
instance is a proxy for `Selection`.  The other is a container for a range to
call methods which take pointer or reference to it.  To make this differences
checked at build time, this patch creates a new subclass of it, and renamed to
`AutoClonedRangeArray` to make it clearer that what are stored in the array.

Oddly, `AutoRangeArray(nsRange&)` constructor has not been cloned.  Therefore,
I make it and its subclass version clone before storing into the array.  Then,
one caller needs to change which range should be tracked.

Differential Revision: https://phabricator.services.mozilla.com/D232174
2025-01-07 01:56:52 +00:00

64 lines
2.9 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 "CaretAssociationHint.h"
#include "mozilla/RangeBoundary.h" // for RangeBoundaryBase
#include "mozilla/SelectionMovementUtils.h" // for CaretFrameData
#include "mozilla/intl/BidiEmbeddingLevel.h" // for BidiEmbeddingLevel
#include "nsCaret.h" // for nsCaret
#include "nsIContent.h" // for nsIContent
#include "nsIFrame.h" // for nsIFrame
#include "nsTextFrame.h" // for nsTextFrame
namespace mozilla {
template CaretAssociationHint ComputeCaretAssociationHint(
CaretAssociationHint aDefault, intl::BidiEmbeddingLevel aBidiLevel,
const RangeBoundary& aCaretPoint);
template CaretAssociationHint ComputeCaretAssociationHint(
CaretAssociationHint aDefault, intl::BidiEmbeddingLevel aBidiLevel,
const RawRangeBoundary& aCaretPoint);
template <typename PT, typename CT>
CaretAssociationHint ComputeCaretAssociationHint(
CaretAssociationHint aDefault, intl::BidiEmbeddingLevel aBidiLevel,
const RangeBoundaryBase<PT, CT>& aCaretPoint) {
MOZ_ASSERT(aCaretPoint.IsSetAndValid());
if (aDefault != CaretAssociationHint::Before ||
!aCaretPoint.Container()->IsContent()) {
return aDefault;
}
const nsCaret::CaretPosition pos{
aCaretPoint.Container(),
static_cast<int32_t>(*aCaretPoint.Offset(
RangeBoundaryBase<PT, CT>::OffsetFilter::kValidOffsets)),
aDefault, aBidiLevel};
CaretFrameData frameData = nsCaret::GetFrameAndOffset(pos);
nsTextFrame* f = do_QueryFrame(frameData.mFrame);
if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) {
// RangeBoundaryBase<PT, CT>::Offset() causes computing offset if it's not
// been done yet. However, it's called only when the container is a text
// node. In such case, offset has always been set since it cannot have
// any children. So, this doesn't cause computing offset with expensive
// method, nsINode::ComputeIndexOf().
const bool caretPointIsAtEndOfFrame =
aCaretPoint.Container() == f->GetContent() &&
f->GetContentEnd() ==
static_cast<int32_t>(*aCaretPoint.Offset(
RangeBoundaryBase<PT, CT>::OffsetFilter::kValidOffsets));
const bool caretPointIsImmediatelyAfterFrameContent =
aCaretPoint.Container() == f->GetContent()->GetParentNode() &&
f->GetContent() == aCaretPoint.GetPreviousSiblingOfChildAtOffset();
if (caretPointIsAtEndOfFrame || caretPointIsImmediatelyAfterFrameContent) {
return CaretAssociationHint::After;
}
}
return frameData.mFrame ? frameData.mHint : aDefault;
}
} // namespace mozilla