`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
64 lines
2.9 KiB
C++
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
|